@ng-forge/dynamic-forms 0.10.0-next.4 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/{ng-forge-dynamic-forms-array-field.component-BfRH3TBm.mjs → ng-forge-dynamic-forms-array-field.component-By12r5Cj.mjs} +47 -26
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-By12r5Cj.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-QJzwEYSj.mjs +2347 -0
- package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-QJzwEYSj.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-container-field.component-bA-8ICHF.mjs → ng-forge-dynamic-forms-container-field.component-BGFguOa9.mjs} +22 -14
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-BGFguOa9.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-container-utils-BvXn8T_i.mjs → ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs} +3 -2
- package/fesm2022/ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-DjhcONFh.mjs → ng-forge-dynamic-forms-css-wrapper.component-AScQAJTQ.mjs} +8 -6
- package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-AScQAJTQ.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-group-field.component-D0_nmXf2.mjs → ng-forge-dynamic-forms-group-field.component-Bz4GD8Hy.mjs} +42 -24
- package/fesm2022/ng-forge-dynamic-forms-group-field.component-Bz4GD8Hy.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs +116 -75
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
- package/fesm2022/ng-forge-dynamic-forms-internal.mjs +16 -14
- package/fesm2022/ng-forge-dynamic-forms-internal.mjs.map +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-CNc4a_Xv.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-DTkMZcnb.mjs} +492 -2691
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-DTkMZcnb.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs → ng-forge-dynamic-forms-page-field.component-CjCd5Byo.mjs} +26 -16
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-CjCd5Byo.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-row-wrapper.component-CwkYn5yl.mjs → ng-forge-dynamic-forms-row-wrapper.component-BlVa801t.mjs} +6 -5
- package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-BlVa801t.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-DABkT0O7.mjs → ng-forge-dynamic-forms-text-field.component-CNCpmf5_.mjs} +18 -11
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-DABkT0O7.mjs.map → ng-forge-dynamic-forms-text-field.component-CNCpmf5_.mjs.map} +1 -1
- package/fesm2022/ng-forge-dynamic-forms.mjs +1 -1
- package/package.json +4 -4
- package/types/ng-forge-dynamic-forms-internal.d.ts +2 -2
- package/types/ng-forge-dynamic-forms.d.ts +2 -1
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-BfRH3TBm.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-bA-8ICHF.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-container-utils-BvXn8T_i.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-DjhcONFh.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-group-field.component-D0_nmXf2.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CNc4a_Xv.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CwkYn5yl.mjs.map +0 -1
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { reflectComponentType, Injector, inject, EnvironmentInjector, DestroyRef, computed, signal, input, ViewContainerRef, Directive, linkedSignal, ChangeDetectionStrategy, Component,
|
|
2
|
+
import { reflectComponentType, Injector, inject, EnvironmentInjector, DestroyRef, computed, signal, input, ViewContainerRef, Directive, linkedSignal, ChangeDetectionStrategy, Component, Injectable, InjectionToken, isSignal, runInInjectionContext, afterNextRender, untracked, PendingTasks, ElementRef, PLATFORM_ID, isDevMode, TemplateRef, model, contentChildren, makeEnvironmentProviders } from '@angular/core';
|
|
3
3
|
import { explicitEffect } from 'ngxtension/explicit-effect';
|
|
4
|
-
import { DynamicFormLogger, WRAPPER_COMPONENT_CACHE, WRAPPER_REGISTRY, toReadonlyFieldTreeCached, writeToFieldValue, WRAPPER_AUTO_ASSOCIATIONS, DEFAULT_WRAPPERS, READONLY_FIELD_TREE_CACHE, EventBus, FieldContextRegistryService, FunctionRegistryService, FORM_OPTIONS, evaluateCondition, FIELD_SIGNAL_CONTEXT, isRowField, isContainerField, isArrayField, hasChildFields, isSimplifiedArrayField, DynamicFormError, isStateLogicConfig, isDerivationLogicConfig, createDebouncedLogicFunction, createLogicFunction, ExpressionParser, applyValidator, isPageField, isContainerTypedField, isGroupField, getFieldValueHandling, normalizeFieldsArray, getNestedValue, isCrossFieldValidator, extractStringDependencies, isCrossFieldBuiltInValidator, hasCrossFieldWhenCondition, extractExpressionDependencies, isCrossFieldStateLogic, isCrossFieldSchema, keyBy, mapValues, isEqual, hasTargetProperty, ARRAY_CONTEXT, GROUP_CONTEXT, FORM_ID_PREFIX, baseFieldMapper, detectFormMode, validatePageNesting, FIELD_REGISTRY, assertNever, ADDON_KIND_REGISTRY, deepMergeDefaults, derivedFromDeferred, createWarningTracker,
|
|
4
|
+
import { DynamicFormLogger, WRAPPER_COMPONENT_CACHE, WRAPPER_REGISTRY, toReadonlyFieldTreeCached, writeToFieldValue, WRAPPER_AUTO_ASSOCIATIONS, DEFAULT_WRAPPERS, READONLY_FIELD_TREE_CACHE, EventBus, FieldContextRegistryService, FunctionRegistryService, FORM_OPTIONS, evaluateCondition, FIELD_SIGNAL_CONTEXT, isRowField, isContainerField, isArrayField, hasChildFields, isSimplifiedArrayField, DynamicFormError, isStateLogicConfig, isDerivationLogicConfig, createDebouncedLogicFunction, createLogicFunction, ExpressionParser, applyValidator, isPageField, isContainerTypedField, isGroupField, getFieldValueHandling, normalizeFieldsArray, getNestedValue, isCrossFieldValidator, extractStringDependencies, isCrossFieldBuiltInValidator, hasCrossFieldWhenCondition, extractExpressionDependencies, isCrossFieldStateLogic, isCrossFieldSchema, keyBy, mapValues, isEqual, hasTargetProperty, ARRAY_CONTEXT, GROUP_CONTEXT, FORM_ID_PREFIX, baseFieldMapper, detectFormMode, validatePageNesting, FIELD_REGISTRY, assertNever, ADDON_KIND_REGISTRY, deepMergeDefaults, derivedFromDeferred, createWarningTracker, RootFormRegistryService, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, EXTERNAL_DATA, DEPRECATION_WARNING_TRACKER, HttpConditionCache, HTTP_CONDITION_CACHE, HttpConditionFunctionCacheService, AsyncConditionFunctionCacheService, DynamicValueFunctionCacheService, LogicFunctionCacheService, setupInitializationTracking, ComponentInitializedEvent, DF_FIELD_TEMPLATES, rowFieldMapper, groupFieldMapper, arrayFieldMapper, pageFieldMapper, textFieldMapper, containerFieldMapper, isWrapperTypeDefinition, createDefaultDerivationLogConfig, NoopLogger, EMIT_FORM_VALUE_ON_EVENTS, resolveDynamicValue, DynamicTextPipe } from '@ng-forge/dynamic-forms/internal';
|
|
5
5
|
import { takeUntilDestroyed, toObservable, toSignal, outputFromObservable } from '@angular/core/rxjs-interop';
|
|
6
|
-
import { of, forkJoin, from, catchError, map, switchMap, isObservable, firstValueFrom, exhaustMap, EMPTY, debounceTime, distinctUntilChanged, tap, pipe, scan, Observable, Subject, concatMap, filter, take
|
|
6
|
+
import { of, forkJoin, from, catchError, map, switchMap, isObservable, firstValueFrom, exhaustMap, EMPTY, debounceTime, distinctUntilChanged, tap, pipe, scan, Observable, Subject, concatMap, filter, take } from 'rxjs';
|
|
7
7
|
import { submit, required, disabled, readonly, hidden, applyEach, applyWhenValue, applyWhen, apply, email, minLength, maxLength, pattern, min, max, schema, validateStandardSchema, validateTree, form, provideSignalFormsConfig } from '@angular/forms/signals';
|
|
8
8
|
import { isStandardSchemaMarker } from '@ng-forge/dynamic-forms/schema';
|
|
9
9
|
import { isPlatformBrowser, NgComponentOutlet, AsyncPipe, NgTemplateOutlet } from '@angular/common';
|
|
10
|
-
import { HttpClient } from '@angular/common/http';
|
|
11
|
-
import { catchError as catchError$1 } from 'rxjs/operators';
|
|
12
10
|
import { NG_STATUS_CLASSES } from '@angular/forms/signals/compat';
|
|
13
11
|
|
|
14
12
|
const EMPTY_WRAPPERS = Object.freeze([]);
|
|
@@ -258,7 +256,8 @@ function buildFieldInputs(rawInputs, cache, fieldType) {
|
|
|
258
256
|
|
|
259
257
|
const EMPTY_STATE = Object.freeze({ phase: 'empty' });
|
|
260
258
|
class FieldComponentSlot {
|
|
261
|
-
state = signal(EMPTY_STATE,
|
|
259
|
+
state = signal(EMPTY_STATE, /* @ts-ignore */
|
|
260
|
+
...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
262
261
|
lastInputs = null;
|
|
263
262
|
phase = () => this.state().phase;
|
|
264
263
|
snapshot = () => this.state();
|
|
@@ -342,8 +341,10 @@ class FieldComponentSlot {
|
|
|
342
341
|
}
|
|
343
342
|
|
|
344
343
|
class DfFieldOutlet {
|
|
345
|
-
dfFieldOutlet = input.required(
|
|
346
|
-
|
|
344
|
+
dfFieldOutlet = input.required(/* @ts-ignore */
|
|
345
|
+
...(ngDevMode ? [{ debugName: "dfFieldOutlet" }] : /* istanbul ignore next */ []));
|
|
346
|
+
dfFieldOutletEnvironmentInjector = input(undefined, /* @ts-ignore */
|
|
347
|
+
...(ngDevMode ? [{ debugName: "dfFieldOutletEnvironmentInjector" }] : /* istanbul ignore next */ []));
|
|
347
348
|
vcrRef = inject(ViewContainerRef);
|
|
348
349
|
vcr = signal(this.vcrRef).asReadonly();
|
|
349
350
|
destroyRef = inject(DestroyRef);
|
|
@@ -351,14 +352,20 @@ class DfFieldOutlet {
|
|
|
351
352
|
defaultWrappersSignal = inject(DEFAULT_WRAPPERS, { optional: true });
|
|
352
353
|
readonlyFieldCache = inject(READONLY_FIELD_TREE_CACHE);
|
|
353
354
|
fieldComponent = new FieldComponentSlot();
|
|
354
|
-
componentIdentity = computed(() => this.dfFieldOutlet().component,
|
|
355
|
-
|
|
356
|
-
|
|
355
|
+
componentIdentity = computed(() => this.dfFieldOutlet().component, /* @ts-ignore */
|
|
356
|
+
...(ngDevMode ? [{ debugName: "componentIdentity" }] : /* istanbul ignore next */ []));
|
|
357
|
+
renderReady = computed(() => this.dfFieldOutlet().renderReady() && !this.dfFieldOutlet().hidden(), /* @ts-ignore */
|
|
358
|
+
...(ngDevMode ? [{ debugName: "renderReady" }] : /* istanbul ignore next */ []));
|
|
359
|
+
rawInputs = computed(() => this.dfFieldOutlet().inputs(), /* @ts-ignore */
|
|
360
|
+
...(ngDevMode ? [{ debugName: "rawInputs" }] : /* istanbul ignore next */ []));
|
|
357
361
|
wrappers = computed(() => resolveWrappers(this.dfFieldOutlet().fieldDef, this.defaultWrappersSignal?.(), this.wrapperAutoAssociations), { ...(ngDevMode ? { debugName: "wrappers" } : /* istanbul ignore next */ {}), equal: isSameWrapperChain });
|
|
358
|
-
fieldInputs = computed(() => buildFieldInputs(this.rawInputs(), this.readonlyFieldCache, this.dfFieldOutlet().fieldDef.type),
|
|
362
|
+
fieldInputs = computed(() => buildFieldInputs(this.rawInputs(), this.readonlyFieldCache, this.dfFieldOutlet().fieldDef.type), /* @ts-ignore */
|
|
363
|
+
...(ngDevMode ? [{ debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
|
|
359
364
|
defaultEnvInjector = inject(EnvironmentInjector);
|
|
360
|
-
fieldEnvInjector = computed(() => this.dfFieldOutletEnvironmentInjector() ?? this.defaultEnvInjector,
|
|
361
|
-
|
|
365
|
+
fieldEnvInjector = computed(() => this.dfFieldOutletEnvironmentInjector() ?? this.defaultEnvInjector, /* @ts-ignore */
|
|
366
|
+
...(ngDevMode ? [{ debugName: "fieldEnvInjector" }] : /* istanbul ignore next */ []));
|
|
367
|
+
fieldInjector = computed(() => this.dfFieldOutlet().injector, /* @ts-ignore */
|
|
368
|
+
...(ngDevMode ? [{ debugName: "fieldInjector" }] : /* istanbul ignore next */ []));
|
|
362
369
|
constructor() {
|
|
363
370
|
createWrapperChainController({
|
|
364
371
|
vcr: this.vcr,
|
|
@@ -376,10 +383,10 @@ class DfFieldOutlet {
|
|
|
376
383
|
explicitEffect([this.rawInputs, this.fieldInputs], ([rawInputs, fieldInputs]) => this.fieldComponent.pushInputs(rawInputs, fieldInputs));
|
|
377
384
|
this.destroyRef.onDestroy(() => this.fieldComponent.destroyOnTeardown());
|
|
378
385
|
}
|
|
379
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
380
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
386
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfFieldOutlet, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
387
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: DfFieldOutlet, isStandalone: true, selector: "[dfFieldOutlet]", inputs: { dfFieldOutlet: { classPropertyName: "dfFieldOutlet", publicName: "dfFieldOutlet", isSignal: true, isRequired: true, transformFunction: null }, dfFieldOutletEnvironmentInjector: { classPropertyName: "dfFieldOutletEnvironmentInjector", publicName: "dfFieldOutletEnvironmentInjector", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
381
388
|
}
|
|
382
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
389
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfFieldOutlet, decorators: [{
|
|
383
390
|
type: Directive,
|
|
384
391
|
args: [{
|
|
385
392
|
selector: '[dfFieldOutlet]',
|
|
@@ -547,26 +554,32 @@ class PageOrchestratorComponent {
|
|
|
547
554
|
fieldContextRegistry = inject(FieldContextRegistryService);
|
|
548
555
|
functionRegistry = inject(FunctionRegistryService);
|
|
549
556
|
formOptions = inject(FORM_OPTIONS, { optional: true });
|
|
550
|
-
pageFields = input.required(
|
|
551
|
-
|
|
552
|
-
|
|
557
|
+
pageFields = input.required(/* @ts-ignore */
|
|
558
|
+
...(ngDevMode ? [{ debugName: "pageFields" }] : /* istanbul ignore next */ []));
|
|
559
|
+
form = input.required(/* @ts-ignore */
|
|
560
|
+
...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
|
|
561
|
+
fieldSignalContext = input.required(/* @ts-ignore */
|
|
562
|
+
...(ngDevMode ? [{ debugName: "fieldSignalContext" }] : /* istanbul ignore next */ []));
|
|
553
563
|
pageHiddenStates = computed(() => {
|
|
554
564
|
const pages = this.pageFields();
|
|
555
565
|
return pages.map((page) => this.evaluatePageHidden(page));
|
|
556
|
-
},
|
|
566
|
+
}, /* @ts-ignore */
|
|
567
|
+
...(ngDevMode ? [{ debugName: "pageHiddenStates" }] : /* istanbul ignore next */ []));
|
|
557
568
|
visiblePageIndices = computed(() => {
|
|
558
569
|
const hiddenStates = this.pageHiddenStates();
|
|
559
570
|
return hiddenStates
|
|
560
571
|
.map((hidden, index) => ({ index, hidden }))
|
|
561
572
|
.filter((item) => !item.hidden)
|
|
562
573
|
.map((item) => item.index);
|
|
563
|
-
},
|
|
574
|
+
}, /* @ts-ignore */
|
|
575
|
+
...(ngDevMode ? [{ debugName: "visiblePageIndices" }] : /* istanbul ignore next */ []));
|
|
564
576
|
currentPageIndex = linkedSignal(() => {
|
|
565
577
|
const totalPages = this.pageFields().length;
|
|
566
578
|
if (totalPages === 0)
|
|
567
579
|
return 0;
|
|
568
580
|
return 0;
|
|
569
|
-
},
|
|
581
|
+
}, /* @ts-ignore */
|
|
582
|
+
...(ngDevMode ? [{ debugName: "currentPageIndex" }] : /* istanbul ignore next */ []));
|
|
570
583
|
state = computed(() => {
|
|
571
584
|
const currentIndex = this.currentPageIndex();
|
|
572
585
|
const totalPages = this.pageFields().length;
|
|
@@ -581,7 +594,8 @@ class PageOrchestratorComponent {
|
|
|
581
594
|
isLastPage: isLastVisiblePage,
|
|
582
595
|
navigationDisabled: false,
|
|
583
596
|
};
|
|
584
|
-
},
|
|
597
|
+
}, /* @ts-ignore */
|
|
598
|
+
...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
585
599
|
currentPageValid = computed(() => {
|
|
586
600
|
const currentIndex = this.currentPageIndex();
|
|
587
601
|
const pages = this.pageFields();
|
|
@@ -602,11 +616,13 @@ class PageOrchestratorComponent {
|
|
|
602
616
|
}
|
|
603
617
|
}
|
|
604
618
|
return true;
|
|
605
|
-
},
|
|
619
|
+
}, /* @ts-ignore */
|
|
620
|
+
...(ngDevMode ? [{ debugName: "currentPageValid" }] : /* istanbul ignore next */ []));
|
|
606
621
|
extendedFieldSignalContext = computed(() => ({
|
|
607
622
|
...this.fieldSignalContext(),
|
|
608
623
|
currentPageValid: this.currentPageValid,
|
|
609
|
-
}),
|
|
624
|
+
}), /* @ts-ignore */
|
|
625
|
+
...(ngDevMode ? [{ debugName: "extendedFieldSignalContext" }] : /* istanbul ignore next */ []));
|
|
610
626
|
constructor() {
|
|
611
627
|
this.setupEventListeners();
|
|
612
628
|
explicitEffect([this.state, this.visiblePageIndices], ([state, visibleIndices]) => {
|
|
@@ -765,8 +781,8 @@ class PageOrchestratorComponent {
|
|
|
765
781
|
}
|
|
766
782
|
return false;
|
|
767
783
|
}
|
|
768
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
769
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
784
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: PageOrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
785
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: PageOrchestratorComponent, isStandalone: true, selector: "div[page-orchestrator]", inputs: { pageFields: { classPropertyName: "pageFields", publicName: "pageFields", isSignal: true, isRequired: true, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, fieldSignalContext: { classPropertyName: "fieldSignalContext", publicName: "fieldSignalContext", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-current-page": "state().currentPageIndex", "attr.data-total-pages": "state().totalPages" }, classAttribute: "df-page-orchestrator" }, providers: [
|
|
770
786
|
{
|
|
771
787
|
provide: FIELD_SIGNAL_CONTEXT,
|
|
772
788
|
useFactory: (orchestrator) => orchestrator.extendedFieldSignalContext(),
|
|
@@ -805,9 +821,12 @@ class PageOrchestratorComponent {
|
|
|
805
821
|
}
|
|
806
822
|
}
|
|
807
823
|
}
|
|
808
|
-
`, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [
|
|
824
|
+
`, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [/* @ts-ignore */
|
|
825
|
+
import('./ng-forge-dynamic-forms-page-field.component-CjCd5Byo.mjs').then(m => m.default)], () => [/* @ts-ignore */
|
|
826
|
+
import('./ng-forge-dynamic-forms-page-field.component-CjCd5Byo.mjs').then(m => m.default)]] });
|
|
809
827
|
}
|
|
810
|
-
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "
|
|
828
|
+
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "22.0.0", ngImport: i0, type: PageOrchestratorComponent, resolveDeferredDeps: () => [/* @ts-ignore */
|
|
829
|
+
import('./ng-forge-dynamic-forms-page-field.component-CjCd5Byo.mjs').then(m => m.default)], resolveMetadata: PageFieldComponent => ({ decorators: [{
|
|
811
830
|
type: Component,
|
|
812
831
|
args: [{ selector: 'div[page-orchestrator]', imports: [PageFieldComponent], template: `
|
|
813
832
|
@for (pageField of pageFields(); track pageField.key; let i = $index) {
|
|
@@ -867,8 +886,6 @@ function collectLeafFieldKeys(fields) {
|
|
|
867
886
|
return keys;
|
|
868
887
|
}
|
|
869
888
|
|
|
870
|
-
const FORM_INITIALIZER = new InjectionToken('FORM_INITIALIZER');
|
|
871
|
-
|
|
872
889
|
const NORMALIZED_ARRAY_METADATA = Symbol('normalizedArrayMetadata');
|
|
873
890
|
function setNormalizedArrayMetadata(arrayField, metadata) {
|
|
874
891
|
arrayField[NORMALIZED_ARRAY_METADATA] = metadata;
|
|
@@ -1008,7 +1025,8 @@ function createSlot() {
|
|
|
1008
1025
|
let counter = 0;
|
|
1009
1026
|
const templates = new Map();
|
|
1010
1027
|
const itemOrder = [];
|
|
1011
|
-
const primitiveFieldKey = signal(undefined,
|
|
1028
|
+
const primitiveFieldKey = signal(undefined, /* @ts-ignore */
|
|
1029
|
+
...(ngDevMode ? [{ debugName: "primitiveFieldKey" }] : /* istanbul ignore next */ []));
|
|
1012
1030
|
return {
|
|
1013
1031
|
templates,
|
|
1014
1032
|
itemOrder,
|
|
@@ -1064,10 +1082,10 @@ class ArrayItemRegistryService {
|
|
|
1064
1082
|
slot.clear();
|
|
1065
1083
|
this.slots.clear();
|
|
1066
1084
|
}
|
|
1067
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1068
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
1085
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ArrayItemRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1086
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ArrayItemRegistryService });
|
|
1069
1087
|
}
|
|
1070
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1088
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ArrayItemRegistryService, decorators: [{
|
|
1071
1089
|
type: Injectable
|
|
1072
1090
|
}] });
|
|
1073
1091
|
|
|
@@ -1091,10 +1109,10 @@ class SchemaRegistryService {
|
|
|
1091
1109
|
}
|
|
1092
1110
|
return schema;
|
|
1093
1111
|
}
|
|
1094
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1095
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
1112
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SchemaRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1113
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SchemaRegistryService });
|
|
1096
1114
|
}
|
|
1097
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1115
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SchemaRegistryService, decorators: [{
|
|
1098
1116
|
type: Injectable
|
|
1099
1117
|
}] });
|
|
1100
1118
|
|
|
@@ -3208,7 +3226,8 @@ class FormStateMachine {
|
|
|
3208
3226
|
}
|
|
3209
3227
|
constructor(config) {
|
|
3210
3228
|
this.config = config;
|
|
3211
|
-
this._state = signal(createUninitializedState(),
|
|
3229
|
+
this._state = signal(createUninitializedState(), /* @ts-ignore */
|
|
3230
|
+
...(ngDevMode ? [{ debugName: "_state" }] : /* istanbul ignore next */ []));
|
|
3212
3231
|
this.state = this._state.asReadonly();
|
|
3213
3232
|
this.state$ = toObservable(this._state, { injector: config.injector });
|
|
3214
3233
|
this.setupActionProcessing();
|
|
@@ -3523,7 +3542,8 @@ class FormStateManager {
|
|
|
3523
3542
|
scheduler = createSideEffectScheduler(this.injector);
|
|
3524
3543
|
machine;
|
|
3525
3544
|
fieldProcessors = inject(CONTAINER_FIELD_PROCESSORS);
|
|
3526
|
-
fieldLoadingErrors = signal([],
|
|
3545
|
+
fieldLoadingErrors = signal([], /* @ts-ignore */
|
|
3546
|
+
...(ngDevMode ? [{ debugName: "fieldLoadingErrors" }] : /* istanbul ignore next */ []));
|
|
3527
3547
|
_formCache = { current: undefined };
|
|
3528
3548
|
activeConfig = computed(() => {
|
|
3529
3549
|
const state = this.machine.state();
|
|
@@ -3540,33 +3560,39 @@ class FormStateManager {
|
|
|
3540
3560
|
if (state.type === LifecycleState.Initializing)
|
|
3541
3561
|
return state.config;
|
|
3542
3562
|
return undefined;
|
|
3543
|
-
},
|
|
3563
|
+
}, /* @ts-ignore */
|
|
3564
|
+
...(ngDevMode ? [{ debugName: "activeConfig" }] : /* istanbul ignore next */ []));
|
|
3544
3565
|
renderPhase = computed(() => {
|
|
3545
3566
|
const state = this.machine.state();
|
|
3546
3567
|
if (isTransitioningState(state) && state.phase === Phase.Teardown)
|
|
3547
3568
|
return Phase.Teardown;
|
|
3548
3569
|
return 'render';
|
|
3549
|
-
},
|
|
3550
|
-
|
|
3570
|
+
}, /* @ts-ignore */
|
|
3571
|
+
...(ngDevMode ? [{ debugName: "renderPhase" }] : /* istanbul ignore next */ []));
|
|
3572
|
+
shouldRender = computed(() => this.activeConfig() !== undefined, /* @ts-ignore */
|
|
3573
|
+
...(ngDevMode ? [{ debugName: "shouldRender" }] : /* istanbul ignore next */ []));
|
|
3551
3574
|
formModeDetection = computed(() => {
|
|
3552
3575
|
const config = this.activeConfig();
|
|
3553
3576
|
if (!config) {
|
|
3554
3577
|
return { mode: 'non-paged', isValid: true, errors: [] };
|
|
3555
3578
|
}
|
|
3556
3579
|
return detectFormMode(config.fields || []);
|
|
3557
|
-
},
|
|
3580
|
+
}, /* @ts-ignore */
|
|
3581
|
+
...(ngDevMode ? [{ debugName: "formModeDetection" }] : /* istanbul ignore next */ []));
|
|
3558
3582
|
formConfigValidation = computed(() => {
|
|
3559
3583
|
const config = this.activeConfig();
|
|
3560
3584
|
if (!config)
|
|
3561
3585
|
return { isValid: true, errors: [], warnings: [] };
|
|
3562
3586
|
return FormModeValidator.validateFormConfiguration(config.fields || []);
|
|
3563
|
-
},
|
|
3587
|
+
}, /* @ts-ignore */
|
|
3588
|
+
...(ngDevMode ? [{ debugName: "formConfigValidation" }] : /* istanbul ignore next */ []));
|
|
3564
3589
|
effectiveFormOptions = computed(() => {
|
|
3565
3590
|
const config = this.activeConfig();
|
|
3566
3591
|
const configOptions = config?.options || {};
|
|
3567
3592
|
const inputOptions = this.deps.formOptions() ?? undefined;
|
|
3568
3593
|
return { ...configOptions, ...inputOptions };
|
|
3569
|
-
},
|
|
3594
|
+
}, /* @ts-ignore */
|
|
3595
|
+
...(ngDevMode ? [{ debugName: "effectiveFormOptions" }] : /* istanbul ignore next */ []));
|
|
3570
3596
|
pageFieldDefinitions = computed(() => {
|
|
3571
3597
|
const config = this.activeConfig();
|
|
3572
3598
|
const mode = this.formModeDetection().mode;
|
|
@@ -3574,8 +3600,10 @@ class FormStateManager {
|
|
|
3574
3600
|
return config.fields.filter(isPageField);
|
|
3575
3601
|
}
|
|
3576
3602
|
return [];
|
|
3577
|
-
},
|
|
3578
|
-
|
|
3603
|
+
}, /* @ts-ignore */
|
|
3604
|
+
...(ngDevMode ? [{ debugName: "pageFieldDefinitions" }] : /* istanbul ignore next */ []));
|
|
3605
|
+
rawFieldRegistry = computed(() => this.fieldRegistry.raw, /* @ts-ignore */
|
|
3606
|
+
...(ngDevMode ? [{ debugName: "rawFieldRegistry" }] : /* istanbul ignore next */ []));
|
|
3579
3607
|
formSetup = computed(() => {
|
|
3580
3608
|
const registry = this.rawFieldRegistry();
|
|
3581
3609
|
const state = this.machine.state();
|
|
@@ -3600,14 +3628,17 @@ class FormStateManager {
|
|
|
3600
3628
|
return this.createFormSetupFromConfig(config.fields, modeDetection.mode, registry);
|
|
3601
3629
|
}
|
|
3602
3630
|
return this.createEmptyFormSetup(registry);
|
|
3603
|
-
},
|
|
3604
|
-
|
|
3631
|
+
}, /* @ts-ignore */
|
|
3632
|
+
...(ngDevMode ? [{ debugName: "formSetup" }] : /* istanbul ignore next */ []));
|
|
3633
|
+
defaultValues = linkedSignal(() => this.formSetup().defaultValues, /* @ts-ignore */
|
|
3634
|
+
...(ngDevMode ? [{ debugName: "defaultValues" }] : /* istanbul ignore next */ []));
|
|
3605
3635
|
validKeys = computed(() => {
|
|
3606
3636
|
const schemaFields = this.formSetup().schemaFields;
|
|
3607
3637
|
if (!schemaFields || schemaFields.length === 0)
|
|
3608
3638
|
return undefined;
|
|
3609
3639
|
return new Set(schemaFields.map((f) => f.key).filter((key) => key !== undefined));
|
|
3610
|
-
},
|
|
3640
|
+
}, /* @ts-ignore */
|
|
3641
|
+
...(ngDevMode ? [{ debugName: "validKeys" }] : /* istanbul ignore next */ []));
|
|
3611
3642
|
fieldStateSnapshot = computed(() => {
|
|
3612
3643
|
const setup = this.formSetup();
|
|
3613
3644
|
if (!setup.schemaFields || setup.schemaFields.length === 0)
|
|
@@ -3615,9 +3646,12 @@ class FormStateManager {
|
|
|
3615
3646
|
const snapshot = {};
|
|
3616
3647
|
collectFieldStateSnapshot(setup.schemaFields, asFieldTreeRecord(this.form()), [], snapshot);
|
|
3617
3648
|
return snapshot;
|
|
3618
|
-
},
|
|
3619
|
-
|
|
3620
|
-
|
|
3649
|
+
}, /* @ts-ignore */
|
|
3650
|
+
...(ngDevMode ? [{ debugName: "fieldStateSnapshot" }] : /* istanbul ignore next */ []));
|
|
3651
|
+
excludedValueStore = signal({}, /* @ts-ignore */
|
|
3652
|
+
...(ngDevMode ? [{ debugName: "excludedValueStore" }] : /* istanbul ignore next */ []));
|
|
3653
|
+
prevFieldStateSnapshot = signal({}, /* @ts-ignore */
|
|
3654
|
+
...(ngDevMode ? [{ debugName: "prevFieldStateSnapshot" }] : /* istanbul ignore next */ []));
|
|
3621
3655
|
entity = linkedSignal(() => {
|
|
3622
3656
|
const inputValue = this.deps.value();
|
|
3623
3657
|
const defaults = this.defaultValues();
|
|
@@ -3656,12 +3690,14 @@ class FormStateManager {
|
|
|
3656
3690
|
return createFormLevelSchema(config.schema);
|
|
3657
3691
|
}
|
|
3658
3692
|
return undefined;
|
|
3659
|
-
}),
|
|
3693
|
+
}), /* @ts-ignore */
|
|
3694
|
+
...(ngDevMode ? [{ debugName: "formSchema" }] : /* istanbul ignore next */ []));
|
|
3660
3695
|
form = computed(() => {
|
|
3661
3696
|
const schema = this.formSchema();
|
|
3662
3697
|
const injector = this.injector;
|
|
3663
3698
|
return untracked(() => (schema ? form(this.entity, schema, { injector }) : form(this.entity, { injector })));
|
|
3664
|
-
},
|
|
3699
|
+
}, /* @ts-ignore */
|
|
3700
|
+
...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
|
|
3665
3701
|
isFieldPipelineSettled;
|
|
3666
3702
|
fieldSignalContext = computed(() => {
|
|
3667
3703
|
const formSignal = this.form;
|
|
@@ -3681,9 +3717,12 @@ class FormStateManager {
|
|
|
3681
3717
|
return currentForm;
|
|
3682
3718
|
},
|
|
3683
3719
|
};
|
|
3684
|
-
},
|
|
3685
|
-
|
|
3686
|
-
|
|
3720
|
+
}, /* @ts-ignore */
|
|
3721
|
+
...(ngDevMode ? [{ debugName: "fieldSignalContext" }] : /* istanbul ignore next */ []));
|
|
3722
|
+
formInstance = computed(() => this.form()(), /* @ts-ignore */
|
|
3723
|
+
...(ngDevMode ? [{ debugName: "formInstance" }] : /* istanbul ignore next */ []));
|
|
3724
|
+
formValue = computed(() => this.formInstance().value(), /* @ts-ignore */
|
|
3725
|
+
...(ngDevMode ? [{ debugName: "formValue" }] : /* istanbul ignore next */ []));
|
|
3687
3726
|
filteredFormValue = computed(() => {
|
|
3688
3727
|
const rawValue = this.formValue();
|
|
3689
3728
|
const setup = this.formSetup();
|
|
@@ -3699,7 +3738,8 @@ class FormStateManager {
|
|
|
3699
3738
|
};
|
|
3700
3739
|
const fieldTreeRecord = asFieldTreeRecord(formTree);
|
|
3701
3740
|
return filterFormValue(rawValue, setup.schemaFields, fieldTreeRecord, setup.registry, this.valueExclusionDefaults, formOptions);
|
|
3702
|
-
},
|
|
3741
|
+
}, /* @ts-ignore */
|
|
3742
|
+
...(ngDevMode ? [{ debugName: "filteredFormValue" }] : /* istanbul ignore next */ []));
|
|
3703
3743
|
boundFormValue = computed(() => {
|
|
3704
3744
|
const rawValue = this.formValue();
|
|
3705
3745
|
const setup = this.formSetup();
|
|
@@ -3715,18 +3755,26 @@ class FormStateManager {
|
|
|
3715
3755
|
};
|
|
3716
3756
|
const fieldTreeRecord = asFieldTreeRecord(formTree);
|
|
3717
3757
|
return filterFormValue(rawValue, setup.schemaFields, fieldTreeRecord, setup.registry, BOUND_VALUE_EXCLUSION_BASELINE, formOptions);
|
|
3718
|
-
},
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3758
|
+
}, /* @ts-ignore */
|
|
3759
|
+
...(ngDevMode ? [{ debugName: "boundFormValue" }] : /* istanbul ignore next */ []));
|
|
3760
|
+
valid = computed(() => this.formInstance().valid(), /* @ts-ignore */
|
|
3761
|
+
...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
|
|
3762
|
+
invalid = computed(() => this.formInstance().invalid(), /* @ts-ignore */
|
|
3763
|
+
...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
|
|
3764
|
+
dirty = computed(() => this.formInstance().dirty(), /* @ts-ignore */
|
|
3765
|
+
...(ngDevMode ? [{ debugName: "dirty" }] : /* istanbul ignore next */ []));
|
|
3766
|
+
touched = computed(() => this.formInstance().touched(), /* @ts-ignore */
|
|
3767
|
+
...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
|
|
3768
|
+
errors = computed(() => this.formInstance().errors(), /* @ts-ignore */
|
|
3769
|
+
...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
3724
3770
|
disabled = computed(() => {
|
|
3725
3771
|
const optionsDisabled = this.effectiveFormOptions().disabled;
|
|
3726
3772
|
const formDisabled = this.formInstance().disabled();
|
|
3727
3773
|
return optionsDisabled ?? formDisabled;
|
|
3728
|
-
},
|
|
3729
|
-
|
|
3774
|
+
}, /* @ts-ignore */
|
|
3775
|
+
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
3776
|
+
submitting = computed(() => this.formInstance().submitting(), /* @ts-ignore */
|
|
3777
|
+
...(ngDevMode ? [{ debugName: "submitting" }] : /* istanbul ignore next */ []));
|
|
3730
3778
|
fieldsSource = computed(() => {
|
|
3731
3779
|
const state = this.machine.state();
|
|
3732
3780
|
if (isTransitioningState(state)) {
|
|
@@ -3751,7 +3799,8 @@ class FormStateManager {
|
|
|
3751
3799
|
fieldInjector = computed(() => Injector.create({
|
|
3752
3800
|
parent: this.injector,
|
|
3753
3801
|
providers: [{ provide: FIELD_SIGNAL_CONTEXT, useValue: this.fieldSignalContext() }],
|
|
3754
|
-
}),
|
|
3802
|
+
}), /* @ts-ignore */
|
|
3803
|
+
...(ngDevMode ? [{ debugName: "fieldInjector" }] : /* istanbul ignore next */ []));
|
|
3755
3804
|
resolvedFields;
|
|
3756
3805
|
submitted$;
|
|
3757
3806
|
reset$;
|
|
@@ -3817,7 +3866,8 @@ class FormStateManager {
|
|
|
3817
3866
|
}), map((fields) => fields.filter((f) => f !== undefined)), scan(reconcileFields, [])), { initialValue: [], injector: this.injector });
|
|
3818
3867
|
this.isFieldPipelineSettled = computed(() => {
|
|
3819
3868
|
return this.fieldsSource().length === this.resolvedFields().length;
|
|
3820
|
-
},
|
|
3869
|
+
}, /* @ts-ignore */
|
|
3870
|
+
...(ngDevMode ? [{ debugName: "isFieldPipelineSettled" }] : /* istanbul ignore next */ []));
|
|
3821
3871
|
this.submitted$ = this.eventBus.on('submit').pipe(filter(() => {
|
|
3822
3872
|
if (!this.valid()) {
|
|
3823
3873
|
this.logger.debug('Form submitted while invalid, not emitting to (submitted) output');
|
|
@@ -4038,17 +4088,115 @@ class FormStateManager {
|
|
|
4038
4088
|
ngOnDestroy() {
|
|
4039
4089
|
this.machine.dispatch({ type: Action.Destroy });
|
|
4040
4090
|
}
|
|
4041
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
4042
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
4091
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormStateManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4092
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormStateManager });
|
|
4043
4093
|
}
|
|
4044
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
4094
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormStateManager, decorators: [{
|
|
4045
4095
|
type: Injectable
|
|
4046
4096
|
}], ctorParameters: () => [] });
|
|
4047
4097
|
|
|
4098
|
+
function traverseFieldsWithContext(fields, context, visitor, hooks) {
|
|
4099
|
+
for (const field of fields) {
|
|
4100
|
+
visitor(field, context);
|
|
4101
|
+
if (!hasChildFields(field))
|
|
4102
|
+
continue;
|
|
4103
|
+
if (field.type === 'array') {
|
|
4104
|
+
const childContext = mergeContext(context, hooks?.onArrayChild?.(context, field));
|
|
4105
|
+
const arrayChildren = collectArrayChildren(field);
|
|
4106
|
+
traverseFieldsWithContext(arrayChildren, childContext, visitor, hooks);
|
|
4107
|
+
continue;
|
|
4108
|
+
}
|
|
4109
|
+
const overrides = field.type === 'group' ? hooks?.onGroupChild?.(context, field) : hooks?.onLayoutChild?.(context, field);
|
|
4110
|
+
const childContext = mergeContext(context, overrides);
|
|
4111
|
+
traverseFieldsWithContext(normalizeFieldsArray(field.fields), childContext, visitor, hooks);
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
function someField(fields, predicate) {
|
|
4115
|
+
return fields.some((field) => {
|
|
4116
|
+
if (predicate(field))
|
|
4117
|
+
return true;
|
|
4118
|
+
if (!hasChildFields(field))
|
|
4119
|
+
return false;
|
|
4120
|
+
const children = field.type === 'array' ? collectArrayChildren(field) : normalizeFieldsArray(field.fields);
|
|
4121
|
+
return someField(children, predicate);
|
|
4122
|
+
});
|
|
4123
|
+
}
|
|
4124
|
+
function collectArrayChildren(field) {
|
|
4125
|
+
let arrayItems = normalizeFieldsArray(field.fields);
|
|
4126
|
+
if (arrayItems.length === 0) {
|
|
4127
|
+
const metadataTemplate = getNormalizedArrayMetadata(field)?.template;
|
|
4128
|
+
if (metadataTemplate) {
|
|
4129
|
+
arrayItems = [
|
|
4130
|
+
Array.isArray(metadataTemplate) ? [...metadataTemplate] : metadataTemplate,
|
|
4131
|
+
];
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
const flattened = [];
|
|
4135
|
+
for (const item of arrayItems) {
|
|
4136
|
+
if (Array.isArray(item)) {
|
|
4137
|
+
flattened.push(...item);
|
|
4138
|
+
}
|
|
4139
|
+
else {
|
|
4140
|
+
flattened.push(item);
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
return flattened;
|
|
4144
|
+
}
|
|
4145
|
+
function mergeContext(base, overrides) {
|
|
4146
|
+
return overrides ? { ...base, ...overrides } : { ...base };
|
|
4147
|
+
}
|
|
4148
|
+
|
|
4149
|
+
function configHasDerivations(fields) {
|
|
4150
|
+
return someField(fields, (field) => {
|
|
4151
|
+
if (!field.key)
|
|
4152
|
+
return false;
|
|
4153
|
+
const validationField = field;
|
|
4154
|
+
return !!validationField.derivation || !!validationField.logic?.some(isDerivationLogicConfig);
|
|
4155
|
+
});
|
|
4156
|
+
}
|
|
4157
|
+
|
|
4158
|
+
const DERIVATION_RENDER_GATE = new InjectionToken('DERIVATION_RENDER_GATE');
|
|
4159
|
+
function createDerivationRenderGate() {
|
|
4160
|
+
const stateManager = inject(FormStateManager);
|
|
4161
|
+
const injector = inject(Injector);
|
|
4162
|
+
const pendingTasks = inject(PendingTasks);
|
|
4163
|
+
const logger = inject(DynamicFormLogger);
|
|
4164
|
+
const hasDerivations = computed(() => {
|
|
4165
|
+
const setup = stateManager.formSetup();
|
|
4166
|
+
return !!setup && configHasDerivations(setup.schemaFields);
|
|
4167
|
+
}, /* @ts-ignore */
|
|
4168
|
+
...(ngDevMode ? [{ debugName: "hasDerivations" }] : /* istanbul ignore next */ []));
|
|
4169
|
+
const engine = signal('idle', /* @ts-ignore */
|
|
4170
|
+
...(ngDevMode ? [{ debugName: "engine" }] : /* istanbul ignore next */ []));
|
|
4171
|
+
explicitEffect([hasDerivations], ([has]) => {
|
|
4172
|
+
if (!has || engine() !== 'idle')
|
|
4173
|
+
return;
|
|
4174
|
+
engine.set('loading');
|
|
4175
|
+
pendingTasks.run(() => import('./ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-QJzwEYSj.mjs')
|
|
4176
|
+
.then(({ bootstrapDerivationOrchestrator }) => {
|
|
4177
|
+
runInInjectionContext(injector, () => bootstrapDerivationOrchestrator());
|
|
4178
|
+
engine.set('wired');
|
|
4179
|
+
})
|
|
4180
|
+
.catch((error) => {
|
|
4181
|
+
engine.set('failed');
|
|
4182
|
+
logger.error('[Dynamic Forms] Failed to load the derivation engine; derivations will not apply.', error);
|
|
4183
|
+
}));
|
|
4184
|
+
});
|
|
4185
|
+
return computed(() => {
|
|
4186
|
+
if (!hasDerivations())
|
|
4187
|
+
return true;
|
|
4188
|
+
const state = engine();
|
|
4189
|
+
return state === 'wired' || state === 'failed';
|
|
4190
|
+
});
|
|
4191
|
+
}
|
|
4192
|
+
|
|
4193
|
+
const FORM_INITIALIZER = new InjectionToken('FORM_INITIALIZER');
|
|
4194
|
+
|
|
4048
4195
|
const AUTO_ID_PREFIX = 'df-';
|
|
4049
4196
|
class DynamicFormInstanceRegistry {
|
|
4050
4197
|
slots = new Map();
|
|
4051
|
-
_membership = signal(0,
|
|
4198
|
+
_membership = signal(0, /* @ts-ignore */
|
|
4199
|
+
...(ngDevMode ? [{ debugName: "_membership" }] : /* istanbul ignore next */ []));
|
|
4052
4200
|
multiplePresent = computed(() => {
|
|
4053
4201
|
this._membership();
|
|
4054
4202
|
let visible = 0;
|
|
@@ -4060,7 +4208,8 @@ class DynamicFormInstanceRegistry {
|
|
|
4060
4208
|
}
|
|
4061
4209
|
}
|
|
4062
4210
|
return false;
|
|
4063
|
-
},
|
|
4211
|
+
}, /* @ts-ignore */
|
|
4212
|
+
...(ngDevMode ? [{ debugName: "multiplePresent" }] : /* istanbul ignore next */ []));
|
|
4064
4213
|
register(visible) {
|
|
4065
4214
|
let slot = 1;
|
|
4066
4215
|
while (this.slots.has(slot))
|
|
@@ -4073,10 +4222,10 @@ class DynamicFormInstanceRegistry {
|
|
|
4073
4222
|
this.slots.delete(Number(id.slice(AUTO_ID_PREFIX.length)));
|
|
4074
4223
|
this._membership.update((m) => m + 1);
|
|
4075
4224
|
}
|
|
4076
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
4077
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
4225
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DynamicFormInstanceRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4226
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DynamicFormInstanceRegistry, providedIn: 'root' });
|
|
4078
4227
|
}
|
|
4079
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
4228
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DynamicFormInstanceRegistry, decorators: [{
|
|
4080
4229
|
type: Injectable,
|
|
4081
4230
|
args: [{ providedIn: 'root' }]
|
|
4082
4231
|
}] });
|
|
@@ -4098,15 +4247,18 @@ class FormIdPrefixService {
|
|
|
4098
4247
|
options = inject(FORM_OPTIONS, { optional: true });
|
|
4099
4248
|
logger = inject(DynamicFormLogger, { optional: true });
|
|
4100
4249
|
host = inject((ElementRef)).nativeElement;
|
|
4101
|
-
visible = signal(true,
|
|
4250
|
+
visible = signal(true, /* @ts-ignore */
|
|
4251
|
+
...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
4102
4252
|
autoId = this.registry.register(this.visible);
|
|
4103
|
-
rawPrefix = computed(() => this.options?.()?.idPrefix?.trim() || undefined,
|
|
4253
|
+
rawPrefix = computed(() => this.options?.()?.idPrefix?.trim() || undefined, /* @ts-ignore */
|
|
4254
|
+
...(ngDevMode ? [{ debugName: "rawPrefix" }] : /* istanbul ignore next */ []));
|
|
4104
4255
|
prefix = computed(() => {
|
|
4105
4256
|
const explicit = this.rawPrefix();
|
|
4106
4257
|
if (explicit)
|
|
4107
4258
|
return sanitizeIdPrefix(explicit);
|
|
4108
4259
|
return this.registry.multiplePresent() ? this.autoId : '';
|
|
4109
|
-
},
|
|
4260
|
+
}, /* @ts-ignore */
|
|
4261
|
+
...(ngDevMode ? [{ debugName: "prefix" }] : /* istanbul ignore next */ []));
|
|
4110
4262
|
constructor() {
|
|
4111
4263
|
const destroyRef = inject(DestroyRef);
|
|
4112
4264
|
destroyRef.onDestroy(() => this.registry.unregister(this.autoId));
|
|
@@ -4124,10 +4276,10 @@ class FormIdPrefixService {
|
|
|
4124
4276
|
});
|
|
4125
4277
|
}
|
|
4126
4278
|
}
|
|
4127
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
4128
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
4279
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormIdPrefixService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4280
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormIdPrefixService });
|
|
4129
4281
|
}
|
|
4130
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
4282
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormIdPrefixService, decorators: [{
|
|
4131
4283
|
type: Injectable
|
|
4132
4284
|
}], ctorParameters: () => [] });
|
|
4133
4285
|
|
|
@@ -4136,2588 +4288,159 @@ const DERIVATION_WARNING_TRACKER = new InjectionToken('DerivationWarningTracker'
|
|
|
4136
4288
|
factory: createWarningTracker,
|
|
4137
4289
|
});
|
|
4138
4290
|
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
};
|
|
4148
|
-
}
|
|
4149
|
-
return {
|
|
4150
|
-
arrayPath: path.substring(0, placeholderIndex),
|
|
4151
|
-
relativePath: path.substring(placeholderIndex + ARRAY_PLACEHOLDER.length),
|
|
4152
|
-
isArrayPath: true,
|
|
4153
|
-
};
|
|
4154
|
-
}
|
|
4155
|
-
function resolveArrayPath(path, index) {
|
|
4156
|
-
const info = parseArrayPath(path);
|
|
4157
|
-
if (!info.isArrayPath) {
|
|
4158
|
-
return path;
|
|
4159
|
-
}
|
|
4160
|
-
return `${info.arrayPath}.${index}.${info.relativePath}`;
|
|
4161
|
-
}
|
|
4162
|
-
function isArrayPlaceholderPath(path) {
|
|
4163
|
-
return path.includes('.$.');
|
|
4164
|
-
}
|
|
4165
|
-
function extractArrayPath(path) {
|
|
4166
|
-
return parseArrayPath(path).arrayPath;
|
|
4167
|
-
}
|
|
4168
|
-
function splitPath(path) {
|
|
4169
|
-
if (!path)
|
|
4170
|
-
return [];
|
|
4171
|
-
return path
|
|
4172
|
-
.replace(/\[(\d+)\]/g, '.$1')
|
|
4173
|
-
.split('.')
|
|
4174
|
-
.filter(Boolean);
|
|
4175
|
-
}
|
|
4176
|
-
function joinPath(segments) {
|
|
4177
|
-
return segments.join('.');
|
|
4178
|
-
}
|
|
4179
|
-
function getParentPath(path) {
|
|
4180
|
-
const segments = splitPath(path);
|
|
4181
|
-
if (segments.length <= 1)
|
|
4182
|
-
return '';
|
|
4183
|
-
return joinPath(segments.slice(0, -1));
|
|
4184
|
-
}
|
|
4185
|
-
function getLeafPath(path) {
|
|
4186
|
-
const segments = splitPath(path);
|
|
4187
|
-
return segments[segments.length - 1] ?? '';
|
|
4188
|
-
}
|
|
4189
|
-
function parseMultiArrayPath(path) {
|
|
4190
|
-
const parts = path.split('.');
|
|
4191
|
-
const segments = [];
|
|
4192
|
-
const placeholderPositions = [];
|
|
4193
|
-
for (let i = 0; i < parts.length; i++) {
|
|
4194
|
-
if (parts[i] === '$') {
|
|
4195
|
-
placeholderPositions.push(i);
|
|
4196
|
-
}
|
|
4197
|
-
else {
|
|
4198
|
-
segments.push(parts[i]);
|
|
4199
|
-
}
|
|
4200
|
-
}
|
|
4201
|
-
return {
|
|
4202
|
-
isArrayPath: placeholderPositions.length > 0,
|
|
4203
|
-
placeholderCount: placeholderPositions.length,
|
|
4204
|
-
segments,
|
|
4205
|
-
placeholderPositions,
|
|
4206
|
-
};
|
|
4207
|
-
}
|
|
4208
|
-
function resolveMultiArrayPath(path, indices) {
|
|
4209
|
-
const parts = path.split('.');
|
|
4210
|
-
let indexPointer = 0;
|
|
4211
|
-
const resolved = parts.map((part) => {
|
|
4212
|
-
if (part === '$') {
|
|
4213
|
-
if (indexPointer >= indices.length) {
|
|
4214
|
-
throw new Error(`Not enough indices provided for path '${path}'. ` +
|
|
4215
|
-
`Expected ${parts.filter((p) => p === '$').length} indices, got ${indices.length}.`);
|
|
4216
|
-
}
|
|
4217
|
-
return String(indices[indexPointer++]);
|
|
4291
|
+
const ADDON_ACTION_HANDLERS = new InjectionToken('ADDON_ACTION_HANDLERS');
|
|
4292
|
+
const ADDON_ACTION_REGISTRY = new InjectionToken('ADDON_ACTION_REGISTRY');
|
|
4293
|
+
function createAddonActionRegistry() {
|
|
4294
|
+
const sources = inject(ADDON_ACTION_HANDLERS, { optional: true }) ?? [];
|
|
4295
|
+
const map = new Map();
|
|
4296
|
+
for (const handlers of sources) {
|
|
4297
|
+
for (const [name, fn] of Object.entries(handlers)) {
|
|
4298
|
+
map.set(name, fn);
|
|
4218
4299
|
}
|
|
4219
|
-
return part;
|
|
4220
|
-
});
|
|
4221
|
-
if (indexPointer < indices.length) {
|
|
4222
|
-
throw new Error(`Too many indices provided for path '${path}'. ` + `Expected ${indexPointer} indices, got ${indices.length}.`);
|
|
4223
4300
|
}
|
|
4224
|
-
return
|
|
4225
|
-
}
|
|
4226
|
-
function countArrayPlaceholders(path) {
|
|
4227
|
-
return path.split('.').filter((part) => part === '$').length;
|
|
4301
|
+
return map;
|
|
4228
4302
|
}
|
|
4229
4303
|
|
|
4230
|
-
function
|
|
4231
|
-
return
|
|
4304
|
+
function coreProviders() {
|
|
4305
|
+
return [
|
|
4306
|
+
EventBus,
|
|
4307
|
+
{ provide: CONTAINER_FIELD_PROCESSORS, useFactory: createContainerFieldProcessors },
|
|
4308
|
+
SchemaRegistryService,
|
|
4309
|
+
FunctionRegistryService,
|
|
4310
|
+
{
|
|
4311
|
+
provide: FORM_STATE_DEPS,
|
|
4312
|
+
useFactory: () => ({ config: null, formOptions: null, value: null, source: null }),
|
|
4313
|
+
},
|
|
4314
|
+
FormStateManager,
|
|
4315
|
+
{
|
|
4316
|
+
provide: RootFormRegistryService,
|
|
4317
|
+
useFactory: (stateManager) => new RootFormRegistryService(stateManager.formValue, computed(() => stateManager.form())),
|
|
4318
|
+
deps: [FormStateManager],
|
|
4319
|
+
},
|
|
4320
|
+
FieldContextRegistryService,
|
|
4321
|
+
ArrayItemRegistryService,
|
|
4322
|
+
{
|
|
4323
|
+
provide: DEFAULT_PROPS,
|
|
4324
|
+
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultProps),
|
|
4325
|
+
deps: [FormStateManager],
|
|
4326
|
+
},
|
|
4327
|
+
{
|
|
4328
|
+
provide: DEFAULT_WRAPPERS,
|
|
4329
|
+
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultWrappers),
|
|
4330
|
+
deps: [FormStateManager],
|
|
4331
|
+
},
|
|
4332
|
+
{
|
|
4333
|
+
provide: DEFAULT_VALIDATION_MESSAGES,
|
|
4334
|
+
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultValidationMessages),
|
|
4335
|
+
deps: [FormStateManager],
|
|
4336
|
+
},
|
|
4337
|
+
{
|
|
4338
|
+
provide: FORM_OPTIONS,
|
|
4339
|
+
useFactory: (stateManager) => stateManager.effectiveFormOptions,
|
|
4340
|
+
deps: [FormStateManager],
|
|
4341
|
+
},
|
|
4342
|
+
{
|
|
4343
|
+
provide: EXTERNAL_DATA,
|
|
4344
|
+
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.externalData),
|
|
4345
|
+
deps: [FormStateManager],
|
|
4346
|
+
},
|
|
4347
|
+
FormIdPrefixService,
|
|
4348
|
+
{
|
|
4349
|
+
provide: FORM_ID_PREFIX,
|
|
4350
|
+
useFactory: (svc) => svc.prefix,
|
|
4351
|
+
deps: [FormIdPrefixService],
|
|
4352
|
+
},
|
|
4353
|
+
{ provide: DEPRECATION_WARNING_TRACKER, useFactory: createWarningTracker },
|
|
4354
|
+
{ provide: ADDON_ACTION_REGISTRY, useFactory: createAddonActionRegistry },
|
|
4355
|
+
];
|
|
4232
4356
|
}
|
|
4233
|
-
function
|
|
4234
|
-
return
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4357
|
+
function derivationProviders() {
|
|
4358
|
+
return [
|
|
4359
|
+
{ provide: DERIVATION_WARNING_TRACKER, useFactory: createWarningTracker },
|
|
4360
|
+
{ provide: DERIVATION_RENDER_GATE, useFactory: createDerivationRenderGate },
|
|
4361
|
+
];
|
|
4238
4362
|
}
|
|
4239
|
-
function
|
|
4240
|
-
return
|
|
4363
|
+
function propertyDerivationProviders() {
|
|
4364
|
+
return [{ provide: PROPERTY_OVERRIDE_STORE, useFactory: createPropertyOverrideStore }];
|
|
4241
4365
|
}
|
|
4242
|
-
function
|
|
4243
|
-
return {
|
|
4366
|
+
function httpExpressionProviders() {
|
|
4367
|
+
return [{ provide: HTTP_CONDITION_CACHE, useFactory: () => new HttpConditionCache(100) }, HttpConditionFunctionCacheService];
|
|
4244
4368
|
}
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
if (value === null || typeof value !== 'object')
|
|
4248
|
-
return false;
|
|
4249
|
-
const candidate = value;
|
|
4250
|
-
return typeof candidate.then === 'function' || typeof candidate.subscribe === 'function';
|
|
4369
|
+
function asyncExpressionProviders() {
|
|
4370
|
+
return [AsyncConditionFunctionCacheService];
|
|
4251
4371
|
}
|
|
4252
|
-
function
|
|
4253
|
-
|
|
4254
|
-
return entry.value;
|
|
4255
|
-
}
|
|
4256
|
-
if (entry.expression) {
|
|
4257
|
-
return ExpressionParser.evaluate(entry.expression, evalContext);
|
|
4258
|
-
}
|
|
4259
|
-
if (entry.fn || entry.functionName) {
|
|
4260
|
-
if (entry.fn && entry.functionName) {
|
|
4261
|
-
evalContext.logger.warn(`Both "fn" and "functionName" are set on derivation for '${options.subject}'. Inline "fn" takes precedence; "functionName" is ignored.`);
|
|
4262
|
-
}
|
|
4263
|
-
const fn = entry.fn ?? (entry.functionName ? options.derivationFunctions?.[entry.functionName] : undefined);
|
|
4264
|
-
if (!fn) {
|
|
4265
|
-
const kind = options.functionKind ?? 'Derivation function';
|
|
4266
|
-
throw new DynamicFormError(`${kind} '${entry.functionName}' not found in customFnConfig.derivations`);
|
|
4267
|
-
}
|
|
4268
|
-
const result = fn(evalContext);
|
|
4269
|
-
if (isThenableOrObservable(result)) {
|
|
4270
|
-
evalContext.logger.warn(`Derivation for '${options.subject}' returned a Promise or Observable. ` +
|
|
4271
|
-
`Synchronous derivations must return a value; use 'asyncDerivations' or an HTTP derivation for asynchronous logic. Skipping.`);
|
|
4272
|
-
return undefined;
|
|
4273
|
-
}
|
|
4274
|
-
return result;
|
|
4275
|
-
}
|
|
4276
|
-
throw new DynamicFormError(`Derivation for ${options.subject} has no value source. Specify 'value', 'expression', 'functionName', or 'fn'.`);
|
|
4372
|
+
function dynamicValueProviders() {
|
|
4373
|
+
return [DynamicValueFunctionCacheService];
|
|
4277
4374
|
}
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
const lastDot = fieldKey.lastIndexOf('.');
|
|
4281
|
-
if (lastDot <= 0)
|
|
4282
|
-
return undefined;
|
|
4283
|
-
return fieldKey.slice(0, lastDot);
|
|
4375
|
+
function logicProviders() {
|
|
4376
|
+
return [LogicFunctionCacheService];
|
|
4284
4377
|
}
|
|
4285
|
-
function
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4378
|
+
function provideDynamicFormDI() {
|
|
4379
|
+
return [
|
|
4380
|
+
...coreProviders(),
|
|
4381
|
+
...logicProviders(),
|
|
4382
|
+
...dynamicValueProviders(),
|
|
4383
|
+
...httpExpressionProviders(),
|
|
4384
|
+
...asyncExpressionProviders(),
|
|
4385
|
+
...propertyDerivationProviders(),
|
|
4386
|
+
...derivationProviders(),
|
|
4387
|
+
];
|
|
4291
4388
|
}
|
|
4292
4389
|
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
function resolveFieldInstance(rootForm, fieldPath) {
|
|
4299
|
-
const parts = fieldPath.split('.');
|
|
4300
|
-
let current = rootForm;
|
|
4301
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
4302
|
-
const next = current[parts[i]];
|
|
4303
|
-
if (!next)
|
|
4304
|
-
return undefined;
|
|
4305
|
-
current = next;
|
|
4306
|
-
}
|
|
4307
|
-
const fieldAccessor = current[parts[parts.length - 1]];
|
|
4308
|
-
if (!fieldAccessor)
|
|
4309
|
-
return undefined;
|
|
4310
|
-
return untracked(fieldAccessor);
|
|
4311
|
-
}
|
|
4312
|
-
function readFieldDirty(rootForm, fieldPath) {
|
|
4313
|
-
const fieldInstance = resolveFieldInstance(rootForm, fieldPath);
|
|
4314
|
-
if (!fieldInstance)
|
|
4315
|
-
return undefined;
|
|
4316
|
-
return untracked(fieldInstance.dirty);
|
|
4317
|
-
}
|
|
4318
|
-
function resetFieldState(rootForm, fieldPath) {
|
|
4319
|
-
const fieldInstance = resolveFieldInstance(rootForm, fieldPath);
|
|
4320
|
-
if (!fieldInstance)
|
|
4321
|
-
return;
|
|
4322
|
-
fieldInstance.reset();
|
|
4323
|
-
}
|
|
4324
|
-
function applyValueToForm(targetPath, value, rootForm, logger, warningTracker) {
|
|
4325
|
-
if (!targetPath.includes('.')) {
|
|
4326
|
-
return setFieldValue(rootForm, targetPath, value, logger, warningTracker);
|
|
4327
|
-
}
|
|
4328
|
-
const parts = targetPath.split('.');
|
|
4329
|
-
let current = rootForm;
|
|
4330
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
4331
|
-
const part = parts[i];
|
|
4332
|
-
if (part === '$') {
|
|
4333
|
-
return false;
|
|
4334
|
-
}
|
|
4335
|
-
const next = current[part];
|
|
4336
|
-
if (!next) {
|
|
4337
|
-
warnMissingField(targetPath, logger, warningTracker);
|
|
4338
|
-
return false; }
|
|
4339
|
-
current = next;
|
|
4340
|
-
}
|
|
4341
|
-
const finalPart = parts[parts.length - 1];
|
|
4342
|
-
return setFieldValue(current, finalPart, value, logger, warningTracker);
|
|
4343
|
-
}
|
|
4344
|
-
function setFieldValue(parent, fieldKey, value, logger, warningTracker) {
|
|
4345
|
-
const fieldAccessor = parent[fieldKey];
|
|
4346
|
-
if (!fieldAccessor) {
|
|
4347
|
-
warnMissingField(fieldKey, logger, warningTracker);
|
|
4348
|
-
return false;
|
|
4349
|
-
}
|
|
4350
|
-
const fieldInstance = untracked(fieldAccessor);
|
|
4351
|
-
if (!fieldInstance || typeof fieldInstance !== 'object') {
|
|
4352
|
-
warnMissingField(fieldKey, logger, warningTracker);
|
|
4353
|
-
return false;
|
|
4354
|
-
}
|
|
4355
|
-
const valueSignal = fieldInstance.value;
|
|
4356
|
-
if (isWritableSignal(valueSignal)) {
|
|
4357
|
-
valueSignal.set(value);
|
|
4358
|
-
return true;
|
|
4390
|
+
class EventDispatcher {
|
|
4391
|
+
bus;
|
|
4392
|
+
dispatch(event) {
|
|
4393
|
+
this.bus?.emitInstance(event);
|
|
4359
4394
|
}
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
return false;
|
|
4395
|
+
connect(bus) {
|
|
4396
|
+
this.bus = bus;
|
|
4363
4397
|
}
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
if (warningTracker) {
|
|
4367
|
-
if (warningTracker.warnedKeys.has(fieldKey)) {
|
|
4368
|
-
return;
|
|
4369
|
-
}
|
|
4370
|
-
warningTracker.warnedKeys.add(fieldKey);
|
|
4398
|
+
disconnect() {
|
|
4399
|
+
this.bus = undefined;
|
|
4371
4400
|
}
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
`This warning is shown once per field.`);
|
|
4401
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: EventDispatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4402
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: EventDispatcher });
|
|
4375
4403
|
}
|
|
4404
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: EventDispatcher, decorators: [{
|
|
4405
|
+
type: Injectable
|
|
4406
|
+
}] });
|
|
4376
4407
|
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
let appliedCount = 0;
|
|
4384
|
-
let skippedCount = 0;
|
|
4385
|
-
let errorCount = 0;
|
|
4386
|
-
let warnCount = 0;
|
|
4387
|
-
const entriesToProcess = changedFields ? getEntriesForChangedFields$1(collection.entries, changedFields) : collection.entries;
|
|
4388
|
-
derivationLogger.cycleStart('onChange', entriesToProcess.length);
|
|
4389
|
-
let hasChanges = true;
|
|
4390
|
-
while (hasChanges && chainContext.iteration < maxIterations) {
|
|
4391
|
-
chainContext.iteration++;
|
|
4392
|
-
hasChanges = false;
|
|
4393
|
-
derivationLogger.iteration(chainContext.iteration);
|
|
4394
|
-
for (const entry of entriesToProcess) {
|
|
4395
|
-
const result = tryApplyDerivation(entry, context, chainContext);
|
|
4396
|
-
if (result.applied) {
|
|
4397
|
-
appliedCount++;
|
|
4398
|
-
hasChanges = true;
|
|
4399
|
-
}
|
|
4400
|
-
else if (result.error) {
|
|
4401
|
-
errorCount++;
|
|
4402
|
-
}
|
|
4403
|
-
else if (result.warning) {
|
|
4404
|
-
warnCount++;
|
|
4405
|
-
skippedCount++;
|
|
4406
|
-
}
|
|
4407
|
-
else {
|
|
4408
|
-
skippedCount++;
|
|
4409
|
-
}
|
|
4410
|
-
}
|
|
4411
|
-
}
|
|
4412
|
-
const processingResult = {
|
|
4413
|
-
appliedCount,
|
|
4414
|
-
skippedCount,
|
|
4415
|
-
errorCount,
|
|
4416
|
-
warnCount,
|
|
4417
|
-
iterations: chainContext.iteration,
|
|
4418
|
-
maxIterationsReached: chainContext.iteration >= maxIterations,
|
|
4419
|
-
};
|
|
4420
|
-
if (processingResult.maxIterationsReached) {
|
|
4421
|
-
derivationLogger.maxIterationsReached(processingResult, 'onChange');
|
|
4422
|
-
}
|
|
4423
|
-
derivationLogger.summary(processingResult, 'onChange');
|
|
4424
|
-
return processingResult;
|
|
4425
|
-
}
|
|
4426
|
-
function getEntriesForChangedFields$1(entries, changedFields) {
|
|
4427
|
-
return entries.filter((entry) => {
|
|
4428
|
-
if (entry.dependsOn.length === 0 || entry.dependsOn.includes('*')) {
|
|
4429
|
-
return true;
|
|
4430
|
-
}
|
|
4431
|
-
if (entry.dependsOn.some((dep) => changedFields.has(dep))) {
|
|
4432
|
-
return true;
|
|
4433
|
-
}
|
|
4434
|
-
for (const changed of changedFields) {
|
|
4435
|
-
if (entry.fieldKey.startsWith(changed + '.'))
|
|
4436
|
-
return true;
|
|
4437
|
-
if (entry.dependsOn.some((dep) => dep.startsWith(changed + '.')))
|
|
4438
|
-
return true;
|
|
4439
|
-
}
|
|
4440
|
-
return false;
|
|
4441
|
-
});
|
|
4408
|
+
class DfTemplate {
|
|
4409
|
+
dfTemplate = input.required(/* @ts-ignore */
|
|
4410
|
+
...(ngDevMode ? [{ debugName: "dfTemplate" }] : /* istanbul ignore next */ []));
|
|
4411
|
+
templateRef = inject(TemplateRef);
|
|
4412
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
4413
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: DfTemplate, isStandalone: true, selector: "ng-template[dfTemplate]", inputs: { dfTemplate: { classPropertyName: "dfTemplate", publicName: "dfTemplate", isSignal: true, isRequired: true, transformFunction: null } }, exportAs: ["dfTemplate"], ngImport: i0 });
|
|
4442
4414
|
}
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
result: 'skipped',
|
|
4457
|
-
skipReason: 'already-applied',
|
|
4458
|
-
});
|
|
4459
|
-
return { applied: false, fieldKey: entry.fieldKey };
|
|
4460
|
-
}
|
|
4461
|
-
if (shouldSkipForUserOverride(entry, entry.fieldKey, context, chainContext)) {
|
|
4462
|
-
derivationLogger.evaluation({
|
|
4463
|
-
debugName: entry.debugName,
|
|
4464
|
-
fieldKey: entry.fieldKey,
|
|
4465
|
-
result: 'skipped',
|
|
4466
|
-
skipReason: 'user-override',
|
|
4467
|
-
});
|
|
4468
|
-
return { applied: false, fieldKey: entry.fieldKey };
|
|
4469
|
-
}
|
|
4470
|
-
const formValue = untracked(() => context.formValue());
|
|
4471
|
-
const evalContext = createEvaluationContext$1(entry, formValue, context, chainContext);
|
|
4472
|
-
if (!evaluateDerivationCondition(entry.condition, evalContext)) {
|
|
4473
|
-
derivationLogger.evaluation({
|
|
4474
|
-
debugName: entry.debugName,
|
|
4475
|
-
fieldKey: entry.fieldKey,
|
|
4476
|
-
result: 'skipped',
|
|
4477
|
-
skipReason: 'condition-false',
|
|
4478
|
-
});
|
|
4479
|
-
return { applied: false, fieldKey: entry.fieldKey };
|
|
4480
|
-
}
|
|
4481
|
-
let newValue;
|
|
4482
|
-
try {
|
|
4483
|
-
newValue = computeDerivedValue(entry, evalContext, context);
|
|
4484
|
-
}
|
|
4485
|
-
catch (error) {
|
|
4486
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4487
|
-
context.logger.error(formatDerivationError(entry, 'compute', errorMessage));
|
|
4488
|
-
derivationLogger.evaluation({
|
|
4489
|
-
debugName: entry.debugName,
|
|
4490
|
-
fieldKey: entry.fieldKey,
|
|
4491
|
-
result: 'error',
|
|
4492
|
-
error: errorMessage,
|
|
4493
|
-
});
|
|
4494
|
-
return {
|
|
4495
|
-
applied: false,
|
|
4496
|
-
fieldKey: entry.fieldKey,
|
|
4497
|
-
error: errorMessage,
|
|
4498
|
-
};
|
|
4499
|
-
}
|
|
4500
|
-
const currentValue = getNestedValue(formValue, entry.fieldKey);
|
|
4501
|
-
if (isEqual(currentValue, newValue)) {
|
|
4502
|
-
derivationLogger.evaluation({
|
|
4503
|
-
debugName: entry.debugName,
|
|
4504
|
-
fieldKey: entry.fieldKey,
|
|
4505
|
-
result: 'skipped',
|
|
4506
|
-
skipReason: 'value-unchanged',
|
|
4507
|
-
});
|
|
4508
|
-
return { applied: false, fieldKey: entry.fieldKey };
|
|
4415
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfTemplate, decorators: [{
|
|
4416
|
+
type: Directive,
|
|
4417
|
+
args: [{
|
|
4418
|
+
selector: 'ng-template[dfTemplate]',
|
|
4419
|
+
exportAs: 'dfTemplate',
|
|
4420
|
+
}]
|
|
4421
|
+
}], propDecorators: { dfTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "dfTemplate", required: true }] }] } });
|
|
4422
|
+
class DfFieldTemplateRegistry {
|
|
4423
|
+
_map = signal(new Map(), /* @ts-ignore */
|
|
4424
|
+
...(ngDevMode ? [{ debugName: "_map" }] : /* istanbul ignore next */ []));
|
|
4425
|
+
map = this._map.asReadonly();
|
|
4426
|
+
set(value) {
|
|
4427
|
+
this._map.set(value);
|
|
4509
4428
|
}
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
if (wasApplied) {
|
|
4513
|
-
chainContext.appliedDerivations.add(derivationKey);
|
|
4514
|
-
derivationLogger.evaluation({
|
|
4515
|
-
debugName: entry.debugName,
|
|
4516
|
-
fieldKey: entry.fieldKey,
|
|
4517
|
-
result: 'applied',
|
|
4518
|
-
previousValue: currentValue,
|
|
4519
|
-
newValue,
|
|
4520
|
-
});
|
|
4521
|
-
return { applied: true, fieldKey: entry.fieldKey, newValue };
|
|
4522
|
-
}
|
|
4523
|
-
else {
|
|
4524
|
-
derivationLogger.evaluation({
|
|
4525
|
-
debugName: entry.debugName,
|
|
4526
|
-
fieldKey: entry.fieldKey,
|
|
4527
|
-
result: 'skipped',
|
|
4528
|
-
skipReason: 'target-not-found',
|
|
4529
|
-
});
|
|
4530
|
-
return {
|
|
4531
|
-
applied: false,
|
|
4532
|
-
fieldKey: entry.fieldKey,
|
|
4533
|
-
warning: `Field '${entry.fieldKey}' not found`,
|
|
4534
|
-
};
|
|
4535
|
-
}
|
|
4536
|
-
}
|
|
4537
|
-
catch (error) {
|
|
4538
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4539
|
-
context.logger.error(formatDerivationError(entry, 'apply', errorMessage));
|
|
4540
|
-
derivationLogger.evaluation({
|
|
4541
|
-
debugName: entry.debugName,
|
|
4542
|
-
fieldKey: entry.fieldKey,
|
|
4543
|
-
result: 'error',
|
|
4544
|
-
error: errorMessage,
|
|
4545
|
-
});
|
|
4546
|
-
return {
|
|
4547
|
-
applied: false,
|
|
4548
|
-
fieldKey: entry.fieldKey,
|
|
4549
|
-
error: errorMessage,
|
|
4550
|
-
};
|
|
4551
|
-
}
|
|
4552
|
-
}
|
|
4553
|
-
function tryApplyArrayDerivation(entry, context, chainContext) {
|
|
4554
|
-
const formValue = untracked(() => context.formValue());
|
|
4555
|
-
const pathInfo = parseArrayPath(entry.fieldKey);
|
|
4556
|
-
if (!pathInfo.isArrayPath) {
|
|
4557
|
-
return { applied: false, fieldKey: entry.fieldKey, error: 'Invalid array derivation path' };
|
|
4558
|
-
}
|
|
4559
|
-
const { arrayPath } = pathInfo;
|
|
4560
|
-
const arrayValue = getNestedValue(formValue, arrayPath);
|
|
4561
|
-
if (!Array.isArray(arrayValue)) {
|
|
4562
|
-
return { applied: false, fieldKey: entry.fieldKey };
|
|
4563
|
-
}
|
|
4564
|
-
let appliedAny = false;
|
|
4565
|
-
for (let i = 0; i < arrayValue.length; i++) {
|
|
4566
|
-
const resolvedPath = resolveArrayPath(entry.fieldKey, i);
|
|
4567
|
-
const derivationKey = createDerivationKey(resolvedPath);
|
|
4568
|
-
if (chainContext.appliedDerivations.has(derivationKey)) {
|
|
4569
|
-
continue;
|
|
4570
|
-
}
|
|
4571
|
-
if (shouldSkipForUserOverride(entry, resolvedPath, context, chainContext)) {
|
|
4572
|
-
context.derivationLogger.evaluation({
|
|
4573
|
-
debugName: entry.debugName,
|
|
4574
|
-
fieldKey: resolvedPath,
|
|
4575
|
-
result: 'skipped',
|
|
4576
|
-
skipReason: 'user-override',
|
|
4577
|
-
});
|
|
4578
|
-
continue;
|
|
4579
|
-
}
|
|
4580
|
-
const arrayItem = arrayValue[i];
|
|
4581
|
-
const evalContext = createArrayItemEvaluationContext$1(entry, arrayItem, formValue, i, arrayPath, context, chainContext);
|
|
4582
|
-
if (!evaluateDerivationCondition(entry.condition, evalContext)) {
|
|
4583
|
-
continue;
|
|
4584
|
-
}
|
|
4585
|
-
let newValue;
|
|
4586
|
-
try {
|
|
4587
|
-
newValue = computeDerivedValue(entry, evalContext, context);
|
|
4588
|
-
}
|
|
4589
|
-
catch (error) {
|
|
4590
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4591
|
-
context.logger.error(formatDerivationError(entry, 'compute', `[index ${i}] ${errorMessage}`));
|
|
4592
|
-
continue;
|
|
4593
|
-
}
|
|
4594
|
-
const currentValue = getNestedValue(formValue, resolvedPath);
|
|
4595
|
-
if (isEqual(currentValue, newValue)) {
|
|
4596
|
-
continue;
|
|
4597
|
-
}
|
|
4598
|
-
try {
|
|
4599
|
-
applyValueToForm(resolvedPath, newValue, context.rootForm, context.logger, context.warningTracker);
|
|
4600
|
-
chainContext.appliedDerivations.add(derivationKey);
|
|
4601
|
-
appliedAny = true;
|
|
4602
|
-
}
|
|
4603
|
-
catch (error) {
|
|
4604
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4605
|
-
context.logger.error(formatDerivationError(entry, 'apply', `[index ${i}] ${errorMessage}`));
|
|
4606
|
-
}
|
|
4607
|
-
}
|
|
4608
|
-
return { applied: appliedAny, fieldKey: entry.fieldKey };
|
|
4609
|
-
}
|
|
4610
|
-
function createEvaluationContext$1(entry, formValue, context, chainContext) {
|
|
4611
|
-
const fieldValue = getNestedValue(formValue, entry.fieldKey);
|
|
4612
|
-
const fieldAccessor = context.rootForm[entry.fieldKey];
|
|
4613
|
-
const fieldState = fieldAccessor ? readFieldStateInfo(fieldAccessor, false) : undefined;
|
|
4614
|
-
const parentPath = getParentPathInScope(entry.fieldKey);
|
|
4615
|
-
const groupValue = parentPath ? getNestedValue(formValue, parentPath) : undefined;
|
|
4616
|
-
return {
|
|
4617
|
-
fieldValue,
|
|
4618
|
-
formValue,
|
|
4619
|
-
fieldPath: entry.fieldKey,
|
|
4620
|
-
groupValue,
|
|
4621
|
-
customFunctions: context.customFunctions,
|
|
4622
|
-
externalData: context.externalData,
|
|
4623
|
-
logger: context.logger,
|
|
4624
|
-
fieldState,
|
|
4625
|
-
formFieldState: chainContext.formFieldState,
|
|
4626
|
-
};
|
|
4627
|
-
}
|
|
4628
|
-
function createArrayItemEvaluationContext$1(entry, arrayItem, rootFormValue, itemIndex, arrayPath, context, chainContext) {
|
|
4629
|
-
const pathInfo = parseArrayPath(entry.fieldKey);
|
|
4630
|
-
let fieldState;
|
|
4631
|
-
if (pathInfo.isArrayPath && pathInfo.relativePath) {
|
|
4632
|
-
const rootFormRecord = context.rootForm;
|
|
4633
|
-
const arrayAccessor = rootFormRecord[arrayPath];
|
|
4634
|
-
if (arrayAccessor) {
|
|
4635
|
-
const arrayItems = arrayAccessor;
|
|
4636
|
-
const itemAccessor = arrayItems[String(itemIndex)];
|
|
4637
|
-
if (itemAccessor) {
|
|
4638
|
-
const itemFields = itemAccessor;
|
|
4639
|
-
const fieldAccessor = itemFields[pathInfo.relativePath];
|
|
4640
|
-
if (fieldAccessor) {
|
|
4641
|
-
fieldState = readFieldStateInfo(fieldAccessor, false);
|
|
4642
|
-
}
|
|
4643
|
-
}
|
|
4644
|
-
}
|
|
4645
|
-
}
|
|
4646
|
-
const { relativePath, groupValue, fieldValue } = resolveArrayItemScope(pathInfo, arrayItem);
|
|
4647
|
-
return {
|
|
4648
|
-
fieldValue,
|
|
4649
|
-
formValue: arrayItem,
|
|
4650
|
-
fieldPath: relativePath ? `${arrayPath}.${itemIndex}.${relativePath}` : `${arrayPath}.${itemIndex}`,
|
|
4651
|
-
groupValue,
|
|
4652
|
-
customFunctions: context.customFunctions,
|
|
4653
|
-
externalData: context.externalData,
|
|
4654
|
-
logger: context.logger,
|
|
4655
|
-
rootFormValue,
|
|
4656
|
-
arrayIndex: itemIndex,
|
|
4657
|
-
arrayPath,
|
|
4658
|
-
fieldState,
|
|
4659
|
-
formFieldState: chainContext.formFieldState,
|
|
4660
|
-
};
|
|
4661
|
-
}
|
|
4662
|
-
function evaluateDerivationCondition(condition, context) {
|
|
4663
|
-
if (typeof condition === 'boolean') {
|
|
4664
|
-
return condition;
|
|
4665
|
-
}
|
|
4666
|
-
return evaluateCondition(condition, context);
|
|
4667
|
-
}
|
|
4668
|
-
function computeDerivedValue(entry, evalContext, applicatorContext) {
|
|
4669
|
-
return computeValueFromEntry(entry, evalContext, {
|
|
4670
|
-
derivationFunctions: applicatorContext.derivationFunctions,
|
|
4671
|
-
subject: entry.fieldKey,
|
|
4672
|
-
});
|
|
4673
|
-
}
|
|
4674
|
-
const ERROR_PREFIX$1 = '[Derivation]';
|
|
4675
|
-
function formatDerivationError(entry, phase, message) {
|
|
4676
|
-
const source = entry.expression
|
|
4677
|
-
? `expression: "${entry.expression.substring(0, 50)}${entry.expression.length > 50 ? '...' : ''}"`
|
|
4678
|
-
: entry.functionName
|
|
4679
|
-
? `function: "${entry.functionName}"`
|
|
4680
|
-
: entry.fn
|
|
4681
|
-
? 'inline fn'
|
|
4682
|
-
: entry.value !== undefined
|
|
4683
|
-
? 'static value'
|
|
4684
|
-
: 'unknown source';
|
|
4685
|
-
return `${ERROR_PREFIX$1} Failed to ${phase} derivation (field: ${entry.fieldKey}, ${source}): ${message}`;
|
|
4686
|
-
}
|
|
4687
|
-
function hasDependencyChanged(entry, changedFields) {
|
|
4688
|
-
return entry.dependsOn.some((dep) => dep === '*' || changedFields.has(dep));
|
|
4689
|
-
}
|
|
4690
|
-
function shouldSkipForUserOverride(entry, resolvedFieldKey, context, chainContext) {
|
|
4691
|
-
if (!entry.stopOnUserOverride)
|
|
4692
|
-
return false;
|
|
4693
|
-
const isDirty = readFieldDirty(context.rootForm, resolvedFieldKey) ?? false;
|
|
4694
|
-
if (!isDirty)
|
|
4695
|
-
return false;
|
|
4696
|
-
if (entry.reEngageOnDependencyChange && chainContext.changedFields) {
|
|
4697
|
-
if (hasDependencyChanged(entry, chainContext.changedFields)) {
|
|
4698
|
-
resetFieldState(context.rootForm, resolvedFieldKey);
|
|
4699
|
-
return false;
|
|
4700
|
-
}
|
|
4701
|
-
}
|
|
4702
|
-
return true;
|
|
4703
|
-
}
|
|
4704
|
-
function applyDerivationsForTrigger(collection, trigger, context, changedFields) {
|
|
4705
|
-
const filteredEntries = collection.entries.filter((entry) => {
|
|
4706
|
-
if (entry.http || entry.asyncFunctionName || entry.asyncFn)
|
|
4707
|
-
return false;
|
|
4708
|
-
if (trigger === 'onChange') {
|
|
4709
|
-
return !entry.trigger || entry.trigger === 'onChange';
|
|
4710
|
-
}
|
|
4711
|
-
return entry.trigger === trigger;
|
|
4712
|
-
});
|
|
4713
|
-
const filteredCollection = {
|
|
4714
|
-
entries: filteredEntries,
|
|
4715
|
-
};
|
|
4716
|
-
return applyDerivations(filteredCollection, context, changedFields);
|
|
4717
|
-
}
|
|
4718
|
-
function getDebouncedDerivationEntries(collection) {
|
|
4719
|
-
return collection.entries.filter((entry) => entry.trigger === 'debounced');
|
|
4720
|
-
}
|
|
4721
|
-
|
|
4722
|
-
function topologicalSort(entries) {
|
|
4723
|
-
if (entries.length <= 1) {
|
|
4724
|
-
return [...entries];
|
|
4725
|
-
}
|
|
4726
|
-
const graph = new Map();
|
|
4727
|
-
const inDegree = new Map();
|
|
4728
|
-
for (const entry of entries) {
|
|
4729
|
-
graph.set(entry, new Set());
|
|
4730
|
-
inDegree.set(entry, 0);
|
|
4731
|
-
}
|
|
4732
|
-
const derivationsByField = new Map();
|
|
4733
|
-
for (const entry of entries) {
|
|
4734
|
-
const fieldEntries = derivationsByField.get(entry.fieldKey) ?? [];
|
|
4735
|
-
fieldEntries.push(entry);
|
|
4736
|
-
derivationsByField.set(entry.fieldKey, fieldEntries);
|
|
4737
|
-
if (entry.fieldKey.includes('.$.')) {
|
|
4738
|
-
const relativePath = entry.fieldKey.split('.$.')[1];
|
|
4739
|
-
if (relativePath) {
|
|
4740
|
-
const relativeEntries = derivationsByField.get(relativePath) ?? [];
|
|
4741
|
-
relativeEntries.push(entry);
|
|
4742
|
-
derivationsByField.set(relativePath, relativeEntries);
|
|
4743
|
-
}
|
|
4744
|
-
}
|
|
4745
|
-
}
|
|
4746
|
-
const wildcardDependents = [];
|
|
4747
|
-
for (const entry of entries) {
|
|
4748
|
-
if (entry.dependsOn.includes('*')) {
|
|
4749
|
-
wildcardDependents.push(entry);
|
|
4750
|
-
}
|
|
4751
|
-
}
|
|
4752
|
-
for (const entryB of entries) {
|
|
4753
|
-
for (const dep of entryB.dependsOn) {
|
|
4754
|
-
if (dep === '*')
|
|
4755
|
-
continue;
|
|
4756
|
-
const producers = derivationsByField.get(dep);
|
|
4757
|
-
if (producers) {
|
|
4758
|
-
for (const entryA of producers) {
|
|
4759
|
-
const entryAEdges = graph.get(entryA);
|
|
4760
|
-
if (entryA !== entryB && entryAEdges && !entryAEdges.has(entryB)) {
|
|
4761
|
-
entryAEdges.add(entryB);
|
|
4762
|
-
inDegree.set(entryB, (inDegree.get(entryB) ?? 0) + 1);
|
|
4763
|
-
}
|
|
4764
|
-
}
|
|
4765
|
-
}
|
|
4766
|
-
}
|
|
4767
|
-
}
|
|
4768
|
-
for (const entryB of wildcardDependents) {
|
|
4769
|
-
for (const entryA of entries) {
|
|
4770
|
-
const entryAEdges = graph.get(entryA);
|
|
4771
|
-
if (entryA !== entryB && entryAEdges && !entryAEdges.has(entryB)) {
|
|
4772
|
-
entryAEdges.add(entryB);
|
|
4773
|
-
inDegree.set(entryB, (inDegree.get(entryB) ?? 0) + 1);
|
|
4774
|
-
}
|
|
4775
|
-
}
|
|
4776
|
-
}
|
|
4777
|
-
const queue = [];
|
|
4778
|
-
const sorted = [];
|
|
4779
|
-
for (const entry of entries) {
|
|
4780
|
-
if (inDegree.get(entry) === 0) {
|
|
4781
|
-
queue.push(entry);
|
|
4782
|
-
}
|
|
4783
|
-
}
|
|
4784
|
-
while (queue.length > 0) {
|
|
4785
|
-
const current = queue.shift();
|
|
4786
|
-
if (!current)
|
|
4787
|
-
break; sorted.push(current);
|
|
4788
|
-
const neighbors = graph.get(current) ?? new Set();
|
|
4789
|
-
for (const neighbor of neighbors) {
|
|
4790
|
-
const newDegree = (inDegree.get(neighbor) ?? 0) - 1;
|
|
4791
|
-
inDegree.set(neighbor, newDegree);
|
|
4792
|
-
if (newDegree === 0) {
|
|
4793
|
-
queue.push(neighbor);
|
|
4794
|
-
}
|
|
4795
|
-
}
|
|
4796
|
-
}
|
|
4797
|
-
if (sorted.length !== entries.length) {
|
|
4798
|
-
const sortedSet = new Set(sorted);
|
|
4799
|
-
for (const entry of entries) {
|
|
4800
|
-
if (!sortedSet.has(entry)) {
|
|
4801
|
-
sorted.push(entry);
|
|
4802
|
-
}
|
|
4803
|
-
}
|
|
4804
|
-
}
|
|
4805
|
-
return sorted;
|
|
4806
|
-
}
|
|
4807
|
-
|
|
4808
|
-
function extractDependenciesFromConfig(config) {
|
|
4809
|
-
const deps = new Set();
|
|
4810
|
-
if (config.dependsOn && config.dependsOn.length > 0) {
|
|
4811
|
-
config.dependsOn.forEach((dep) => deps.add(dep));
|
|
4812
|
-
}
|
|
4813
|
-
else {
|
|
4814
|
-
if (config.expression) {
|
|
4815
|
-
extractStringDependencies(config.expression).forEach((dep) => deps.add(dep));
|
|
4816
|
-
}
|
|
4817
|
-
if (config.functionName || config.fn) {
|
|
4818
|
-
deps.add('*');
|
|
4819
|
-
}
|
|
4820
|
-
}
|
|
4821
|
-
if (config.condition && config.condition !== true) {
|
|
4822
|
-
extractExpressionDependencies(config.condition).forEach((dep) => deps.add(dep));
|
|
4823
|
-
}
|
|
4824
|
-
return Array.from(deps);
|
|
4825
|
-
}
|
|
4826
|
-
|
|
4827
|
-
function extractInlineFn(config) {
|
|
4828
|
-
return config.fn;
|
|
4829
|
-
}
|
|
4830
|
-
function extractInlineAsyncFn(config) {
|
|
4831
|
-
return config.asyncFn;
|
|
4832
|
-
}
|
|
4833
|
-
|
|
4834
|
-
function traverseFieldsWithContext(fields, context, visitor, hooks) {
|
|
4835
|
-
for (const field of fields) {
|
|
4836
|
-
visitor(field, context);
|
|
4837
|
-
if (!hasChildFields(field))
|
|
4838
|
-
continue;
|
|
4839
|
-
if (field.type === 'array') {
|
|
4840
|
-
const childContext = mergeContext(context, hooks?.onArrayChild?.(context, field));
|
|
4841
|
-
const arrayChildren = collectArrayChildren(field);
|
|
4842
|
-
traverseFieldsWithContext(arrayChildren, childContext, visitor, hooks);
|
|
4843
|
-
continue;
|
|
4844
|
-
}
|
|
4845
|
-
const overrides = field.type === 'group' ? hooks?.onGroupChild?.(context, field) : hooks?.onLayoutChild?.(context, field);
|
|
4846
|
-
const childContext = mergeContext(context, overrides);
|
|
4847
|
-
traverseFieldsWithContext(normalizeFieldsArray(field.fields), childContext, visitor, hooks);
|
|
4848
|
-
}
|
|
4849
|
-
}
|
|
4850
|
-
function collectArrayChildren(field) {
|
|
4851
|
-
let arrayItems = normalizeFieldsArray(field.fields);
|
|
4852
|
-
if (arrayItems.length === 0) {
|
|
4853
|
-
const metadataTemplate = getNormalizedArrayMetadata(field)?.template;
|
|
4854
|
-
if (metadataTemplate) {
|
|
4855
|
-
arrayItems = [
|
|
4856
|
-
Array.isArray(metadataTemplate) ? [...metadataTemplate] : metadataTemplate,
|
|
4857
|
-
];
|
|
4858
|
-
}
|
|
4859
|
-
}
|
|
4860
|
-
const flattened = [];
|
|
4861
|
-
for (const item of arrayItems) {
|
|
4862
|
-
if (Array.isArray(item)) {
|
|
4863
|
-
flattened.push(...item);
|
|
4864
|
-
}
|
|
4865
|
-
else {
|
|
4866
|
-
flattened.push(item);
|
|
4867
|
-
}
|
|
4868
|
-
}
|
|
4869
|
-
return flattened;
|
|
4870
|
-
}
|
|
4871
|
-
function mergeContext(base, overrides) {
|
|
4872
|
-
return overrides ? { ...base, ...overrides } : { ...base };
|
|
4873
|
-
}
|
|
4874
|
-
|
|
4875
|
-
const SELF_DEPENDENCY_TOKEN = '$self';
|
|
4876
|
-
const GROUP_DEPENDENCY_TOKEN = '$group';
|
|
4877
|
-
function buildEffectiveFieldKey(fieldKey, context) {
|
|
4878
|
-
if (context.arrayPath && context.groupPath) {
|
|
4879
|
-
return `${context.arrayPath}.$.${context.groupPath}.${fieldKey}`;
|
|
4880
|
-
}
|
|
4881
|
-
if (context.arrayPath) {
|
|
4882
|
-
return `${context.arrayPath}.$.${fieldKey}`;
|
|
4883
|
-
}
|
|
4884
|
-
if (context.groupPath) {
|
|
4885
|
-
return `${context.groupPath}.${fieldKey}`;
|
|
4886
|
-
}
|
|
4887
|
-
return fieldKey;
|
|
4888
|
-
}
|
|
4889
|
-
function buildNearestGroupPath(context) {
|
|
4890
|
-
if (context.arrayPath && context.groupPath) {
|
|
4891
|
-
return `${context.arrayPath}.$.${context.groupPath}`;
|
|
4892
|
-
}
|
|
4893
|
-
return context.arrayPath ?? context.groupPath;
|
|
4894
|
-
}
|
|
4895
|
-
function resolveTokenDependencies(deps, effectiveFieldKey, context) {
|
|
4896
|
-
return deps.map((dep) => {
|
|
4897
|
-
if (dep === SELF_DEPENDENCY_TOKEN) {
|
|
4898
|
-
return effectiveFieldKey;
|
|
4899
|
-
}
|
|
4900
|
-
if (dep.startsWith(`${SELF_DEPENDENCY_TOKEN}.`)) {
|
|
4901
|
-
return effectiveFieldKey + dep.slice(SELF_DEPENDENCY_TOKEN.length);
|
|
4902
|
-
}
|
|
4903
|
-
if (dep === GROUP_DEPENDENCY_TOKEN || dep.startsWith(`${GROUP_DEPENDENCY_TOKEN}.`)) {
|
|
4904
|
-
const groupPath = buildNearestGroupPath(context);
|
|
4905
|
-
if (!groupPath) {
|
|
4906
|
-
throw new DynamicFormError(`Derivation for '${effectiveFieldKey}' uses '${dep}' but the field has no parent group or array. ` +
|
|
4907
|
-
`'${GROUP_DEPENDENCY_TOKEN}' is only valid for fields nested inside a group or array container.`);
|
|
4908
|
-
}
|
|
4909
|
-
if (dep === GROUP_DEPENDENCY_TOKEN)
|
|
4910
|
-
return groupPath;
|
|
4911
|
-
return groupPath + dep.slice(GROUP_DEPENDENCY_TOKEN.length);
|
|
4912
|
-
}
|
|
4913
|
-
return dep;
|
|
4914
|
-
});
|
|
4915
|
-
}
|
|
4916
|
-
const CONTEXT_TRAVERSAL_OPTIONS = {
|
|
4917
|
-
onArrayChild: (_parent, field) => ({ arrayPath: field.key, groupPath: undefined }),
|
|
4918
|
-
onGroupChild: (parent, field) => {
|
|
4919
|
-
if (!field.key)
|
|
4920
|
-
return {};
|
|
4921
|
-
return { groupPath: parent.groupPath ? `${parent.groupPath}.${field.key}` : field.key };
|
|
4922
|
-
},
|
|
4923
|
-
};
|
|
4924
|
-
|
|
4925
|
-
function collectDerivations(fields) {
|
|
4926
|
-
const entries = [];
|
|
4927
|
-
traverseFieldsWithContext(fields, {}, (field, context) => collectValueEntriesFromField(field, entries, context), CONTEXT_TRAVERSAL_OPTIONS);
|
|
4928
|
-
return { entries: topologicalSort(entries) };
|
|
4929
|
-
}
|
|
4930
|
-
function collectAllDerivations(fields) {
|
|
4931
|
-
const valueEntries = [];
|
|
4932
|
-
const propertyEntries = [];
|
|
4933
|
-
traverseFieldsWithContext(fields, {}, (field, context) => {
|
|
4934
|
-
collectValueEntriesFromField(field, valueEntries, context);
|
|
4935
|
-
collectPropertyEntriesFromField(field, propertyEntries, context);
|
|
4936
|
-
}, CONTEXT_TRAVERSAL_OPTIONS);
|
|
4937
|
-
return {
|
|
4938
|
-
value: { entries: topologicalSort(valueEntries) },
|
|
4939
|
-
property: { entries: propertyEntries },
|
|
4940
|
-
};
|
|
4941
|
-
}
|
|
4942
|
-
function collectValueEntriesFromField(field, entries, context) {
|
|
4943
|
-
const fieldKey = field.key;
|
|
4944
|
-
if (!fieldKey)
|
|
4945
|
-
return;
|
|
4946
|
-
const validationField = field;
|
|
4947
|
-
if (validationField.derivation) {
|
|
4948
|
-
entries.push(createShorthandEntry(fieldKey, validationField.derivation, context));
|
|
4949
|
-
}
|
|
4950
|
-
if (validationField.logic) {
|
|
4951
|
-
for (const logicConfig of validationField.logic) {
|
|
4952
|
-
if (!isDerivationLogicConfig(logicConfig))
|
|
4953
|
-
continue;
|
|
4954
|
-
if (hasTargetProperty(logicConfig))
|
|
4955
|
-
continue; entries.push(createValueLogicEntry(fieldKey, logicConfig, context));
|
|
4956
|
-
}
|
|
4957
|
-
}
|
|
4958
|
-
}
|
|
4959
|
-
function collectPropertyEntriesFromField(field, entries, context) {
|
|
4960
|
-
const fieldKey = field.key;
|
|
4961
|
-
if (!fieldKey)
|
|
4962
|
-
return;
|
|
4963
|
-
const validationField = field;
|
|
4964
|
-
if (!validationField.logic)
|
|
4965
|
-
return;
|
|
4966
|
-
for (const logicConfig of validationField.logic) {
|
|
4967
|
-
if (!isDerivationLogicConfig(logicConfig))
|
|
4968
|
-
continue;
|
|
4969
|
-
if (!hasTargetProperty(logicConfig))
|
|
4970
|
-
continue;
|
|
4971
|
-
entries.push(createPropertyLogicEntry(fieldKey, logicConfig, context));
|
|
4972
|
-
}
|
|
4973
|
-
}
|
|
4974
|
-
function createShorthandEntry(fieldKey, expression, context) {
|
|
4975
|
-
const effectiveFieldKey = buildEffectiveFieldKey(fieldKey, context);
|
|
4976
|
-
return {
|
|
4977
|
-
fieldKey: effectiveFieldKey,
|
|
4978
|
-
dependsOn: extractStringDependencies(expression),
|
|
4979
|
-
condition: true,
|
|
4980
|
-
expression,
|
|
4981
|
-
trigger: 'onChange',
|
|
4982
|
-
isShorthand: true,
|
|
4983
|
-
};
|
|
4984
|
-
}
|
|
4985
|
-
function createValueLogicEntry(fieldKey, config, context) {
|
|
4986
|
-
const effectiveFieldKey = buildEffectiveFieldKey(fieldKey, context);
|
|
4987
|
-
if (config.source === 'http') {
|
|
4988
|
-
if (config.dependsOn.length === 0) {
|
|
4989
|
-
throw new DynamicFormError(`HTTP derivation for '${effectiveFieldKey}' requires explicit 'dependsOn'. ` +
|
|
4990
|
-
`Wildcard dependencies would trigger HTTP requests on every form change.`);
|
|
4991
|
-
}
|
|
4992
|
-
if (config.dependsOn.includes('*')) {
|
|
4993
|
-
throw new DynamicFormError(`HTTP derivation for '${effectiveFieldKey}' cannot use wildcard ('*') in 'dependsOn'. ` +
|
|
4994
|
-
`Wildcards would trigger HTTP requests on every form change. Specify explicit field dependencies instead.`);
|
|
4995
|
-
}
|
|
4996
|
-
}
|
|
4997
|
-
if (config.source === 'asyncFunction') {
|
|
4998
|
-
if (config.dependsOn.length === 0) {
|
|
4999
|
-
throw new DynamicFormError(`Async derivation for '${effectiveFieldKey}' requires explicit 'dependsOn'. ` +
|
|
5000
|
-
`Wildcard dependencies would trigger async functions on every form change.`);
|
|
5001
|
-
}
|
|
5002
|
-
if (config.dependsOn.includes('*')) {
|
|
5003
|
-
throw new DynamicFormError(`Async derivation for '${effectiveFieldKey}' cannot use wildcard ('*') in 'dependsOn'. ` +
|
|
5004
|
-
`Wildcards would trigger async functions on every form change. Specify explicit field dependencies instead.`);
|
|
5005
|
-
}
|
|
5006
|
-
}
|
|
5007
|
-
const dependsOn = resolveTokenDependencies(extractDependenciesFromConfig(config), effectiveFieldKey, context);
|
|
5008
|
-
const condition = config.condition ?? true;
|
|
5009
|
-
const trigger = config.trigger ?? 'onChange';
|
|
5010
|
-
const debounceMs = trigger === 'debounced' ? config.debounceMs : undefined;
|
|
5011
|
-
return {
|
|
5012
|
-
fieldKey: effectiveFieldKey,
|
|
5013
|
-
dependsOn,
|
|
5014
|
-
condition,
|
|
5015
|
-
value: config.value,
|
|
5016
|
-
expression: config.expression,
|
|
5017
|
-
functionName: config.functionName,
|
|
5018
|
-
fn: extractInlineFn(config),
|
|
5019
|
-
http: config.http,
|
|
5020
|
-
responseExpression: config.responseExpression,
|
|
5021
|
-
asyncFunctionName: config.asyncFunctionName,
|
|
5022
|
-
asyncFn: extractInlineAsyncFn(config),
|
|
5023
|
-
trigger,
|
|
5024
|
-
debounceMs,
|
|
5025
|
-
isShorthand: false,
|
|
5026
|
-
originalConfig: config,
|
|
5027
|
-
debugName: config.debugName,
|
|
5028
|
-
stopOnUserOverride: config.stopOnUserOverride,
|
|
5029
|
-
reEngageOnDependencyChange: config.reEngageOnDependencyChange,
|
|
5030
|
-
};
|
|
5031
|
-
}
|
|
5032
|
-
function createPropertyLogicEntry(fieldKey, config, context) {
|
|
5033
|
-
const effectiveFieldKey = buildEffectiveFieldKey(fieldKey, context);
|
|
5034
|
-
const errorLocation = `'${effectiveFieldKey}.${config.targetProperty}'`;
|
|
5035
|
-
const untypedConfig = config;
|
|
5036
|
-
if (untypedConfig.source === 'http' || untypedConfig.source === 'asyncFunction') {
|
|
5037
|
-
const conflictingSyncSources = [];
|
|
5038
|
-
if (untypedConfig.value !== undefined)
|
|
5039
|
-
conflictingSyncSources.push('value');
|
|
5040
|
-
if (untypedConfig.expression !== undefined)
|
|
5041
|
-
conflictingSyncSources.push('expression');
|
|
5042
|
-
if (untypedConfig.functionName !== undefined)
|
|
5043
|
-
conflictingSyncSources.push('functionName');
|
|
5044
|
-
if (untypedConfig.fn !== undefined)
|
|
5045
|
-
conflictingSyncSources.push('fn');
|
|
5046
|
-
if (conflictingSyncSources.length > 0) {
|
|
5047
|
-
throw new DynamicFormError(`Property derivation for ${errorLocation} sets 'source: ${JSON.stringify(untypedConfig.source)}' alongside ` +
|
|
5048
|
-
`sync value source(s) [${conflictingSyncSources.join(', ')}]. These are mutually exclusive — remove either ` +
|
|
5049
|
-
`the sync fields or the async source.`);
|
|
5050
|
-
}
|
|
5051
|
-
}
|
|
5052
|
-
if (untypedConfig.source === 'http' && (untypedConfig.asyncFunctionName !== undefined || untypedConfig.asyncFn !== undefined)) {
|
|
5053
|
-
throw new DynamicFormError(`Property derivation for ${errorLocation} sets 'source: "http"' alongside an async function — these are mutually exclusive.`);
|
|
5054
|
-
}
|
|
5055
|
-
if (untypedConfig.source === 'asyncFunction' && (untypedConfig.http !== undefined || untypedConfig.responseExpression !== undefined)) {
|
|
5056
|
-
throw new DynamicFormError(`Property derivation for ${errorLocation} sets 'source: "asyncFunction"' alongside HTTP fields — these are mutually exclusive.`);
|
|
5057
|
-
}
|
|
5058
|
-
if (untypedConfig.source === 'http') {
|
|
5059
|
-
if (!untypedConfig.http) {
|
|
5060
|
-
throw new DynamicFormError(`HTTP property derivation for ${errorLocation} requires an 'http' config object.`);
|
|
5061
|
-
}
|
|
5062
|
-
if (typeof untypedConfig.responseExpression !== 'string' || untypedConfig.responseExpression.trim() === '') {
|
|
5063
|
-
throw new DynamicFormError(`HTTP property derivation for ${errorLocation} requires a non-empty 'responseExpression'.`);
|
|
5064
|
-
}
|
|
5065
|
-
if (!Array.isArray(untypedConfig.dependsOn) || untypedConfig.dependsOn.length === 0) {
|
|
5066
|
-
throw new DynamicFormError(`HTTP property derivation for ${errorLocation} requires explicit 'dependsOn'. ` +
|
|
5067
|
-
`Wildcard dependencies would trigger HTTP requests on every form change.`);
|
|
5068
|
-
}
|
|
5069
|
-
if (untypedConfig.dependsOn.includes('*')) {
|
|
5070
|
-
throw new DynamicFormError(`HTTP property derivation for ${errorLocation} cannot use wildcard ('*') in 'dependsOn'. ` +
|
|
5071
|
-
`Wildcards would trigger HTTP requests on every form change. Specify explicit field dependencies instead.`);
|
|
5072
|
-
}
|
|
5073
|
-
}
|
|
5074
|
-
if (untypedConfig.source === 'asyncFunction') {
|
|
5075
|
-
if (!untypedConfig.asyncFunctionName && !untypedConfig.asyncFn) {
|
|
5076
|
-
throw new DynamicFormError(`Async property derivation for ${errorLocation} requires either 'asyncFunctionName' or 'asyncFn'.`);
|
|
5077
|
-
}
|
|
5078
|
-
if (!Array.isArray(untypedConfig.dependsOn) || untypedConfig.dependsOn.length === 0) {
|
|
5079
|
-
throw new DynamicFormError(`Async property derivation for ${errorLocation} requires explicit 'dependsOn'. ` +
|
|
5080
|
-
`Wildcard dependencies would trigger async functions on every form change.`);
|
|
5081
|
-
}
|
|
5082
|
-
if (untypedConfig.dependsOn.includes('*')) {
|
|
5083
|
-
throw new DynamicFormError(`Async property derivation for ${errorLocation} cannot use wildcard ('*') in 'dependsOn'. ` +
|
|
5084
|
-
`Wildcards would trigger async functions on every form change. Specify explicit field dependencies instead.`);
|
|
5085
|
-
}
|
|
5086
|
-
}
|
|
5087
|
-
const dependsOn = resolveTokenDependencies(extractDependenciesFromConfig(config), effectiveFieldKey, context);
|
|
5088
|
-
const condition = config.condition ?? true;
|
|
5089
|
-
const trigger = config.trigger ?? 'onChange';
|
|
5090
|
-
const debounceMs = trigger === 'debounced' ? config.debounceMs : undefined;
|
|
5091
|
-
return {
|
|
5092
|
-
fieldKey: effectiveFieldKey,
|
|
5093
|
-
targetProperty: config.targetProperty,
|
|
5094
|
-
dependsOn,
|
|
5095
|
-
condition,
|
|
5096
|
-
value: config.value,
|
|
5097
|
-
expression: config.expression,
|
|
5098
|
-
functionName: config.functionName,
|
|
5099
|
-
fn: extractInlineFn(config),
|
|
5100
|
-
http: config.http,
|
|
5101
|
-
responseExpression: config.responseExpression,
|
|
5102
|
-
asyncFunctionName: config.asyncFunctionName,
|
|
5103
|
-
asyncFn: extractInlineAsyncFn(config),
|
|
5104
|
-
trigger,
|
|
5105
|
-
debounceMs,
|
|
5106
|
-
debugName: config.debugName,
|
|
5107
|
-
originalConfig: config,
|
|
5108
|
-
};
|
|
5109
|
-
}
|
|
5110
|
-
|
|
5111
|
-
function collectPropertyDerivations(fields) {
|
|
5112
|
-
return collectAllDerivations(fields).property;
|
|
5113
|
-
}
|
|
5114
|
-
|
|
5115
|
-
function detectCycles(collection) {
|
|
5116
|
-
const graph = buildDependencyGraph(collection);
|
|
5117
|
-
if (graph.size === 0) {
|
|
5118
|
-
return { hasCycle: false };
|
|
5119
|
-
}
|
|
5120
|
-
const bidirectionalPairs = detectBidirectionalPairs(collection);
|
|
5121
|
-
const result = detectCyclesWithDFS(graph, bidirectionalPairs);
|
|
5122
|
-
if (bidirectionalPairs.size > 0) {
|
|
5123
|
-
result.bidirectionalPairs = Array.from(bidirectionalPairs);
|
|
5124
|
-
}
|
|
5125
|
-
return result;
|
|
5126
|
-
}
|
|
5127
|
-
function detectBidirectionalPairs(collection) {
|
|
5128
|
-
const pairs = new Set();
|
|
5129
|
-
const dependencyMap = new Map();
|
|
5130
|
-
for (const entry of collection.entries) {
|
|
5131
|
-
const deps = dependencyMap.get(entry.fieldKey) ?? new Set();
|
|
5132
|
-
for (const dep of entry.dependsOn) {
|
|
5133
|
-
if (dep !== '*' && dep !== entry.fieldKey) {
|
|
5134
|
-
deps.add(dep);
|
|
5135
|
-
}
|
|
5136
|
-
}
|
|
5137
|
-
dependencyMap.set(entry.fieldKey, deps);
|
|
5138
|
-
}
|
|
5139
|
-
for (const [fieldA, depsA] of dependencyMap) {
|
|
5140
|
-
for (const fieldB of depsA) {
|
|
5141
|
-
const depsB = dependencyMap.get(fieldB);
|
|
5142
|
-
if (depsB?.has(fieldA)) {
|
|
5143
|
-
const pairKey = fieldA < fieldB ? `${fieldA}↔${fieldB}` : `${fieldB}↔${fieldA}`;
|
|
5144
|
-
pairs.add(pairKey);
|
|
5145
|
-
}
|
|
5146
|
-
}
|
|
5147
|
-
}
|
|
5148
|
-
return pairs;
|
|
5149
|
-
}
|
|
5150
|
-
function isBidirectionalCycle(cyclePath, bidirectionalPairs) {
|
|
5151
|
-
if (cyclePath.length !== 3) {
|
|
5152
|
-
return false;
|
|
5153
|
-
}
|
|
5154
|
-
const [first, second, third] = cyclePath;
|
|
5155
|
-
if (first !== third) {
|
|
5156
|
-
return false;
|
|
5157
|
-
}
|
|
5158
|
-
const pairKey = first < second ? `${first}↔${second}` : `${second}↔${first}`;
|
|
5159
|
-
return bidirectionalPairs.has(pairKey);
|
|
5160
|
-
}
|
|
5161
|
-
function buildDependencyGraph(collection) {
|
|
5162
|
-
const graph = new Map();
|
|
5163
|
-
const ensureNode = (fieldKey) => {
|
|
5164
|
-
let node = graph.get(fieldKey);
|
|
5165
|
-
if (!node) {
|
|
5166
|
-
node = {
|
|
5167
|
-
fieldKey,
|
|
5168
|
-
dependsOn: new Set(),
|
|
5169
|
-
dependedOnBy: new Set(),
|
|
5170
|
-
};
|
|
5171
|
-
graph.set(fieldKey, node);
|
|
5172
|
-
}
|
|
5173
|
-
return node;
|
|
5174
|
-
};
|
|
5175
|
-
for (const entry of collection.entries) {
|
|
5176
|
-
const fieldNode = ensureNode(entry.fieldKey);
|
|
5177
|
-
for (const dep of entry.dependsOn) {
|
|
5178
|
-
if (dep !== '*' && dep !== entry.fieldKey) {
|
|
5179
|
-
const depNode = ensureNode(dep);
|
|
5180
|
-
fieldNode.dependsOn.add(dep);
|
|
5181
|
-
depNode.dependedOnBy.add(entry.fieldKey);
|
|
5182
|
-
}
|
|
5183
|
-
}
|
|
5184
|
-
}
|
|
5185
|
-
return graph;
|
|
5186
|
-
}
|
|
5187
|
-
function detectCyclesWithDFS(graph, bidirectionalPairs) {
|
|
5188
|
-
const visitState = new Map();
|
|
5189
|
-
const parent = new Map();
|
|
5190
|
-
for (const fieldKey of graph.keys()) {
|
|
5191
|
-
visitState.set(fieldKey, 0 /* VisitState.Unvisited */);
|
|
5192
|
-
}
|
|
5193
|
-
for (const fieldKey of graph.keys()) {
|
|
5194
|
-
if (visitState.get(fieldKey) === 0 /* VisitState.Unvisited */) {
|
|
5195
|
-
const cycleResult = dfsVisit(fieldKey, graph, visitState, parent, [], bidirectionalPairs);
|
|
5196
|
-
if (cycleResult) {
|
|
5197
|
-
return cycleResult;
|
|
5198
|
-
}
|
|
5199
|
-
}
|
|
5200
|
-
}
|
|
5201
|
-
return { hasCycle: false };
|
|
5202
|
-
}
|
|
5203
|
-
function dfsVisit(fieldKey, graph, visitState, parent, path, bidirectionalPairs) {
|
|
5204
|
-
visitState.set(fieldKey, 1 /* VisitState.InProgress */);
|
|
5205
|
-
path.push(fieldKey);
|
|
5206
|
-
const node = graph.get(fieldKey);
|
|
5207
|
-
if (!node) {
|
|
5208
|
-
visitState.set(fieldKey, 2 /* VisitState.Completed */);
|
|
5209
|
-
path.pop();
|
|
5210
|
-
return null;
|
|
5211
|
-
}
|
|
5212
|
-
for (const targetFieldKey of node.dependedOnBy) {
|
|
5213
|
-
const targetState = visitState.get(targetFieldKey);
|
|
5214
|
-
if (targetState === 1 /* VisitState.InProgress */) {
|
|
5215
|
-
const cyclePath = extractCyclePath(path, targetFieldKey);
|
|
5216
|
-
if (isBidirectionalCycle(cyclePath, bidirectionalPairs)) {
|
|
5217
|
-
continue;
|
|
5218
|
-
}
|
|
5219
|
-
return {
|
|
5220
|
-
hasCycle: true,
|
|
5221
|
-
cyclePath,
|
|
5222
|
-
errorMessage: formatCycleError(cyclePath),
|
|
5223
|
-
};
|
|
5224
|
-
}
|
|
5225
|
-
if (targetState === 0 /* VisitState.Unvisited */) {
|
|
5226
|
-
parent.set(targetFieldKey, fieldKey);
|
|
5227
|
-
const result = dfsVisit(targetFieldKey, graph, visitState, parent, path, bidirectionalPairs);
|
|
5228
|
-
if (result) {
|
|
5229
|
-
return result;
|
|
5230
|
-
}
|
|
5231
|
-
}
|
|
5232
|
-
}
|
|
5233
|
-
visitState.set(fieldKey, 2 /* VisitState.Completed */);
|
|
5234
|
-
path.pop();
|
|
5235
|
-
return null;
|
|
5236
|
-
}
|
|
5237
|
-
function extractCyclePath(path, cycleStart) {
|
|
5238
|
-
const cycleStartIndex = path.indexOf(cycleStart);
|
|
5239
|
-
if (cycleStartIndex === -1) {
|
|
5240
|
-
return [...path, cycleStart];
|
|
5241
|
-
}
|
|
5242
|
-
const cyclePath = path.slice(cycleStartIndex);
|
|
5243
|
-
cyclePath.push(cycleStart); return cyclePath;
|
|
5244
|
-
}
|
|
5245
|
-
function formatCycleError(cyclePath) {
|
|
5246
|
-
const pathStr = cyclePath.join(' -> ');
|
|
5247
|
-
return (`Derivation cycle detected: ${pathStr}\n` +
|
|
5248
|
-
`This would cause an infinite loop at runtime. ` +
|
|
5249
|
-
`Remove one of the derivations to break the cycle.`);
|
|
5250
|
-
}
|
|
5251
|
-
function validateNoCycles(collection, logger) {
|
|
5252
|
-
const result = detectCycles(collection);
|
|
5253
|
-
if (result.hasCycle) {
|
|
5254
|
-
throw new Error(result.errorMessage);
|
|
5255
|
-
}
|
|
5256
|
-
if (DEV_MODE && result.bidirectionalPairs && result.bidirectionalPairs.length > 0 && logger) {
|
|
5257
|
-
logger.warn('Derivation: bidirectional derivation patterns detected. ' +
|
|
5258
|
-
'These patterns stabilize via equality checks, but may oscillate with floating-point values ' +
|
|
5259
|
-
'(e.g., currency conversions with rounding). ' +
|
|
5260
|
-
'Consider adding tolerance-based comparisons for numeric values.', result.bidirectionalPairs);
|
|
5261
|
-
}
|
|
5262
|
-
}
|
|
5263
|
-
|
|
5264
|
-
const LOG_PREFIX$3 = 'HTTP Derivation -';
|
|
5265
|
-
const DEFAULT_HTTP_DEBOUNCE_MS$1 = 300;
|
|
5266
|
-
function createHttpDerivationStream(entry, formValue$, context) {
|
|
5267
|
-
if (isArrayPlaceholderPath(entry.fieldKey)) {
|
|
5268
|
-
context.logger.warn(`${LOG_PREFIX$3} HTTP derivation for array field '${entry.fieldKey}' is not supported. ` +
|
|
5269
|
-
`Array HTTP derivations will be supported in a future release.`);
|
|
5270
|
-
return EMPTY;
|
|
5271
|
-
}
|
|
5272
|
-
if (!entry.http || !entry.responseExpression) {
|
|
5273
|
-
return EMPTY;
|
|
5274
|
-
}
|
|
5275
|
-
const responseExpression = entry.responseExpression;
|
|
5276
|
-
const debounceMs = entry.debounceMs ?? DEFAULT_HTTP_DEBOUNCE_MS$1;
|
|
5277
|
-
return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
|
|
5278
|
-
current: current,
|
|
5279
|
-
changedFields: getChangedKeys(previous, current),
|
|
5280
|
-
})),
|
|
5281
|
-
filter(({ changedFields }) => {
|
|
5282
|
-
if (changedFields.size === 0)
|
|
5283
|
-
return false;
|
|
5284
|
-
return entry.dependsOn.some((dep) => changedFields.has(dep));
|
|
5285
|
-
}), debounceTime(debounceMs), switchMap(({ current, changedFields }) => {
|
|
5286
|
-
return new Observable((subscriber) => {
|
|
5287
|
-
const formAccessor = untracked(() => context.form());
|
|
5288
|
-
if (entry.stopOnUserOverride) {
|
|
5289
|
-
const isDirty = readFieldDirty(formAccessor, entry.fieldKey);
|
|
5290
|
-
if (isDirty) {
|
|
5291
|
-
if (entry.reEngageOnDependencyChange && changedFields.size > 0) {
|
|
5292
|
-
if (entry.dependsOn.some((dep) => changedFields.has(dep))) {
|
|
5293
|
-
resetFieldState(formAccessor, entry.fieldKey);
|
|
5294
|
-
}
|
|
5295
|
-
else {
|
|
5296
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5297
|
-
derivationLogger.evaluation({
|
|
5298
|
-
debugName: entry.debugName,
|
|
5299
|
-
fieldKey: entry.fieldKey,
|
|
5300
|
-
result: 'skipped',
|
|
5301
|
-
skipReason: 'user-override',
|
|
5302
|
-
});
|
|
5303
|
-
subscriber.complete();
|
|
5304
|
-
return;
|
|
5305
|
-
}
|
|
5306
|
-
}
|
|
5307
|
-
else {
|
|
5308
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5309
|
-
derivationLogger.evaluation({
|
|
5310
|
-
debugName: entry.debugName,
|
|
5311
|
-
fieldKey: entry.fieldKey,
|
|
5312
|
-
result: 'skipped',
|
|
5313
|
-
skipReason: 'user-override',
|
|
5314
|
-
});
|
|
5315
|
-
subscriber.complete();
|
|
5316
|
-
return;
|
|
5317
|
-
}
|
|
5318
|
-
}
|
|
5319
|
-
}
|
|
5320
|
-
const customFunctions = context.customFunctions?.();
|
|
5321
|
-
const externalData = context.externalData?.();
|
|
5322
|
-
const fieldValue = getNestedValue(current, entry.fieldKey);
|
|
5323
|
-
const fieldAccessor = formAccessor[entry.fieldKey];
|
|
5324
|
-
const fieldState = fieldAccessor ? readFieldStateInfo(fieldAccessor, false) : undefined;
|
|
5325
|
-
const evalContext = {
|
|
5326
|
-
fieldValue,
|
|
5327
|
-
formValue: current,
|
|
5328
|
-
fieldPath: entry.fieldKey,
|
|
5329
|
-
customFunctions,
|
|
5330
|
-
externalData,
|
|
5331
|
-
logger: context.logger,
|
|
5332
|
-
fieldState,
|
|
5333
|
-
formFieldState: createFormFieldStateMap(formAccessor, false),
|
|
5334
|
-
};
|
|
5335
|
-
if (entry.condition !== true) {
|
|
5336
|
-
const conditionMet = typeof entry.condition === 'boolean' ? entry.condition : evaluateCondition(entry.condition, evalContext);
|
|
5337
|
-
if (!conditionMet) {
|
|
5338
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5339
|
-
derivationLogger.evaluation({
|
|
5340
|
-
debugName: entry.debugName,
|
|
5341
|
-
fieldKey: entry.fieldKey,
|
|
5342
|
-
result: 'skipped',
|
|
5343
|
-
skipReason: 'condition-false',
|
|
5344
|
-
});
|
|
5345
|
-
subscriber.complete();
|
|
5346
|
-
return;
|
|
5347
|
-
}
|
|
5348
|
-
}
|
|
5349
|
-
const resolvedRequest = resolveHttpRequest(entry.http, evalContext);
|
|
5350
|
-
if (!resolvedRequest) {
|
|
5351
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5352
|
-
derivationLogger.evaluation({
|
|
5353
|
-
debugName: entry.debugName,
|
|
5354
|
-
fieldKey: entry.fieldKey,
|
|
5355
|
-
result: 'skipped',
|
|
5356
|
-
skipReason: 'condition-false',
|
|
5357
|
-
});
|
|
5358
|
-
subscriber.complete();
|
|
5359
|
-
return;
|
|
5360
|
-
}
|
|
5361
|
-
const method = (resolvedRequest.method ?? 'GET').toUpperCase();
|
|
5362
|
-
const httpSub = context.httpClient
|
|
5363
|
-
.request(method, resolvedRequest.url, {
|
|
5364
|
-
body: resolvedRequest.body,
|
|
5365
|
-
headers: resolvedRequest.headers,
|
|
5366
|
-
})
|
|
5367
|
-
.subscribe({
|
|
5368
|
-
next: (response) => {
|
|
5369
|
-
try {
|
|
5370
|
-
const newValue = ExpressionParser.evaluate(responseExpression, { response });
|
|
5371
|
-
const currentFormValue = untracked(() => context.formValue());
|
|
5372
|
-
const currentValue = getNestedValue(currentFormValue, entry.fieldKey);
|
|
5373
|
-
if (isEqual(currentValue, newValue)) {
|
|
5374
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5375
|
-
derivationLogger.evaluation({
|
|
5376
|
-
debugName: entry.debugName,
|
|
5377
|
-
fieldKey: entry.fieldKey,
|
|
5378
|
-
result: 'skipped',
|
|
5379
|
-
skipReason: 'value-unchanged',
|
|
5380
|
-
});
|
|
5381
|
-
subscriber.complete();
|
|
5382
|
-
return;
|
|
5383
|
-
}
|
|
5384
|
-
const currentForm = untracked(() => context.form());
|
|
5385
|
-
if (entry.stopOnUserOverride && readFieldDirty(currentForm, entry.fieldKey)) {
|
|
5386
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5387
|
-
derivationLogger.evaluation({
|
|
5388
|
-
debugName: entry.debugName,
|
|
5389
|
-
fieldKey: entry.fieldKey,
|
|
5390
|
-
result: 'skipped',
|
|
5391
|
-
skipReason: 'user-override',
|
|
5392
|
-
});
|
|
5393
|
-
subscriber.complete();
|
|
5394
|
-
return;
|
|
5395
|
-
}
|
|
5396
|
-
applyValueToForm(entry.fieldKey, newValue, currentForm, context.logger, context.warningTracker);
|
|
5397
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5398
|
-
derivationLogger.evaluation({
|
|
5399
|
-
debugName: entry.debugName,
|
|
5400
|
-
fieldKey: entry.fieldKey,
|
|
5401
|
-
result: 'applied',
|
|
5402
|
-
previousValue: currentValue,
|
|
5403
|
-
newValue,
|
|
5404
|
-
});
|
|
5405
|
-
subscriber.complete();
|
|
5406
|
-
}
|
|
5407
|
-
catch (error) {
|
|
5408
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5409
|
-
context.logger.warn(`${LOG_PREFIX$3} Failed to process response for '${entry.fieldKey}': ${message}`);
|
|
5410
|
-
subscriber.complete();
|
|
5411
|
-
}
|
|
5412
|
-
},
|
|
5413
|
-
error: (error) => {
|
|
5414
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5415
|
-
context.logger.warn(`${LOG_PREFIX$3} HTTP request failed for '${entry.fieldKey}': ${message}`);
|
|
5416
|
-
subscriber.complete();
|
|
5417
|
-
},
|
|
5418
|
-
});
|
|
5419
|
-
return () => {
|
|
5420
|
-
httpSub.unsubscribe();
|
|
5421
|
-
};
|
|
5422
|
-
});
|
|
5423
|
-
}), catchError$1((error) => {
|
|
5424
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5425
|
-
context.logger.warn(`${LOG_PREFIX$3} Unexpected stream error for '${entry.fieldKey}': ${message}`);
|
|
5426
|
-
return EMPTY;
|
|
5427
|
-
}));
|
|
5428
|
-
}
|
|
5429
|
-
|
|
5430
|
-
const LOG_PREFIX$2 = 'Async Derivation -';
|
|
5431
|
-
const DEFAULT_ASYNC_DEBOUNCE_MS$1 = 300;
|
|
5432
|
-
function createAsyncDerivationStream(entry, formValue$, context) {
|
|
5433
|
-
if (isArrayPlaceholderPath(entry.fieldKey)) {
|
|
5434
|
-
context.logger.warn(`${LOG_PREFIX$2} Async derivation for array field '${entry.fieldKey}' is not supported. ` +
|
|
5435
|
-
`Array async derivations will be supported in a future release.`);
|
|
5436
|
-
return EMPTY;
|
|
5437
|
-
}
|
|
5438
|
-
if (!entry.asyncFunctionName && !entry.asyncFn) {
|
|
5439
|
-
context.logger.warn(`${LOG_PREFIX$2} Derivation for '${entry.fieldKey}' has neither "asyncFunctionName" nor "asyncFn". ` +
|
|
5440
|
-
`One of the two is required for source: 'asyncFunction'.`);
|
|
5441
|
-
return EMPTY;
|
|
5442
|
-
}
|
|
5443
|
-
if (entry.asyncFn && entry.asyncFunctionName) {
|
|
5444
|
-
context.logger.warn(`${LOG_PREFIX$2} Both "asyncFn" and "asyncFunctionName" are set on derivation for '${entry.fieldKey}'. ` +
|
|
5445
|
-
`Inline "asyncFn" takes precedence; "asyncFunctionName" is ignored.`);
|
|
5446
|
-
}
|
|
5447
|
-
const inlineAsyncFn = entry.asyncFn;
|
|
5448
|
-
const asyncFunctionName = entry.asyncFunctionName;
|
|
5449
|
-
const debounceMs = entry.debounceMs ?? DEFAULT_ASYNC_DEBOUNCE_MS$1;
|
|
5450
|
-
return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
|
|
5451
|
-
current: current,
|
|
5452
|
-
changedFields: getChangedKeys(previous, current),
|
|
5453
|
-
})),
|
|
5454
|
-
filter(({ changedFields }) => {
|
|
5455
|
-
if (changedFields.size === 0)
|
|
5456
|
-
return false;
|
|
5457
|
-
return entry.dependsOn.some((dep) => changedFields.has(dep));
|
|
5458
|
-
}), debounceTime(debounceMs), switchMap(({ current, changedFields }) => {
|
|
5459
|
-
return new Observable((subscriber) => {
|
|
5460
|
-
const formAccessor = untracked(() => context.form());
|
|
5461
|
-
if (entry.stopOnUserOverride) {
|
|
5462
|
-
const isDirty = readFieldDirty(formAccessor, entry.fieldKey);
|
|
5463
|
-
if (isDirty) {
|
|
5464
|
-
if (entry.reEngageOnDependencyChange) {
|
|
5465
|
-
resetFieldState(formAccessor, entry.fieldKey);
|
|
5466
|
-
}
|
|
5467
|
-
else {
|
|
5468
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5469
|
-
derivationLogger.evaluation({
|
|
5470
|
-
debugName: entry.debugName,
|
|
5471
|
-
fieldKey: entry.fieldKey,
|
|
5472
|
-
result: 'skipped',
|
|
5473
|
-
skipReason: 'user-override',
|
|
5474
|
-
});
|
|
5475
|
-
subscriber.complete();
|
|
5476
|
-
return;
|
|
5477
|
-
}
|
|
5478
|
-
}
|
|
5479
|
-
}
|
|
5480
|
-
const customFunctions = context.customFunctions?.();
|
|
5481
|
-
const asyncDerivationFunctions = context.asyncDerivationFunctions?.();
|
|
5482
|
-
const externalData = context.externalData?.();
|
|
5483
|
-
const asyncFn = inlineAsyncFn ?? (asyncFunctionName ? asyncDerivationFunctions?.[asyncFunctionName] : undefined);
|
|
5484
|
-
if (!asyncFn) {
|
|
5485
|
-
context.logger.warn(`${LOG_PREFIX$2} Async derivation function '${asyncFunctionName}' not found for field '${entry.fieldKey}'. ` +
|
|
5486
|
-
`Register it in customFnConfig.asyncDerivations.`);
|
|
5487
|
-
subscriber.complete();
|
|
5488
|
-
return;
|
|
5489
|
-
}
|
|
5490
|
-
const fieldValue = getNestedValue(current, entry.fieldKey);
|
|
5491
|
-
const fieldAccessor = formAccessor[entry.fieldKey];
|
|
5492
|
-
const fieldState = fieldAccessor ? readFieldStateInfo(fieldAccessor, false) : undefined;
|
|
5493
|
-
const evalContext = {
|
|
5494
|
-
fieldValue,
|
|
5495
|
-
formValue: current,
|
|
5496
|
-
fieldPath: entry.fieldKey,
|
|
5497
|
-
customFunctions,
|
|
5498
|
-
externalData,
|
|
5499
|
-
logger: context.logger,
|
|
5500
|
-
fieldState,
|
|
5501
|
-
formFieldState: createFormFieldStateMap(formAccessor, false),
|
|
5502
|
-
};
|
|
5503
|
-
if (entry.condition !== true) {
|
|
5504
|
-
const conditionMet = typeof entry.condition === 'boolean' ? entry.condition : evaluateCondition(entry.condition, evalContext);
|
|
5505
|
-
if (!conditionMet) {
|
|
5506
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5507
|
-
derivationLogger.evaluation({
|
|
5508
|
-
debugName: entry.debugName,
|
|
5509
|
-
fieldKey: entry.fieldKey,
|
|
5510
|
-
result: 'skipped',
|
|
5511
|
-
skipReason: 'condition-false',
|
|
5512
|
-
});
|
|
5513
|
-
subscriber.complete();
|
|
5514
|
-
return;
|
|
5515
|
-
}
|
|
5516
|
-
}
|
|
5517
|
-
const result$ = from(asyncFn(evalContext));
|
|
5518
|
-
const asyncSub = result$.subscribe({
|
|
5519
|
-
next: (newValue) => {
|
|
5520
|
-
try {
|
|
5521
|
-
const currentFormValue = untracked(() => context.formValue());
|
|
5522
|
-
const currentValue = getNestedValue(currentFormValue, entry.fieldKey);
|
|
5523
|
-
if (isEqual(currentValue, newValue)) {
|
|
5524
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5525
|
-
derivationLogger.evaluation({
|
|
5526
|
-
debugName: entry.debugName,
|
|
5527
|
-
fieldKey: entry.fieldKey,
|
|
5528
|
-
result: 'skipped',
|
|
5529
|
-
skipReason: 'value-unchanged',
|
|
5530
|
-
});
|
|
5531
|
-
subscriber.complete();
|
|
5532
|
-
return;
|
|
5533
|
-
}
|
|
5534
|
-
const currentForm = untracked(() => context.form());
|
|
5535
|
-
if (entry.stopOnUserOverride && readFieldDirty(currentForm, entry.fieldKey)) {
|
|
5536
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5537
|
-
derivationLogger.evaluation({
|
|
5538
|
-
debugName: entry.debugName,
|
|
5539
|
-
fieldKey: entry.fieldKey,
|
|
5540
|
-
result: 'skipped',
|
|
5541
|
-
skipReason: 'user-override',
|
|
5542
|
-
});
|
|
5543
|
-
subscriber.complete();
|
|
5544
|
-
return;
|
|
5545
|
-
}
|
|
5546
|
-
applyValueToForm(entry.fieldKey, newValue, currentForm, context.logger, context.warningTracker);
|
|
5547
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5548
|
-
derivationLogger.evaluation({
|
|
5549
|
-
debugName: entry.debugName,
|
|
5550
|
-
fieldKey: entry.fieldKey,
|
|
5551
|
-
result: 'applied',
|
|
5552
|
-
previousValue: currentValue,
|
|
5553
|
-
newValue,
|
|
5554
|
-
});
|
|
5555
|
-
subscriber.complete();
|
|
5556
|
-
}
|
|
5557
|
-
catch (error) {
|
|
5558
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5559
|
-
context.logger.warn(`${LOG_PREFIX$2} Failed to process result for '${entry.fieldKey}': ${message}`);
|
|
5560
|
-
subscriber.complete();
|
|
5561
|
-
}
|
|
5562
|
-
},
|
|
5563
|
-
error: (error) => {
|
|
5564
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5565
|
-
context.logger.warn(`${LOG_PREFIX$2} Async function failed for '${entry.fieldKey}': ${message}`);
|
|
5566
|
-
subscriber.complete();
|
|
5567
|
-
},
|
|
5568
|
-
complete: () => {
|
|
5569
|
-
subscriber.complete();
|
|
5570
|
-
},
|
|
5571
|
-
});
|
|
5572
|
-
return () => {
|
|
5573
|
-
asyncSub.unsubscribe();
|
|
5574
|
-
};
|
|
5575
|
-
});
|
|
5576
|
-
}), catchError$1((error) => {
|
|
5577
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5578
|
-
context.logger.warn(`${LOG_PREFIX$2} Unexpected stream error for '${entry.fieldKey}': ${message}`);
|
|
5579
|
-
return EMPTY;
|
|
5580
|
-
}));
|
|
5581
|
-
}
|
|
5582
|
-
|
|
5583
|
-
function resolveExternalData(externalDataSignal) {
|
|
5584
|
-
const externalDataRecord = untracked(() => externalDataSignal?.());
|
|
5585
|
-
if (!externalDataRecord)
|
|
5586
|
-
return undefined;
|
|
5587
|
-
const resolved = {};
|
|
5588
|
-
for (const [key, value] of Object.entries(externalDataRecord)) {
|
|
5589
|
-
if (isSignal(value)) {
|
|
5590
|
-
resolved[key] = untracked(() => value());
|
|
5591
|
-
}
|
|
5592
|
-
else {
|
|
5593
|
-
resolved[key] = value;
|
|
5594
|
-
}
|
|
5595
|
-
}
|
|
5596
|
-
return resolved;
|
|
5597
|
-
}
|
|
5598
|
-
|
|
5599
|
-
function getDebouncePeriods(entries) {
|
|
5600
|
-
const periods = new Set();
|
|
5601
|
-
for (const entry of entries) {
|
|
5602
|
-
if (entry.trigger === 'debounced') {
|
|
5603
|
-
periods.add(entry.debounceMs ?? DEFAULT_DEBOUNCE_MS);
|
|
5604
|
-
}
|
|
5605
|
-
}
|
|
5606
|
-
return Array.from(periods);
|
|
5607
|
-
}
|
|
5608
|
-
function filterEntriesByDebouncePeriod(entries, period) {
|
|
5609
|
-
return entries.filter((entry) => entry.trigger === 'debounced' && (entry.debounceMs ?? DEFAULT_DEBOUNCE_MS) === period);
|
|
5610
|
-
}
|
|
5611
|
-
|
|
5612
|
-
function entrySetsEqual(prev, next, sig) {
|
|
5613
|
-
if (prev.length !== next.length)
|
|
5614
|
-
return false;
|
|
5615
|
-
const counts = new Map();
|
|
5616
|
-
for (const item of prev) {
|
|
5617
|
-
const key = sig(item);
|
|
5618
|
-
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
5619
|
-
}
|
|
5620
|
-
for (const item of next) {
|
|
5621
|
-
const key = sig(item);
|
|
5622
|
-
const remaining = counts.get(key);
|
|
5623
|
-
if (!remaining)
|
|
5624
|
-
return false;
|
|
5625
|
-
if (remaining === 1)
|
|
5626
|
-
counts.delete(key);
|
|
5627
|
-
else
|
|
5628
|
-
counts.set(key, remaining - 1);
|
|
5629
|
-
}
|
|
5630
|
-
return counts.size === 0;
|
|
5631
|
-
}
|
|
5632
|
-
function buildEntryStreamPipeline(config) {
|
|
5633
|
-
return config.collection$.pipe(map((collection) => config.selectEntries(collection)), distinctUntilChanged((prev, next) => entrySetsEqual(prev, next, config.entrySignature)), switchMap((entries) => {
|
|
5634
|
-
if (entries.length === 0)
|
|
5635
|
-
return EMPTY;
|
|
5636
|
-
const streams = entries.map((entry) => config.createStream(entry)).filter((stream) => stream !== null);
|
|
5637
|
-
if (streams.length === 0)
|
|
5638
|
-
return EMPTY;
|
|
5639
|
-
return merge(...streams);
|
|
5640
|
-
}));
|
|
5641
|
-
}
|
|
5642
|
-
|
|
5643
|
-
function setupOnChangeStream(opts) {
|
|
5644
|
-
const collection$ = toObservable(opts.collectionSignal, { injector: opts.injector });
|
|
5645
|
-
const formValue$ = toObservable(opts.formValueSignal, { injector: opts.injector });
|
|
5646
|
-
const extra$ = opts.additionalAwakeners?.length ? opts.additionalAwakeners.map((s) => toObservable(s, { injector: opts.injector })) : [];
|
|
5647
|
-
collection$
|
|
5648
|
-
.pipe(filter((collection) => collection !== null), combineLatestWith(formValue$, ...extra$), auditTime(0), startWith(null), pairwise(), filter((pair) => pair[1] !== null), map(([previous, current]) => ({
|
|
5649
|
-
collection: current[0],
|
|
5650
|
-
changedFields: getChangedKeys(previous?.[1] ?? null, current[1]),
|
|
5651
|
-
})), exhaustMap(({ collection, changedFields }) => {
|
|
5652
|
-
opts.applyOnChange(collection, changedFields);
|
|
5653
|
-
return scheduled([null], queueScheduler);
|
|
5654
|
-
}), takeUntilDestroyed(opts.destroyRef))
|
|
5655
|
-
.subscribe({
|
|
5656
|
-
error: (err) => opts.logger.error(opts.errorLabel, err),
|
|
5657
|
-
});
|
|
5658
|
-
}
|
|
5659
|
-
function setupDebouncedStream(opts) {
|
|
5660
|
-
toObservable(opts.formValueSignal, { injector: opts.injector })
|
|
5661
|
-
.pipe(debounceTime(DEFAULT_DEBOUNCE_MS), startWith(null), pairwise(), filter((pair) => pair[1] !== null), map(([previous, current]) => ({ current, changedFields: getChangedKeys(previous, current) })), filter(({ changedFields }) => changedFields.size > 0), switchMap(({ changedFields }) => {
|
|
5662
|
-
const collection = untracked(() => opts.collectionSignal());
|
|
5663
|
-
if (!collection)
|
|
5664
|
-
return of(null);
|
|
5665
|
-
const debouncePeriods = getDebouncePeriods(collection.entries);
|
|
5666
|
-
if (debouncePeriods.length === 0)
|
|
5667
|
-
return of(null);
|
|
5668
|
-
const periodStreams = debouncePeriods.map((debounceMs) => {
|
|
5669
|
-
const additionalWait = Math.max(0, debounceMs - DEFAULT_DEBOUNCE_MS);
|
|
5670
|
-
if (additionalWait === 0) {
|
|
5671
|
-
return of(null).pipe(map(() => {
|
|
5672
|
-
opts.applyDebouncedForPeriod(debounceMs, collection, changedFields);
|
|
5673
|
-
}));
|
|
5674
|
-
}
|
|
5675
|
-
return timer(additionalWait).pipe(map(() => {
|
|
5676
|
-
const fresh = untracked(() => opts.collectionSignal()) ?? collection;
|
|
5677
|
-
opts.applyDebouncedForPeriod(debounceMs, fresh, changedFields);
|
|
5678
|
-
}));
|
|
5679
|
-
});
|
|
5680
|
-
return merge(...periodStreams);
|
|
5681
|
-
}), takeUntilDestroyed(opts.destroyRef))
|
|
5682
|
-
.subscribe({
|
|
5683
|
-
error: (err) => opts.logger.error(opts.errorLabel, err),
|
|
5684
|
-
});
|
|
5685
|
-
}
|
|
5686
|
-
function setupEntryAsyncStream(opts) {
|
|
5687
|
-
const collection$ = toObservable(opts.collectionSignal, { injector: opts.injector });
|
|
5688
|
-
buildEntryStreamPipeline({
|
|
5689
|
-
collection$,
|
|
5690
|
-
selectEntries: opts.selectEntries,
|
|
5691
|
-
entrySignature: opts.entrySignature,
|
|
5692
|
-
createStream: opts.createStream,
|
|
5693
|
-
})
|
|
5694
|
-
.pipe(catchError((err) => {
|
|
5695
|
-
opts.logger.error(opts.errorLabel, err);
|
|
5696
|
-
return EMPTY;
|
|
5697
|
-
}), takeUntilDestroyed(opts.destroyRef))
|
|
5698
|
-
.subscribe();
|
|
5699
|
-
}
|
|
5700
|
-
|
|
5701
|
-
function createEmptyPropertyDerivationCollection() {
|
|
5702
|
-
return { entries: [] };
|
|
5703
|
-
}
|
|
5704
|
-
function isAsyncPropertyDerivationEntry(entry) {
|
|
5705
|
-
return Boolean(entry.http) || Boolean(entry.asyncFunctionName) || Boolean(entry.asyncFn);
|
|
5706
|
-
}
|
|
5707
|
-
|
|
5708
|
-
const ERROR_PREFIX = '[PropertyDerivation]';
|
|
5709
|
-
function applyPropertyDerivations(collection, context, changedFields) {
|
|
5710
|
-
let appliedCount = 0;
|
|
5711
|
-
let skippedCount = 0;
|
|
5712
|
-
let errorCount = 0;
|
|
5713
|
-
const baseEntries = changedFields ? getEntriesForChangedFields(collection.entries, changedFields) : collection.entries;
|
|
5714
|
-
const entriesToProcess = baseEntries.filter((entry) => !isAsyncPropertyDerivationEntry(entry));
|
|
5715
|
-
for (const entry of entriesToProcess) {
|
|
5716
|
-
try {
|
|
5717
|
-
const applied = tryApplyPropertyDerivation(entry, context);
|
|
5718
|
-
if (applied) {
|
|
5719
|
-
appliedCount++;
|
|
5720
|
-
}
|
|
5721
|
-
else {
|
|
5722
|
-
skippedCount++;
|
|
5723
|
-
}
|
|
5724
|
-
}
|
|
5725
|
-
catch (error) {
|
|
5726
|
-
errorCount++;
|
|
5727
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5728
|
-
context.logger.error(`${ERROR_PREFIX} Failed to process property derivation (field: ${entry.fieldKey}, property: ${entry.targetProperty}): ${errorMessage}`);
|
|
5729
|
-
}
|
|
5730
|
-
}
|
|
5731
|
-
return { appliedCount, skippedCount, errorCount };
|
|
5732
|
-
}
|
|
5733
|
-
function applyPropertyDerivationsForTrigger(collection, trigger, context, changedFields) {
|
|
5734
|
-
const filteredEntries = collection.entries.filter((entry) => {
|
|
5735
|
-
if (trigger === 'onChange') {
|
|
5736
|
-
return !entry.trigger || entry.trigger === 'onChange';
|
|
5737
|
-
}
|
|
5738
|
-
return entry.trigger === trigger;
|
|
5739
|
-
});
|
|
5740
|
-
const filteredCollection = { entries: filteredEntries };
|
|
5741
|
-
return applyPropertyDerivations(filteredCollection, context, changedFields);
|
|
5742
|
-
}
|
|
5743
|
-
function getEntriesForChangedFields(entries, changedFields) {
|
|
5744
|
-
return entries.filter((entry) => {
|
|
5745
|
-
if (entry.dependsOn.includes('*'))
|
|
5746
|
-
return true;
|
|
5747
|
-
return entry.dependsOn.some((dep) => changedFields.has(dep));
|
|
5748
|
-
});
|
|
5749
|
-
}
|
|
5750
|
-
function tryApplyPropertyDerivation(entry, context) {
|
|
5751
|
-
if (isArrayPlaceholderPath(entry.fieldKey)) {
|
|
5752
|
-
return tryApplyArrayPropertyDerivation(entry, context);
|
|
5753
|
-
}
|
|
5754
|
-
const formValue = untracked(() => context.formValue());
|
|
5755
|
-
const evalContext = createEvaluationContext(entry, formValue, context);
|
|
5756
|
-
if (!evaluatePropertyCondition(entry.condition, evalContext)) {
|
|
5757
|
-
return false;
|
|
5758
|
-
}
|
|
5759
|
-
const newValue = computePropertyValue(entry, evalContext, context);
|
|
5760
|
-
context.store.setOverride(entry.fieldKey, entry.targetProperty, newValue);
|
|
5761
|
-
return true;
|
|
5762
|
-
}
|
|
5763
|
-
function tryApplyArrayPropertyDerivation(entry, context) {
|
|
5764
|
-
const formValue = untracked(() => context.formValue());
|
|
5765
|
-
const pathInfo = parseArrayPath(entry.fieldKey);
|
|
5766
|
-
if (!pathInfo.isArrayPath) {
|
|
5767
|
-
return false;
|
|
5768
|
-
}
|
|
5769
|
-
const { arrayPath } = pathInfo;
|
|
5770
|
-
const arrayValue = getNestedValue(formValue, arrayPath);
|
|
5771
|
-
if (!Array.isArray(arrayValue)) {
|
|
5772
|
-
return false;
|
|
5773
|
-
}
|
|
5774
|
-
let appliedAny = false;
|
|
5775
|
-
for (let i = 0; i < arrayValue.length; i++) {
|
|
5776
|
-
const resolvedPath = resolveArrayPath(entry.fieldKey, i);
|
|
5777
|
-
const arrayItem = arrayValue[i];
|
|
5778
|
-
const evalContext = createArrayItemEvaluationContext(entry, arrayItem, formValue, i, arrayPath, context);
|
|
5779
|
-
if (!evaluatePropertyCondition(entry.condition, evalContext)) {
|
|
5780
|
-
continue;
|
|
5781
|
-
}
|
|
5782
|
-
try {
|
|
5783
|
-
const newValue = computePropertyValue(entry, evalContext, context);
|
|
5784
|
-
context.store.setOverride(resolvedPath, entry.targetProperty, newValue);
|
|
5785
|
-
appliedAny = true;
|
|
5786
|
-
}
|
|
5787
|
-
catch (error) {
|
|
5788
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5789
|
-
context.logger.error(`${ERROR_PREFIX} Failed to compute property derivation (field: ${entry.fieldKey}, property: ${entry.targetProperty}, index: ${i}): ${errorMessage}`);
|
|
5790
|
-
}
|
|
5791
|
-
}
|
|
5792
|
-
return appliedAny;
|
|
5793
|
-
}
|
|
5794
|
-
function createEvaluationContext(entry, formValue, context) {
|
|
5795
|
-
const fieldValue = getNestedValue(formValue, entry.fieldKey);
|
|
5796
|
-
const parentPath = getParentPathInScope(entry.fieldKey);
|
|
5797
|
-
const groupValue = parentPath ? getNestedValue(formValue, parentPath) : undefined;
|
|
5798
|
-
return {
|
|
5799
|
-
fieldValue,
|
|
5800
|
-
formValue,
|
|
5801
|
-
fieldPath: entry.fieldKey,
|
|
5802
|
-
groupValue,
|
|
5803
|
-
customFunctions: context.customFunctions,
|
|
5804
|
-
externalData: context.externalData,
|
|
5805
|
-
logger: context.logger,
|
|
5806
|
-
};
|
|
5807
|
-
}
|
|
5808
|
-
function createArrayItemEvaluationContext(entry, arrayItem, rootFormValue, itemIndex, arrayPath, context) {
|
|
5809
|
-
const pathInfo = parseArrayPath(entry.fieldKey);
|
|
5810
|
-
const { relativePath, groupValue, fieldValue } = resolveArrayItemScope(pathInfo, arrayItem);
|
|
5811
|
-
return {
|
|
5812
|
-
fieldValue,
|
|
5813
|
-
formValue: arrayItem,
|
|
5814
|
-
fieldPath: relativePath ? `${arrayPath}.${itemIndex}.${relativePath}` : `${arrayPath}.${itemIndex}`,
|
|
5815
|
-
groupValue,
|
|
5816
|
-
customFunctions: context.customFunctions,
|
|
5817
|
-
externalData: context.externalData,
|
|
5818
|
-
logger: context.logger,
|
|
5819
|
-
rootFormValue,
|
|
5820
|
-
arrayIndex: itemIndex,
|
|
5821
|
-
arrayPath,
|
|
5822
|
-
};
|
|
5823
|
-
}
|
|
5824
|
-
function evaluatePropertyCondition(condition, context) {
|
|
5825
|
-
if (typeof condition === 'boolean') {
|
|
5826
|
-
return condition;
|
|
5827
|
-
}
|
|
5828
|
-
return evaluateCondition(condition, context);
|
|
5829
|
-
}
|
|
5830
|
-
function computePropertyValue(entry, evalContext, applicatorContext) {
|
|
5831
|
-
return computeValueFromEntry(entry, evalContext, {
|
|
5832
|
-
derivationFunctions: applicatorContext.derivationFunctions,
|
|
5833
|
-
subject: `${entry.fieldKey}.${entry.targetProperty}`,
|
|
5834
|
-
functionKind: 'Property derivation function',
|
|
5835
|
-
});
|
|
5836
|
-
}
|
|
5837
|
-
|
|
5838
|
-
const LOG_PREFIX$1 = 'HTTP Property Derivation -';
|
|
5839
|
-
const DEFAULT_HTTP_DEBOUNCE_MS = 300;
|
|
5840
|
-
function createHttpPropertyDerivationStream(entry, formValue$, context) {
|
|
5841
|
-
if (isArrayPlaceholderPath(entry.fieldKey)) {
|
|
5842
|
-
context.logger.warn(`${LOG_PREFIX$1} HTTP property derivation for array field '${entry.fieldKey}' is not supported. ` +
|
|
5843
|
-
`Array HTTP property derivations will be supported in a future release.`);
|
|
5844
|
-
return EMPTY;
|
|
5845
|
-
}
|
|
5846
|
-
if (!entry.http || !entry.responseExpression) {
|
|
5847
|
-
return EMPTY;
|
|
5848
|
-
}
|
|
5849
|
-
const responseExpression = entry.responseExpression;
|
|
5850
|
-
const debounceMs = entry.debounceMs ?? DEFAULT_HTTP_DEBOUNCE_MS;
|
|
5851
|
-
return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
|
|
5852
|
-
current: current,
|
|
5853
|
-
changedFields: getChangedKeys(previous, current),
|
|
5854
|
-
})), filter(({ changedFields }) => {
|
|
5855
|
-
if (changedFields.size === 0)
|
|
5856
|
-
return false;
|
|
5857
|
-
return entry.dependsOn.some((dep) => changedFields.has(dep));
|
|
5858
|
-
}), debounceTime(debounceMs), switchMap(({ current }) => {
|
|
5859
|
-
return new Observable((subscriber) => {
|
|
5860
|
-
const customFunctions = context.customFunctions?.();
|
|
5861
|
-
const externalData = context.externalData?.();
|
|
5862
|
-
const fieldValue = getNestedValue(current, entry.fieldKey);
|
|
5863
|
-
const evalContext = {
|
|
5864
|
-
fieldValue,
|
|
5865
|
-
formValue: current,
|
|
5866
|
-
fieldPath: entry.fieldKey,
|
|
5867
|
-
customFunctions,
|
|
5868
|
-
externalData,
|
|
5869
|
-
logger: context.logger,
|
|
5870
|
-
};
|
|
5871
|
-
if (entry.condition !== true) {
|
|
5872
|
-
const conditionMet = typeof entry.condition === 'boolean' ? entry.condition : evaluateCondition(entry.condition, evalContext);
|
|
5873
|
-
if (!conditionMet) {
|
|
5874
|
-
subscriber.complete();
|
|
5875
|
-
return;
|
|
5876
|
-
}
|
|
5877
|
-
}
|
|
5878
|
-
const resolvedRequest = resolveHttpRequest(entry.http, evalContext);
|
|
5879
|
-
if (!resolvedRequest) {
|
|
5880
|
-
subscriber.complete();
|
|
5881
|
-
return;
|
|
5882
|
-
}
|
|
5883
|
-
const method = (resolvedRequest.method ?? 'GET').toUpperCase();
|
|
5884
|
-
const httpSub = context.httpClient
|
|
5885
|
-
.request(method, resolvedRequest.url, {
|
|
5886
|
-
body: resolvedRequest.body,
|
|
5887
|
-
headers: resolvedRequest.headers,
|
|
5888
|
-
})
|
|
5889
|
-
.subscribe({
|
|
5890
|
-
next: (response) => {
|
|
5891
|
-
try {
|
|
5892
|
-
const newValue = ExpressionParser.evaluate(responseExpression, { response });
|
|
5893
|
-
untracked(() => context.store.setOverride(entry.fieldKey, entry.targetProperty, newValue));
|
|
5894
|
-
subscriber.complete();
|
|
5895
|
-
}
|
|
5896
|
-
catch (error) {
|
|
5897
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5898
|
-
context.logger.warn(`${LOG_PREFIX$1} Failed to process response for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
5899
|
-
subscriber.complete();
|
|
5900
|
-
}
|
|
5901
|
-
},
|
|
5902
|
-
error: (error) => {
|
|
5903
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5904
|
-
context.logger.warn(`${LOG_PREFIX$1} HTTP request failed for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
5905
|
-
subscriber.complete();
|
|
5906
|
-
},
|
|
5907
|
-
});
|
|
5908
|
-
return () => {
|
|
5909
|
-
httpSub.unsubscribe();
|
|
5910
|
-
};
|
|
5911
|
-
});
|
|
5912
|
-
}), catchError$1((error) => {
|
|
5913
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5914
|
-
context.logger.warn(`${LOG_PREFIX$1} Unexpected stream error for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
5915
|
-
return EMPTY;
|
|
5916
|
-
}));
|
|
5917
|
-
}
|
|
5918
|
-
|
|
5919
|
-
const LOG_PREFIX = 'Async Property Derivation -';
|
|
5920
|
-
const DEFAULT_ASYNC_DEBOUNCE_MS = 300;
|
|
5921
|
-
function createAsyncPropertyDerivationStream(entry, formValue$, context) {
|
|
5922
|
-
if (isArrayPlaceholderPath(entry.fieldKey)) {
|
|
5923
|
-
context.logger.warn(`${LOG_PREFIX} Async property derivation for array field '${entry.fieldKey}' is not supported. ` +
|
|
5924
|
-
`Array async property derivations will be supported in a future release.`);
|
|
5925
|
-
return EMPTY;
|
|
5926
|
-
}
|
|
5927
|
-
if (!entry.asyncFunctionName && !entry.asyncFn) {
|
|
5928
|
-
context.logger.warn(`${LOG_PREFIX} Property derivation for '${entry.fieldKey}.${entry.targetProperty}' has neither "asyncFunctionName" nor "asyncFn". ` +
|
|
5929
|
-
`One of the two is required for source: 'asyncFunction'.`);
|
|
5930
|
-
return EMPTY;
|
|
5931
|
-
}
|
|
5932
|
-
if (entry.asyncFn && entry.asyncFunctionName) {
|
|
5933
|
-
context.logger.warn(`${LOG_PREFIX} Both "asyncFn" and "asyncFunctionName" are set on property derivation for '${entry.fieldKey}.${entry.targetProperty}'. ` +
|
|
5934
|
-
`Inline "asyncFn" takes precedence; "asyncFunctionName" is ignored.`);
|
|
5935
|
-
}
|
|
5936
|
-
const inlineAsyncFn = entry.asyncFn;
|
|
5937
|
-
const asyncFunctionName = entry.asyncFunctionName;
|
|
5938
|
-
const debounceMs = entry.debounceMs ?? DEFAULT_ASYNC_DEBOUNCE_MS;
|
|
5939
|
-
return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
|
|
5940
|
-
current: current,
|
|
5941
|
-
changedFields: getChangedKeys(previous, current),
|
|
5942
|
-
})), filter(({ changedFields }) => {
|
|
5943
|
-
if (changedFields.size === 0)
|
|
5944
|
-
return false;
|
|
5945
|
-
return entry.dependsOn.some((dep) => changedFields.has(dep));
|
|
5946
|
-
}), debounceTime(debounceMs), switchMap(({ current }) => {
|
|
5947
|
-
return new Observable((subscriber) => {
|
|
5948
|
-
const customFunctions = context.customFunctions?.();
|
|
5949
|
-
const asyncDerivationFunctions = context.asyncDerivationFunctions?.();
|
|
5950
|
-
const externalData = context.externalData?.();
|
|
5951
|
-
const asyncFn = inlineAsyncFn ?? (asyncFunctionName ? asyncDerivationFunctions?.[asyncFunctionName] : undefined);
|
|
5952
|
-
if (!asyncFn) {
|
|
5953
|
-
context.logger.warn(`${LOG_PREFIX} Async derivation function '${asyncFunctionName}' not found for '${entry.fieldKey}.${entry.targetProperty}'. ` +
|
|
5954
|
-
`Register it in customFnConfig.asyncDerivations.`);
|
|
5955
|
-
subscriber.complete();
|
|
5956
|
-
return;
|
|
5957
|
-
}
|
|
5958
|
-
const fieldValue = getNestedValue(current, entry.fieldKey);
|
|
5959
|
-
const evalContext = {
|
|
5960
|
-
fieldValue,
|
|
5961
|
-
formValue: current,
|
|
5962
|
-
fieldPath: entry.fieldKey,
|
|
5963
|
-
customFunctions,
|
|
5964
|
-
externalData,
|
|
5965
|
-
logger: context.logger,
|
|
5966
|
-
};
|
|
5967
|
-
if (entry.condition !== true) {
|
|
5968
|
-
const conditionMet = typeof entry.condition === 'boolean' ? entry.condition : evaluateCondition(entry.condition, evalContext);
|
|
5969
|
-
if (!conditionMet) {
|
|
5970
|
-
subscriber.complete();
|
|
5971
|
-
return;
|
|
5972
|
-
}
|
|
5973
|
-
}
|
|
5974
|
-
const result$ = from(asyncFn(evalContext));
|
|
5975
|
-
const asyncSub = result$.subscribe({
|
|
5976
|
-
next: (newValue) => {
|
|
5977
|
-
try {
|
|
5978
|
-
untracked(() => context.store.setOverride(entry.fieldKey, entry.targetProperty, newValue));
|
|
5979
|
-
subscriber.complete();
|
|
5980
|
-
}
|
|
5981
|
-
catch (error) {
|
|
5982
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5983
|
-
context.logger.warn(`${LOG_PREFIX} Failed to process result for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
5984
|
-
subscriber.complete();
|
|
5985
|
-
}
|
|
5986
|
-
},
|
|
5987
|
-
error: (error) => {
|
|
5988
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5989
|
-
context.logger.warn(`${LOG_PREFIX} Async function failed for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
5990
|
-
subscriber.complete();
|
|
5991
|
-
},
|
|
5992
|
-
complete: () => {
|
|
5993
|
-
subscriber.complete();
|
|
5994
|
-
},
|
|
5995
|
-
});
|
|
5996
|
-
return () => {
|
|
5997
|
-
asyncSub.unsubscribe();
|
|
5998
|
-
};
|
|
5999
|
-
});
|
|
6000
|
-
}), catchError$1((error) => {
|
|
6001
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
6002
|
-
context.logger.warn(`${LOG_PREFIX} Unexpected stream error for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
6003
|
-
return EMPTY;
|
|
6004
|
-
}));
|
|
6005
|
-
}
|
|
6006
|
-
|
|
6007
|
-
class DerivationOrchestrator {
|
|
6008
|
-
config;
|
|
6009
|
-
injector = inject(Injector);
|
|
6010
|
-
destroyRef = inject(DestroyRef);
|
|
6011
|
-
logger = inject(DynamicFormLogger);
|
|
6012
|
-
warningTracker = inject(DERIVATION_WARNING_TRACKER);
|
|
6013
|
-
functionRegistry = inject(FunctionRegistryService);
|
|
6014
|
-
httpClient = inject(HttpClient, { optional: true });
|
|
6015
|
-
formOptions;
|
|
6016
|
-
derivationCollection;
|
|
6017
|
-
propertyDerivationCollection;
|
|
6018
|
-
constructor(config) {
|
|
6019
|
-
this.config = config;
|
|
6020
|
-
const valueEnabled = !!(config.form && config.derivationLogger);
|
|
6021
|
-
const propertyStore = config.propertyStore;
|
|
6022
|
-
const propertyEnabled = !!propertyStore;
|
|
6023
|
-
const sharedAllDerivations = valueEnabled && propertyEnabled
|
|
6024
|
-
? computed(() => {
|
|
6025
|
-
const fields = config.schemaFields();
|
|
6026
|
-
if (!fields || fields.length === 0)
|
|
6027
|
-
return null;
|
|
6028
|
-
return collectAllDerivations(fields);
|
|
6029
|
-
})
|
|
6030
|
-
: null;
|
|
6031
|
-
this.derivationCollection = valueEnabled
|
|
6032
|
-
? this.setupValuePipeline(sharedAllDerivations)
|
|
6033
|
-
: computed(() => null);
|
|
6034
|
-
this.propertyDerivationCollection = propertyStore
|
|
6035
|
-
? this.setupPropertyPipeline(propertyStore, sharedAllDerivations)
|
|
6036
|
-
: computed(() => null);
|
|
6037
|
-
}
|
|
6038
|
-
setupValuePipeline(sharedAllDerivations) {
|
|
6039
|
-
this.formOptions = inject(FORM_OPTIONS);
|
|
6040
|
-
const collection = computed(() => {
|
|
6041
|
-
const fields = this.config.schemaFields();
|
|
6042
|
-
if (!fields || fields.length === 0) {
|
|
6043
|
-
return null;
|
|
6044
|
-
}
|
|
6045
|
-
const collected = sharedAllDerivations ? (sharedAllDerivations()?.value ?? null) : collectDerivations(fields);
|
|
6046
|
-
if (!collected || collected.entries.length === 0) {
|
|
6047
|
-
return null;
|
|
6048
|
-
}
|
|
6049
|
-
validateNoCycles(collected, this.logger);
|
|
6050
|
-
if (DEV_MODE) {
|
|
6051
|
-
warnAboutWildcardDependencies(this.logger, collected.entries, fields.length);
|
|
6052
|
-
warnAboutMisconfiguredReEngagement(this.logger, collected.entries);
|
|
6053
|
-
}
|
|
6054
|
-
return collected;
|
|
6055
|
-
}, ...(ngDevMode ? [{ debugName: "collection" }] : /* istanbul ignore next */ []));
|
|
6056
|
-
setupOnChangeStream({
|
|
6057
|
-
injector: this.injector,
|
|
6058
|
-
destroyRef: this.destroyRef,
|
|
6059
|
-
logger: this.logger,
|
|
6060
|
-
errorLabel: 'Derivation onChange stream error',
|
|
6061
|
-
collectionSignal: collection,
|
|
6062
|
-
formValueSignal: this.config.formValue,
|
|
6063
|
-
additionalAwakeners: [this.config.form],
|
|
6064
|
-
applyOnChange: (current, changedFields) => {
|
|
6065
|
-
this.applyOnChangeDerivations(current, untracked(() => this.config.form()), changedFields);
|
|
6066
|
-
},
|
|
6067
|
-
});
|
|
6068
|
-
setupDebouncedStream({
|
|
6069
|
-
injector: this.injector,
|
|
6070
|
-
destroyRef: this.destroyRef,
|
|
6071
|
-
logger: this.logger,
|
|
6072
|
-
errorLabel: 'Derivation debounced stream error',
|
|
6073
|
-
collectionSignal: collection,
|
|
6074
|
-
formValueSignal: this.config.formValue,
|
|
6075
|
-
applyDebouncedForPeriod: (debounceMs, current, changedFields) => {
|
|
6076
|
-
const formAccessor = untracked(() => this.config.form());
|
|
6077
|
-
if (!formAccessor)
|
|
6078
|
-
return;
|
|
6079
|
-
this.applyDebouncedEntriesForPeriod(debounceMs, current, formAccessor, changedFields);
|
|
6080
|
-
},
|
|
6081
|
-
});
|
|
6082
|
-
this.setupValueHttpStreams(collection);
|
|
6083
|
-
this.setupValueAsyncFunctionStreams(collection);
|
|
6084
|
-
return collection;
|
|
6085
|
-
}
|
|
6086
|
-
setupPropertyPipeline(propertyStore, sharedAllDerivations) {
|
|
6087
|
-
const collection = computed(() => {
|
|
6088
|
-
const fields = this.config.schemaFields();
|
|
6089
|
-
if (!fields || fields.length === 0) {
|
|
6090
|
-
return null;
|
|
6091
|
-
}
|
|
6092
|
-
const collected = sharedAllDerivations ? (sharedAllDerivations()?.property ?? null) : collectPropertyDerivations(fields);
|
|
6093
|
-
if (!collected || collected.entries.length === 0) {
|
|
6094
|
-
return null;
|
|
6095
|
-
}
|
|
6096
|
-
return collected;
|
|
6097
|
-
}, ...(ngDevMode ? [{ debugName: "collection" }] : /* istanbul ignore next */ []));
|
|
6098
|
-
explicitEffect([collection, this.config.schemaFields], ([current, fields]) => {
|
|
6099
|
-
propertyStore.clear();
|
|
6100
|
-
if (!current)
|
|
6101
|
-
return;
|
|
6102
|
-
for (const entry of current.entries) {
|
|
6103
|
-
propertyStore.registerField(entry.fieldKey);
|
|
6104
|
-
}
|
|
6105
|
-
if (DEV_MODE) {
|
|
6106
|
-
warnAboutWildcardPropertyDependencies(this.logger, current.entries, fields?.length ?? 0);
|
|
6107
|
-
}
|
|
6108
|
-
});
|
|
6109
|
-
setupOnChangeStream({
|
|
6110
|
-
injector: this.injector,
|
|
6111
|
-
destroyRef: this.destroyRef,
|
|
6112
|
-
logger: this.logger,
|
|
6113
|
-
errorLabel: 'Property derivation onChange stream error',
|
|
6114
|
-
collectionSignal: collection,
|
|
6115
|
-
formValueSignal: this.config.formValue,
|
|
6116
|
-
applyOnChange: (current) => this.applyOnChangePropertyDerivations(current, propertyStore),
|
|
6117
|
-
});
|
|
6118
|
-
setupDebouncedStream({
|
|
6119
|
-
injector: this.injector,
|
|
6120
|
-
destroyRef: this.destroyRef,
|
|
6121
|
-
logger: this.logger,
|
|
6122
|
-
errorLabel: 'Property derivation debounced stream error',
|
|
6123
|
-
collectionSignal: collection,
|
|
6124
|
-
formValueSignal: this.config.formValue,
|
|
6125
|
-
applyDebouncedForPeriod: (debounceMs, current, changedFields) => {
|
|
6126
|
-
this.applyDebouncedPropertyEntriesForPeriod(debounceMs, current, propertyStore, changedFields);
|
|
6127
|
-
},
|
|
6128
|
-
});
|
|
6129
|
-
this.setupPropertyHttpStreams(collection, propertyStore);
|
|
6130
|
-
this.setupPropertyAsyncFunctionStreams(collection, propertyStore);
|
|
6131
|
-
return collection;
|
|
6132
|
-
}
|
|
6133
|
-
applyOnChangeDerivations(collection, formAccessor, changedFields) {
|
|
6134
|
-
const applicatorContext = {
|
|
6135
|
-
formValue: this.config.formValue,
|
|
6136
|
-
rootForm: formAccessor,
|
|
6137
|
-
derivationFunctions: this.functionRegistry.getDerivationFunctions(),
|
|
6138
|
-
customFunctions: this.functionRegistry.getCustomFunctions(),
|
|
6139
|
-
externalData: resolveExternalData(this.config.externalData),
|
|
6140
|
-
logger: this.logger,
|
|
6141
|
-
warningTracker: this.warningTracker,
|
|
6142
|
-
derivationLogger: untracked(() => this.config.derivationLogger()),
|
|
6143
|
-
maxIterations: untracked(() => this.formOptions()?.maxDerivationIterations),
|
|
6144
|
-
};
|
|
6145
|
-
const result = applyDerivationsForTrigger(collection, 'onChange', applicatorContext, changedFields);
|
|
6146
|
-
if (result.maxIterationsReached) {
|
|
6147
|
-
this.logger.warn(`Derivation processing reached max iterations. ` +
|
|
6148
|
-
`This may indicate a loop in derivation logic that wasn't caught at build time. ` +
|
|
6149
|
-
`Applied: ${result.appliedCount}, Skipped: ${result.skippedCount}, Errors: ${result.errorCount}`);
|
|
6150
|
-
}
|
|
6151
|
-
}
|
|
6152
|
-
applyDebouncedEntriesForPeriod(debounceMs, collection, formAccessor, changedFields) {
|
|
6153
|
-
const debouncedEntries = collection.entries.filter((entry) => entry.trigger === 'debounced' && (entry.debounceMs ?? DEFAULT_DEBOUNCE_MS) === debounceMs);
|
|
6154
|
-
if (debouncedEntries.length === 0) {
|
|
6155
|
-
return;
|
|
6156
|
-
}
|
|
6157
|
-
const filteredCollection = {
|
|
6158
|
-
entries: debouncedEntries,
|
|
6159
|
-
};
|
|
6160
|
-
const applicatorContext = {
|
|
6161
|
-
formValue: this.config.formValue,
|
|
6162
|
-
rootForm: formAccessor,
|
|
6163
|
-
derivationFunctions: this.functionRegistry.getDerivationFunctions(),
|
|
6164
|
-
customFunctions: this.functionRegistry.getCustomFunctions(),
|
|
6165
|
-
externalData: resolveExternalData(this.config.externalData),
|
|
6166
|
-
logger: this.logger,
|
|
6167
|
-
warningTracker: this.warningTracker,
|
|
6168
|
-
derivationLogger: untracked(() => this.config.derivationLogger()),
|
|
6169
|
-
maxIterations: untracked(() => this.formOptions()?.maxDerivationIterations),
|
|
6170
|
-
};
|
|
6171
|
-
const result = applyDerivationsForTrigger(filteredCollection, 'debounced', applicatorContext, changedFields);
|
|
6172
|
-
if (result.maxIterationsReached) {
|
|
6173
|
-
this.logger.warn(`Debounced derivation processing reached max iterations (${debounceMs}ms). ` +
|
|
6174
|
-
`Applied: ${result.appliedCount}, Skipped: ${result.skippedCount}, Errors: ${result.errorCount}`);
|
|
6175
|
-
}
|
|
6176
|
-
}
|
|
6177
|
-
setupValueHttpStreams(collectionSignal) {
|
|
6178
|
-
const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
|
|
6179
|
-
setupEntryAsyncStream({
|
|
6180
|
-
injector: this.injector,
|
|
6181
|
-
destroyRef: this.destroyRef,
|
|
6182
|
-
logger: this.logger,
|
|
6183
|
-
errorLabel: 'HTTP Derivation: outer stream error',
|
|
6184
|
-
collectionSignal,
|
|
6185
|
-
selectEntries: (collection) => collection?.entries.filter((entry) => entry.http) ?? [],
|
|
6186
|
-
entrySignature: valueHttpEntrySignature,
|
|
6187
|
-
createStream: (entry) => {
|
|
6188
|
-
if (!this.httpClient) {
|
|
6189
|
-
this.logger.error('HTTP Derivation: HttpClient is not available. Ensure provideHttpClient() is included in your application providers.');
|
|
6190
|
-
return null;
|
|
6191
|
-
}
|
|
6192
|
-
const context = {
|
|
6193
|
-
formValue: this.config.formValue,
|
|
6194
|
-
form: this.config.form,
|
|
6195
|
-
httpClient: this.httpClient,
|
|
6196
|
-
logger: this.logger,
|
|
6197
|
-
derivationLogger: this.config.derivationLogger,
|
|
6198
|
-
customFunctions: () => this.functionRegistry.getCustomFunctions(),
|
|
6199
|
-
externalData: () => resolveExternalData(this.config.externalData),
|
|
6200
|
-
warningTracker: this.warningTracker,
|
|
6201
|
-
};
|
|
6202
|
-
return createHttpDerivationStream(entry, formValue$, context);
|
|
6203
|
-
},
|
|
6204
|
-
});
|
|
6205
|
-
}
|
|
6206
|
-
setupValueAsyncFunctionStreams(collectionSignal) {
|
|
6207
|
-
const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
|
|
6208
|
-
setupEntryAsyncStream({
|
|
6209
|
-
injector: this.injector,
|
|
6210
|
-
destroyRef: this.destroyRef,
|
|
6211
|
-
logger: this.logger,
|
|
6212
|
-
errorLabel: 'Async Derivation: outer stream error',
|
|
6213
|
-
collectionSignal,
|
|
6214
|
-
selectEntries: (collection) => collection?.entries.filter((entry) => entry.asyncFunctionName || entry.asyncFn) ?? [],
|
|
6215
|
-
entrySignature: valueAsyncEntrySignature,
|
|
6216
|
-
createStream: (entry) => createAsyncDerivationStream(entry, formValue$, {
|
|
6217
|
-
formValue: this.config.formValue,
|
|
6218
|
-
form: this.config.form,
|
|
6219
|
-
logger: this.logger,
|
|
6220
|
-
derivationLogger: this.config.derivationLogger,
|
|
6221
|
-
customFunctions: () => this.functionRegistry.getCustomFunctions(),
|
|
6222
|
-
asyncDerivationFunctions: () => this.functionRegistry.getAsyncDerivationFunctions(),
|
|
6223
|
-
externalData: () => resolveExternalData(this.config.externalData),
|
|
6224
|
-
warningTracker: this.warningTracker,
|
|
6225
|
-
}),
|
|
6226
|
-
});
|
|
6227
|
-
}
|
|
6228
|
-
applyOnChangePropertyDerivations(collection, store) {
|
|
6229
|
-
const applicatorContext = {
|
|
6230
|
-
formValue: this.config.formValue,
|
|
6231
|
-
store,
|
|
6232
|
-
derivationFunctions: this.functionRegistry.getDerivationFunctions(),
|
|
6233
|
-
customFunctions: this.functionRegistry.getCustomFunctions(),
|
|
6234
|
-
externalData: resolveExternalData(this.config.externalData),
|
|
6235
|
-
logger: this.logger,
|
|
6236
|
-
warningTracker: this.warningTracker,
|
|
6237
|
-
};
|
|
6238
|
-
applyPropertyDerivationsForTrigger(collection, 'onChange', applicatorContext);
|
|
6239
|
-
}
|
|
6240
|
-
applyDebouncedPropertyEntriesForPeriod(debounceMs, collection, store, changedFields) {
|
|
6241
|
-
const debouncedEntries = collection.entries.filter((entry) => entry.trigger === 'debounced' && (entry.debounceMs ?? DEFAULT_DEBOUNCE_MS) === debounceMs);
|
|
6242
|
-
if (debouncedEntries.length === 0)
|
|
6243
|
-
return;
|
|
6244
|
-
const filteredCollection = { entries: debouncedEntries };
|
|
6245
|
-
const applicatorContext = {
|
|
6246
|
-
formValue: this.config.formValue,
|
|
6247
|
-
store,
|
|
6248
|
-
derivationFunctions: this.functionRegistry.getDerivationFunctions(),
|
|
6249
|
-
customFunctions: this.functionRegistry.getCustomFunctions(),
|
|
6250
|
-
externalData: resolveExternalData(this.config.externalData),
|
|
6251
|
-
logger: this.logger,
|
|
6252
|
-
warningTracker: this.warningTracker,
|
|
6253
|
-
};
|
|
6254
|
-
applyPropertyDerivationsForTrigger(filteredCollection, 'debounced', applicatorContext, changedFields);
|
|
6255
|
-
}
|
|
6256
|
-
setupPropertyHttpStreams(collectionSignal, store) {
|
|
6257
|
-
const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
|
|
6258
|
-
setupEntryAsyncStream({
|
|
6259
|
-
injector: this.injector,
|
|
6260
|
-
destroyRef: this.destroyRef,
|
|
6261
|
-
logger: this.logger,
|
|
6262
|
-
errorLabel: 'HTTP Property Derivation: outer stream error',
|
|
6263
|
-
collectionSignal,
|
|
6264
|
-
selectEntries: (collection) => collection?.entries.filter((entry) => entry.http) ?? [],
|
|
6265
|
-
entrySignature: propertyHttpEntrySignature,
|
|
6266
|
-
createStream: (entry) => {
|
|
6267
|
-
if (!this.httpClient) {
|
|
6268
|
-
this.logger.error('HTTP Property Derivation: HttpClient is not available. Ensure provideHttpClient() is included in your application providers.');
|
|
6269
|
-
return null;
|
|
6270
|
-
}
|
|
6271
|
-
return createHttpPropertyDerivationStream(entry, formValue$, {
|
|
6272
|
-
formValue: this.config.formValue,
|
|
6273
|
-
store,
|
|
6274
|
-
httpClient: this.httpClient,
|
|
6275
|
-
logger: this.logger,
|
|
6276
|
-
customFunctions: () => this.functionRegistry.getCustomFunctions(),
|
|
6277
|
-
externalData: () => resolveExternalData(this.config.externalData),
|
|
6278
|
-
});
|
|
6279
|
-
},
|
|
6280
|
-
});
|
|
6281
|
-
}
|
|
6282
|
-
setupPropertyAsyncFunctionStreams(collectionSignal, store) {
|
|
6283
|
-
const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
|
|
6284
|
-
setupEntryAsyncStream({
|
|
6285
|
-
injector: this.injector,
|
|
6286
|
-
destroyRef: this.destroyRef,
|
|
6287
|
-
logger: this.logger,
|
|
6288
|
-
errorLabel: 'Async Property Derivation: outer stream error',
|
|
6289
|
-
collectionSignal,
|
|
6290
|
-
selectEntries: (collection) => collection?.entries.filter((entry) => entry.asyncFunctionName || entry.asyncFn) ?? [],
|
|
6291
|
-
entrySignature: propertyAsyncEntrySignature,
|
|
6292
|
-
createStream: (entry) => createAsyncPropertyDerivationStream(entry, formValue$, {
|
|
6293
|
-
formValue: this.config.formValue,
|
|
6294
|
-
store,
|
|
6295
|
-
logger: this.logger,
|
|
6296
|
-
customFunctions: () => this.functionRegistry.getCustomFunctions(),
|
|
6297
|
-
asyncDerivationFunctions: () => this.functionRegistry.getAsyncDerivationFunctions(),
|
|
6298
|
-
externalData: () => resolveExternalData(this.config.externalData),
|
|
6299
|
-
}),
|
|
6300
|
-
});
|
|
6301
|
-
}
|
|
6302
|
-
}
|
|
6303
|
-
function valueHttpEntrySignature(entry) {
|
|
6304
|
-
const config = {
|
|
6305
|
-
http: entry.http,
|
|
6306
|
-
responseExpression: entry.responseExpression,
|
|
6307
|
-
dependsOn: entry.dependsOn,
|
|
6308
|
-
debounceMs: entry.debounceMs,
|
|
6309
|
-
trigger: entry.trigger,
|
|
6310
|
-
condition: entry.condition,
|
|
6311
|
-
stopOnUserOverride: entry.stopOnUserOverride,
|
|
6312
|
-
reEngageOnDependencyChange: entry.reEngageOnDependencyChange,
|
|
6313
|
-
};
|
|
6314
|
-
return `${entry.fieldKey}:${JSON.stringify(config)}`;
|
|
6315
|
-
}
|
|
6316
|
-
function valueAsyncEntrySignature(entry) {
|
|
6317
|
-
const config = {
|
|
6318
|
-
asyncFunctionName: entry.asyncFunctionName,
|
|
6319
|
-
asyncFnId: entry.asyncFn ? `inline:${entry.fieldKey}` : undefined,
|
|
6320
|
-
dependsOn: entry.dependsOn,
|
|
6321
|
-
debounceMs: entry.debounceMs,
|
|
6322
|
-
trigger: entry.trigger,
|
|
6323
|
-
condition: entry.condition,
|
|
6324
|
-
stopOnUserOverride: entry.stopOnUserOverride,
|
|
6325
|
-
reEngageOnDependencyChange: entry.reEngageOnDependencyChange,
|
|
6326
|
-
};
|
|
6327
|
-
return `${entry.fieldKey}:${JSON.stringify(config)}`;
|
|
6328
|
-
}
|
|
6329
|
-
function propertyHttpEntrySignature(entry) {
|
|
6330
|
-
const config = {
|
|
6331
|
-
http: entry.http,
|
|
6332
|
-
responseExpression: entry.responseExpression,
|
|
6333
|
-
dependsOn: entry.dependsOn,
|
|
6334
|
-
debounceMs: entry.debounceMs,
|
|
6335
|
-
trigger: entry.trigger,
|
|
6336
|
-
condition: entry.condition,
|
|
6337
|
-
};
|
|
6338
|
-
return `${entry.fieldKey}.${entry.targetProperty}:${JSON.stringify(config)}`;
|
|
6339
|
-
}
|
|
6340
|
-
function propertyAsyncEntrySignature(entry) {
|
|
6341
|
-
const config = {
|
|
6342
|
-
asyncFunctionName: entry.asyncFunctionName,
|
|
6343
|
-
asyncFnId: entry.asyncFn ? `inline:${entry.fieldKey}.${entry.targetProperty}` : undefined,
|
|
6344
|
-
dependsOn: entry.dependsOn,
|
|
6345
|
-
debounceMs: entry.debounceMs,
|
|
6346
|
-
trigger: entry.trigger,
|
|
6347
|
-
condition: entry.condition,
|
|
6348
|
-
};
|
|
6349
|
-
return `${entry.fieldKey}.${entry.targetProperty}:${JSON.stringify(config)}`;
|
|
6350
|
-
}
|
|
6351
|
-
function createDerivationOrchestrator(config) {
|
|
6352
|
-
return new DerivationOrchestrator(config);
|
|
6353
|
-
}
|
|
6354
|
-
const DERIVATION_ORCHESTRATOR = new InjectionToken('DERIVATION_ORCHESTRATOR');
|
|
6355
|
-
const PROPERTY_DERIVATION_ORCHESTRATOR = new InjectionToken('PROPERTY_DERIVATION_ORCHESTRATOR');
|
|
6356
|
-
function injectDerivationOrchestrator() {
|
|
6357
|
-
return inject(DERIVATION_ORCHESTRATOR);
|
|
6358
|
-
}
|
|
6359
|
-
function warnAboutWildcardDependencies(logger, entries, fieldCount) {
|
|
6360
|
-
const wildcardEntries = entries.filter((entry) => entry.dependsOn.includes('*'));
|
|
6361
|
-
if (wildcardEntries.length === 0)
|
|
6362
|
-
return;
|
|
6363
|
-
const implicitWildcards = wildcardEntries.filter((entry) => !entry.http &&
|
|
6364
|
-
!entry.asyncFunctionName &&
|
|
6365
|
-
!entry.asyncFn &&
|
|
6366
|
-
(entry.functionName || entry.fn) &&
|
|
6367
|
-
(!entry.originalConfig?.dependsOn || entry.originalConfig.dependsOn.length === 0));
|
|
6368
|
-
if (implicitWildcards.length > 0) {
|
|
6369
|
-
const derivationDescs = implicitWildcards.map((e) => `${e.fieldKey} (${e.functionName ?? '<inline fn>'})`);
|
|
6370
|
-
logger.warn('Derivation: custom functions without explicit dependsOn detected. ' +
|
|
6371
|
-
`These run on EVERY form change, which may impact performance (form has ${fieldCount} fields). ` +
|
|
6372
|
-
'Consider specifying explicit dependsOn arrays for better performance.', derivationDescs);
|
|
6373
|
-
}
|
|
6374
|
-
}
|
|
6375
|
-
function warnAboutMisconfiguredReEngagement(logger, entries) {
|
|
6376
|
-
const misconfigured = entries.filter((entry) => entry.reEngageOnDependencyChange && !entry.stopOnUserOverride);
|
|
6377
|
-
if (misconfigured.length === 0)
|
|
6378
|
-
return;
|
|
6379
|
-
const fieldKeys = misconfigured.map((e) => e.debugName ?? e.fieldKey);
|
|
6380
|
-
logger.warn('Derivation: reEngageOnDependencyChange without stopOnUserOverride detected. ' +
|
|
6381
|
-
'reEngageOnDependencyChange only takes effect when stopOnUserOverride is true. ' +
|
|
6382
|
-
'Either add stopOnUserOverride: true or remove reEngageOnDependencyChange.', fieldKeys);
|
|
6383
|
-
}
|
|
6384
|
-
function warnAboutWildcardPropertyDependencies(logger, entries, fieldCount) {
|
|
6385
|
-
const implicitWildcards = entries.filter((entry) => entry.dependsOn.includes('*') &&
|
|
6386
|
-
(entry.functionName || entry.fn) &&
|
|
6387
|
-
(!entry.originalConfig?.dependsOn || entry.originalConfig.dependsOn.length === 0));
|
|
6388
|
-
if (implicitWildcards.length > 0) {
|
|
6389
|
-
const derivationDescs = implicitWildcards.map((e) => `${e.fieldKey}.${e.targetProperty} (${e.functionName ?? '<inline fn>'})`);
|
|
6390
|
-
logger.warn('PropertyDerivation: custom functions without explicit dependsOn detected. ' +
|
|
6391
|
-
`These run on EVERY form change, which may impact performance (form has ${fieldCount} fields). ` +
|
|
6392
|
-
'Consider specifying explicit dependsOn arrays for better performance.', derivationDescs);
|
|
6393
|
-
}
|
|
6394
|
-
}
|
|
6395
|
-
|
|
6396
|
-
class ActiveDerivationLogger {
|
|
6397
|
-
logger;
|
|
6398
|
-
config = createDefaultDerivationLogConfig();
|
|
6399
|
-
setConfig(config) {
|
|
6400
|
-
this.config = config;
|
|
6401
|
-
}
|
|
6402
|
-
cycleStart(trigger, entryCount) {
|
|
6403
|
-
if (!shouldLog(this.config, 'verbose'))
|
|
6404
|
-
return;
|
|
6405
|
-
this.logger.debug(`Derivation - Starting cycle (${trigger}) with ${entryCount} derivation(s)`);
|
|
6406
|
-
}
|
|
6407
|
-
iteration(iterationNumber) {
|
|
6408
|
-
if (!shouldLog(this.config, 'verbose'))
|
|
6409
|
-
return;
|
|
6410
|
-
this.logger.debug(`Derivation - Iteration ${iterationNumber}`);
|
|
6411
|
-
}
|
|
6412
|
-
evaluation(entry) {
|
|
6413
|
-
if (!shouldLog(this.config, 'verbose'))
|
|
6414
|
-
return;
|
|
6415
|
-
const name = entry.debugName ? `"${entry.debugName}"` : entry.fieldKey;
|
|
6416
|
-
switch (entry.result) {
|
|
6417
|
-
case 'applied':
|
|
6418
|
-
this.logger.debug(`Derivation - Applied ${name}`, {
|
|
6419
|
-
field: entry.fieldKey,
|
|
6420
|
-
previousValue: entry.previousValue,
|
|
6421
|
-
newValue: entry.newValue,
|
|
6422
|
-
...(entry.durationMs !== undefined && { durationMs: entry.durationMs }),
|
|
6423
|
-
});
|
|
6424
|
-
break;
|
|
6425
|
-
case 'skipped':
|
|
6426
|
-
this.logger.debug(`Derivation - Skipped ${name} (${this.formatSkipReason(entry.skipReason)})`, {
|
|
6427
|
-
field: entry.fieldKey,
|
|
6428
|
-
reason: entry.skipReason,
|
|
6429
|
-
});
|
|
6430
|
-
break;
|
|
6431
|
-
case 'error':
|
|
6432
|
-
this.logger.debug(`Derivation - Error ${name}`, {
|
|
6433
|
-
field: entry.fieldKey,
|
|
6434
|
-
error: entry.error,
|
|
6435
|
-
});
|
|
6436
|
-
break;
|
|
6437
|
-
}
|
|
6438
|
-
}
|
|
6439
|
-
summary(result, trigger) {
|
|
6440
|
-
if (!shouldLog(this.config, 'summary'))
|
|
6441
|
-
return;
|
|
6442
|
-
if (result.appliedCount === 0 && result.errorCount === 0)
|
|
6443
|
-
return;
|
|
6444
|
-
this.logger.debug(`Derivation - Cycle complete (${trigger})`, {
|
|
6445
|
-
applied: result.appliedCount,
|
|
6446
|
-
skipped: result.skippedCount,
|
|
6447
|
-
errors: result.errorCount,
|
|
6448
|
-
iterations: result.iterations,
|
|
6449
|
-
});
|
|
6450
|
-
}
|
|
6451
|
-
maxIterationsReached(result, trigger) {
|
|
6452
|
-
this.logger.warn(`Derivation - Max iterations reached (${trigger}). ` +
|
|
6453
|
-
`This may indicate a loop in derivation logic. ` +
|
|
6454
|
-
`Applied: ${result.appliedCount}, Skipped: ${result.skippedCount}, Errors: ${result.errorCount}`);
|
|
6455
|
-
}
|
|
6456
|
-
formatSkipReason(reason) {
|
|
6457
|
-
switch (reason) {
|
|
6458
|
-
case 'condition-false':
|
|
6459
|
-
return 'condition not met';
|
|
6460
|
-
case 'value-unchanged':
|
|
6461
|
-
return 'value unchanged';
|
|
6462
|
-
case 'already-applied':
|
|
6463
|
-
return 'already applied this cycle';
|
|
6464
|
-
case 'user-override':
|
|
6465
|
-
return 'user override active';
|
|
6466
|
-
case 'target-not-found':
|
|
6467
|
-
return 'target not found';
|
|
6468
|
-
default:
|
|
6469
|
-
return 'unknown';
|
|
6470
|
-
}
|
|
6471
|
-
}
|
|
6472
|
-
}
|
|
6473
|
-
class NoopDerivationLogger {
|
|
6474
|
-
cycleStart() {
|
|
6475
|
-
/* no-op */
|
|
6476
|
-
}
|
|
6477
|
-
iteration() {
|
|
6478
|
-
/* no-op */
|
|
6479
|
-
}
|
|
6480
|
-
evaluation() {
|
|
6481
|
-
/* no-op */
|
|
6482
|
-
}
|
|
6483
|
-
summary() {
|
|
6484
|
-
/* no-op */
|
|
6485
|
-
}
|
|
6486
|
-
maxIterationsReached() {
|
|
6487
|
-
/* no-op */
|
|
6488
|
-
}
|
|
6489
|
-
}
|
|
6490
|
-
const NOOP_INSTANCE = new NoopDerivationLogger();
|
|
6491
|
-
function createDerivationLogger(config, logger) {
|
|
6492
|
-
const effectiveConfig = config ?? createDefaultDerivationLogConfig();
|
|
6493
|
-
if (effectiveConfig.level === 'none') {
|
|
6494
|
-
return NOOP_INSTANCE;
|
|
6495
|
-
}
|
|
6496
|
-
const service = new ActiveDerivationLogger();
|
|
6497
|
-
service.logger = logger;
|
|
6498
|
-
service.setConfig(effectiveConfig);
|
|
6499
|
-
return service;
|
|
6500
|
-
}
|
|
6501
|
-
|
|
6502
|
-
function isDynamicFormFeature(value) {
|
|
6503
|
-
return (typeof value === 'object' &&
|
|
6504
|
-
value !== null &&
|
|
6505
|
-
'ɵkind' in value &&
|
|
6506
|
-
typeof value.ɵkind === 'string' &&
|
|
6507
|
-
'ɵproviders' in value &&
|
|
6508
|
-
Array.isArray(value.ɵproviders));
|
|
6509
|
-
}
|
|
6510
|
-
function createFeature(kind, providers) {
|
|
6511
|
-
return {
|
|
6512
|
-
ɵkind: kind,
|
|
6513
|
-
ɵproviders: providers,
|
|
6514
|
-
};
|
|
6515
|
-
}
|
|
6516
|
-
|
|
6517
|
-
class ConsoleLogger {
|
|
6518
|
-
prefix = '[Dynamic Forms]';
|
|
6519
|
-
debug(message, ...args) {
|
|
6520
|
-
console.debug(this.prefix, message, ...args);
|
|
6521
|
-
}
|
|
6522
|
-
info(message, ...args) {
|
|
6523
|
-
console.info(this.prefix, message, ...args);
|
|
6524
|
-
}
|
|
6525
|
-
warn(message, ...args) {
|
|
6526
|
-
console.warn(this.prefix, message, ...args);
|
|
6527
|
-
}
|
|
6528
|
-
error(message, ...args) {
|
|
6529
|
-
console.error(this.prefix, message, ...args);
|
|
6530
|
-
}
|
|
6531
|
-
}
|
|
6532
|
-
|
|
6533
|
-
const DERIVATION_LOG_CONFIG = new InjectionToken('DERIVATION_LOG_CONFIG', {
|
|
6534
|
-
providedIn: 'root',
|
|
6535
|
-
factory: createDefaultDerivationLogConfig,
|
|
6536
|
-
});
|
|
6537
|
-
function withLoggerConfig(config = true) {
|
|
6538
|
-
if (typeof config === 'boolean' || typeof config === 'function') {
|
|
6539
|
-
const isEnabled = typeof config === 'function' ? config() : config;
|
|
6540
|
-
const logger = isEnabled ? new ConsoleLogger() : new NoopLogger();
|
|
6541
|
-
return createFeature('logger', [{ provide: DynamicFormLogger, useValue: logger }]);
|
|
6542
|
-
}
|
|
6543
|
-
const isEnabled = config.enabled !== false;
|
|
6544
|
-
const logger = isEnabled ? new ConsoleLogger() : new NoopLogger();
|
|
6545
|
-
const providers = [{ provide: DynamicFormLogger, useValue: logger }];
|
|
6546
|
-
if (config.derivations !== undefined) {
|
|
6547
|
-
providers.push({
|
|
6548
|
-
provide: DERIVATION_LOG_CONFIG,
|
|
6549
|
-
useValue: { level: config.derivations },
|
|
6550
|
-
});
|
|
6551
|
-
}
|
|
6552
|
-
return createFeature('logger', providers);
|
|
6553
|
-
}
|
|
6554
|
-
|
|
6555
|
-
const ADDON_ACTION_HANDLERS = new InjectionToken('ADDON_ACTION_HANDLERS');
|
|
6556
|
-
const ADDON_ACTION_REGISTRY = new InjectionToken('ADDON_ACTION_REGISTRY');
|
|
6557
|
-
function createAddonActionRegistry() {
|
|
6558
|
-
const sources = inject(ADDON_ACTION_HANDLERS, { optional: true }) ?? [];
|
|
6559
|
-
const map = new Map();
|
|
6560
|
-
for (const handlers of sources) {
|
|
6561
|
-
for (const [name, fn] of Object.entries(handlers)) {
|
|
6562
|
-
map.set(name, fn);
|
|
6563
|
-
}
|
|
6564
|
-
}
|
|
6565
|
-
return map;
|
|
6566
|
-
}
|
|
6567
|
-
|
|
6568
|
-
function coreProviders() {
|
|
6569
|
-
return [
|
|
6570
|
-
EventBus,
|
|
6571
|
-
{ provide: CONTAINER_FIELD_PROCESSORS, useFactory: createContainerFieldProcessors },
|
|
6572
|
-
SchemaRegistryService,
|
|
6573
|
-
FunctionRegistryService,
|
|
6574
|
-
{
|
|
6575
|
-
provide: FORM_STATE_DEPS,
|
|
6576
|
-
useFactory: () => ({ config: null, formOptions: null, value: null, source: null }),
|
|
6577
|
-
},
|
|
6578
|
-
FormStateManager,
|
|
6579
|
-
{
|
|
6580
|
-
provide: RootFormRegistryService,
|
|
6581
|
-
useFactory: (stateManager) => new RootFormRegistryService(stateManager.formValue, computed(() => stateManager.form())),
|
|
6582
|
-
deps: [FormStateManager],
|
|
6583
|
-
},
|
|
6584
|
-
FieldContextRegistryService,
|
|
6585
|
-
ArrayItemRegistryService,
|
|
6586
|
-
{
|
|
6587
|
-
provide: DEFAULT_PROPS,
|
|
6588
|
-
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultProps),
|
|
6589
|
-
deps: [FormStateManager],
|
|
6590
|
-
},
|
|
6591
|
-
{
|
|
6592
|
-
provide: DEFAULT_WRAPPERS,
|
|
6593
|
-
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultWrappers),
|
|
6594
|
-
deps: [FormStateManager],
|
|
6595
|
-
},
|
|
6596
|
-
{
|
|
6597
|
-
provide: DEFAULT_VALIDATION_MESSAGES,
|
|
6598
|
-
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultValidationMessages),
|
|
6599
|
-
deps: [FormStateManager],
|
|
6600
|
-
},
|
|
6601
|
-
{
|
|
6602
|
-
provide: FORM_OPTIONS,
|
|
6603
|
-
useFactory: (stateManager) => stateManager.effectiveFormOptions,
|
|
6604
|
-
deps: [FormStateManager],
|
|
6605
|
-
},
|
|
6606
|
-
{
|
|
6607
|
-
provide: EXTERNAL_DATA,
|
|
6608
|
-
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.externalData),
|
|
6609
|
-
deps: [FormStateManager],
|
|
6610
|
-
},
|
|
6611
|
-
FormIdPrefixService,
|
|
6612
|
-
{
|
|
6613
|
-
provide: FORM_ID_PREFIX,
|
|
6614
|
-
useFactory: (svc) => svc.prefix,
|
|
6615
|
-
deps: [FormIdPrefixService],
|
|
6616
|
-
},
|
|
6617
|
-
{ provide: DEPRECATION_WARNING_TRACKER, useFactory: createWarningTracker },
|
|
6618
|
-
{ provide: ADDON_ACTION_REGISTRY, useFactory: createAddonActionRegistry },
|
|
6619
|
-
];
|
|
6620
|
-
}
|
|
6621
|
-
function derivationProviders() {
|
|
6622
|
-
return [
|
|
6623
|
-
{ provide: DERIVATION_WARNING_TRACKER, useFactory: createWarningTracker },
|
|
6624
|
-
{
|
|
6625
|
-
provide: DERIVATION_ORCHESTRATOR,
|
|
6626
|
-
useFactory: (stateManager, logger, logConfig, externalData, propertyStore) => {
|
|
6627
|
-
const config = {
|
|
6628
|
-
schemaFields: computed(() => stateManager.formSetup()?.schemaFields),
|
|
6629
|
-
formValue: stateManager.formValue,
|
|
6630
|
-
form: computed(() => stateManager.form()),
|
|
6631
|
-
derivationLogger: computed(() => createDerivationLogger(logConfig, logger)),
|
|
6632
|
-
propertyStore,
|
|
6633
|
-
externalData,
|
|
6634
|
-
};
|
|
6635
|
-
return createDerivationOrchestrator(config);
|
|
6636
|
-
},
|
|
6637
|
-
deps: [FormStateManager, DynamicFormLogger, DERIVATION_LOG_CONFIG, EXTERNAL_DATA, PROPERTY_OVERRIDE_STORE],
|
|
6638
|
-
},
|
|
6639
|
-
{ provide: FORM_INITIALIZER, useExisting: DERIVATION_ORCHESTRATOR, multi: true },
|
|
6640
|
-
];
|
|
6641
|
-
}
|
|
6642
|
-
function propertyDerivationProviders() {
|
|
6643
|
-
return [
|
|
6644
|
-
{ provide: PROPERTY_OVERRIDE_STORE, useFactory: createPropertyOverrideStore },
|
|
6645
|
-
{ provide: PROPERTY_DERIVATION_ORCHESTRATOR, useExisting: DERIVATION_ORCHESTRATOR },
|
|
6646
|
-
];
|
|
6647
|
-
}
|
|
6648
|
-
function httpExpressionProviders() {
|
|
6649
|
-
return [{ provide: HTTP_CONDITION_CACHE, useFactory: () => new HttpConditionCache(100) }, HttpConditionFunctionCacheService];
|
|
6650
|
-
}
|
|
6651
|
-
function asyncExpressionProviders() {
|
|
6652
|
-
return [AsyncConditionFunctionCacheService];
|
|
6653
|
-
}
|
|
6654
|
-
function dynamicValueProviders() {
|
|
6655
|
-
return [DynamicValueFunctionCacheService];
|
|
6656
|
-
}
|
|
6657
|
-
function logicProviders() {
|
|
6658
|
-
return [LogicFunctionCacheService];
|
|
6659
|
-
}
|
|
6660
|
-
function provideDynamicFormDI() {
|
|
6661
|
-
return [
|
|
6662
|
-
...coreProviders(),
|
|
6663
|
-
...logicProviders(),
|
|
6664
|
-
...dynamicValueProviders(),
|
|
6665
|
-
...httpExpressionProviders(),
|
|
6666
|
-
...asyncExpressionProviders(),
|
|
6667
|
-
...propertyDerivationProviders(),
|
|
6668
|
-
...derivationProviders(),
|
|
6669
|
-
];
|
|
6670
|
-
}
|
|
6671
|
-
|
|
6672
|
-
class EventDispatcher {
|
|
6673
|
-
bus;
|
|
6674
|
-
dispatch(event) {
|
|
6675
|
-
this.bus?.emitInstance(event);
|
|
6676
|
-
}
|
|
6677
|
-
connect(bus) {
|
|
6678
|
-
this.bus = bus;
|
|
6679
|
-
}
|
|
6680
|
-
disconnect() {
|
|
6681
|
-
this.bus = undefined;
|
|
6682
|
-
}
|
|
6683
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: EventDispatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
6684
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: EventDispatcher });
|
|
6685
|
-
}
|
|
6686
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: EventDispatcher, decorators: [{
|
|
6687
|
-
type: Injectable
|
|
6688
|
-
}] });
|
|
6689
|
-
|
|
6690
|
-
class DfTemplate {
|
|
6691
|
-
dfTemplate = input.required(...(ngDevMode ? [{ debugName: "dfTemplate" }] : /* istanbul ignore next */ []));
|
|
6692
|
-
templateRef = inject(TemplateRef);
|
|
6693
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
6694
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: DfTemplate, isStandalone: true, selector: "ng-template[dfTemplate]", inputs: { dfTemplate: { classPropertyName: "dfTemplate", publicName: "dfTemplate", isSignal: true, isRequired: true, transformFunction: null } }, exportAs: ["dfTemplate"], ngImport: i0 });
|
|
6695
|
-
}
|
|
6696
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfTemplate, decorators: [{
|
|
6697
|
-
type: Directive,
|
|
6698
|
-
args: [{
|
|
6699
|
-
selector: 'ng-template[dfTemplate]',
|
|
6700
|
-
exportAs: 'dfTemplate',
|
|
6701
|
-
}]
|
|
6702
|
-
}], propDecorators: { dfTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "dfTemplate", required: true }] }] } });
|
|
6703
|
-
class DfFieldTemplateRegistry {
|
|
6704
|
-
_map = signal(new Map(), ...(ngDevMode ? [{ debugName: "_map" }] : /* istanbul ignore next */ []));
|
|
6705
|
-
map = this._map.asReadonly();
|
|
6706
|
-
set(value) {
|
|
6707
|
-
this._map.set(value);
|
|
6708
|
-
}
|
|
6709
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfFieldTemplateRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
6710
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfFieldTemplateRegistry });
|
|
4429
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfFieldTemplateRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4430
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfFieldTemplateRegistry });
|
|
6711
4431
|
}
|
|
6712
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
4432
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfFieldTemplateRegistry, decorators: [{
|
|
6713
4433
|
type: Injectable
|
|
6714
4434
|
}] });
|
|
6715
4435
|
|
|
6716
4436
|
class DynamicForm {
|
|
6717
4437
|
config = input.required({ ...(ngDevMode ? { debugName: "config" } : /* istanbul ignore next */ {}), alias: 'dynamic-form' });
|
|
6718
|
-
formOptions = input(undefined,
|
|
6719
|
-
|
|
6720
|
-
|
|
4438
|
+
formOptions = input(undefined, /* @ts-ignore */
|
|
4439
|
+
...(ngDevMode ? [{ debugName: "formOptions" }] : /* istanbul ignore next */ []));
|
|
4440
|
+
value = model(undefined, /* @ts-ignore */
|
|
4441
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
4442
|
+
source = input('inline', /* @ts-ignore */
|
|
4443
|
+
...(ngDevMode ? [{ debugName: "source" }] : /* istanbul ignore next */ []));
|
|
6721
4444
|
destroyRef = inject(DestroyRef);
|
|
6722
4445
|
injector = inject(Injector);
|
|
6723
4446
|
environmentInjector = inject(EnvironmentInjector);
|
|
@@ -6725,7 +4448,8 @@ class DynamicForm {
|
|
|
6725
4448
|
logger = inject(DynamicFormLogger);
|
|
6726
4449
|
dispatcher = inject(EventDispatcher, { optional: true });
|
|
6727
4450
|
templateRegistry = inject(DfFieldTemplateRegistry);
|
|
6728
|
-
_projectedTemplates = contentChildren(DfTemplate,
|
|
4451
|
+
_projectedTemplates = contentChildren(DfTemplate, /* @ts-ignore */
|
|
4452
|
+
...(ngDevMode ? [{ debugName: "_projectedTemplates" }] : /* istanbul ignore next */ []));
|
|
6729
4453
|
stateManager = this.connectDeps();
|
|
6730
4454
|
formIdPrefix = inject(FormIdPrefixService);
|
|
6731
4455
|
idPrefix = this.formIdPrefix.prefix;
|
|
@@ -6747,12 +4471,15 @@ class DynamicForm {
|
|
|
6747
4471
|
disabled = this.stateManager.disabled;
|
|
6748
4472
|
submitting = this.stateManager.submitting;
|
|
6749
4473
|
fieldLoadingErrors = this.stateManager.fieldLoadingErrors;
|
|
6750
|
-
|
|
4474
|
+
derivationReady = inject(DERIVATION_RENDER_GATE);
|
|
4475
|
+
shouldRender = computed(() => this.stateManager.shouldRender() && this.derivationReady(), /* @ts-ignore */
|
|
4476
|
+
...(ngDevMode ? [{ debugName: "shouldRender" }] : /* istanbul ignore next */ []));
|
|
6751
4477
|
resolvedFields = this.stateManager.resolvedFields;
|
|
6752
4478
|
totalComponentsCount = computed(() => {
|
|
6753
4479
|
const fields = this.stateManager.formSetup()?.fields ?? [];
|
|
6754
4480
|
return countContainersRecursive(fields) + 1;
|
|
6755
|
-
},
|
|
4481
|
+
}, /* @ts-ignore */
|
|
4482
|
+
...(ngDevMode ? [{ debugName: "totalComponentsCount" }] : /* istanbul ignore next */ []));
|
|
6756
4483
|
initialized$ = setupInitializationTracking({
|
|
6757
4484
|
eventBus: this.eventBus,
|
|
6758
4485
|
totalComponentsCount: this.totalComponentsCount,
|
|
@@ -6821,8 +4548,8 @@ class DynamicForm {
|
|
|
6821
4548
|
error: (err) => this.logger.error('Submission handler error', err),
|
|
6822
4549
|
});
|
|
6823
4550
|
}
|
|
6824
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
6825
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
4551
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DynamicForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4552
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: DynamicForm, isStandalone: true, selector: "form[dynamic-form]", inputs: { config: { classPropertyName: "config", publicName: "dynamic-form", isSignal: true, isRequired: true, transformFunction: null }, formOptions: { classPropertyName: "formOptions", publicName: "formOptions", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", validityChange: "validityChange", dirtyChange: "dirtyChange", submitted: "submitted", reset: "reset", cleared: "cleared", events: "events", initialized: "initialized", onPageChange: "onPageChange", onPageNavigationStateChange: "onPageNavigationStateChange" }, host: { attributes: { "novalidate": "" }, listeners: { "submit": "onNativeSubmit($event)" }, properties: { "class.disabled": "disabled()", "class.df-form-paged": "formModeDetection().mode === \"paged\"", "class.df-form-non-paged": "formModeDetection().mode === \"non-paged\"", "attr.data-form-mode": "formModeDetection().mode", "attr.id": "idPrefix() || null" }, classAttribute: "df-dynamic-form df-form" }, providers: [
|
|
6826
4553
|
provideDynamicFormDI(),
|
|
6827
4554
|
DfFieldTemplateRegistry,
|
|
6828
4555
|
{ provide: DF_FIELD_TEMPLATES, useFactory: () => inject(DfFieldTemplateRegistry).map },
|
|
@@ -6850,7 +4577,7 @@ class DynamicForm {
|
|
|
6850
4577
|
}
|
|
6851
4578
|
`, isInline: true, styles: [".df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap, .5rem);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-sm, .5rem)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns, 12),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-md, .5rem)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-lg, .5rem)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-xl, .5rem)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}:host>*{grid-column:1/-1}:host.df-form-spacing-custom{--df-grid-row-gap: var(--df-form-custom-spacing, 1.5rem)}:host.df-form-gap-none{--df-grid-row-gap: 0}:host.df-form-gap-xs{--df-grid-row-gap: .25rem}:host.df-form-gap-sm{--df-grid-row-gap: .5rem}:host.df-form-gap-md{--df-grid-row-gap: 1rem}:host.df-form-gap-lg{--df-grid-row-gap: 1.5rem}:host.df-form-gap-xl{--df-grid-row-gap: 2rem}:host.df-form-paged{gap:0}\n"], dependencies: [{ kind: "directive", type: DfFieldOutlet, selector: "[dfFieldOutlet]", inputs: ["dfFieldOutlet", "dfFieldOutletEnvironmentInjector"] }, { kind: "component", type: PageOrchestratorComponent, selector: "div[page-orchestrator]", inputs: ["pageFields", "form", "fieldSignalContext"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6852
4579
|
}
|
|
6853
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
4580
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DynamicForm, decorators: [{
|
|
6854
4581
|
type: Component,
|
|
6855
4582
|
args: [{ selector: 'form[dynamic-form]', imports: [DfFieldOutlet, PageOrchestratorComponent], template: `
|
|
6856
4583
|
@if (shouldRender()) {
|
|
@@ -7006,35 +4733,35 @@ const DISPLAY_FIELD_TYPES_BASE = {
|
|
|
7006
4733
|
const BUILT_IN_FIELDS = [
|
|
7007
4734
|
{
|
|
7008
4735
|
name: 'row',
|
|
7009
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-
|
|
4736
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-BGFguOa9.mjs'),
|
|
7010
4737
|
mapper: rowFieldMapper,
|
|
7011
4738
|
valueHandling: 'flatten',
|
|
7012
4739
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
7013
4740
|
},
|
|
7014
4741
|
{
|
|
7015
4742
|
name: 'group',
|
|
7016
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-group-field.component-
|
|
4743
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-group-field.component-Bz4GD8Hy.mjs'),
|
|
7017
4744
|
mapper: groupFieldMapper,
|
|
7018
4745
|
valueHandling: 'include',
|
|
7019
4746
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
7020
4747
|
},
|
|
7021
4748
|
{
|
|
7022
4749
|
name: 'array',
|
|
7023
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-array-field.component-
|
|
4750
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-array-field.component-By12r5Cj.mjs'),
|
|
7024
4751
|
mapper: arrayFieldMapper,
|
|
7025
4752
|
valueHandling: 'include',
|
|
7026
4753
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
7027
4754
|
},
|
|
7028
4755
|
{
|
|
7029
4756
|
name: 'page',
|
|
7030
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-
|
|
4757
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-CjCd5Byo.mjs'),
|
|
7031
4758
|
mapper: pageFieldMapper,
|
|
7032
4759
|
valueHandling: 'flatten',
|
|
7033
4760
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
7034
4761
|
},
|
|
7035
4762
|
{
|
|
7036
4763
|
name: 'text',
|
|
7037
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-
|
|
4764
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-CNCpmf5_.mjs'),
|
|
7038
4765
|
mapper: textFieldMapper,
|
|
7039
4766
|
valueHandling: 'exclude',
|
|
7040
4767
|
...DISPLAY_FIELD_TYPES_BASE,
|
|
@@ -7045,7 +4772,7 @@ const BUILT_IN_FIELDS = [
|
|
|
7045
4772
|
},
|
|
7046
4773
|
{
|
|
7047
4774
|
name: 'container',
|
|
7048
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-
|
|
4775
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-BGFguOa9.mjs'),
|
|
7049
4776
|
mapper: containerFieldMapper,
|
|
7050
4777
|
valueHandling: 'flatten',
|
|
7051
4778
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
@@ -7054,16 +4781,47 @@ const BUILT_IN_FIELDS = [
|
|
|
7054
4781
|
const BUILT_IN_WRAPPERS = [
|
|
7055
4782
|
{
|
|
7056
4783
|
wrapperName: 'css',
|
|
7057
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-css-wrapper.component-
|
|
4784
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-css-wrapper.component-AScQAJTQ.mjs'),
|
|
7058
4785
|
},
|
|
7059
4786
|
{
|
|
7060
4787
|
wrapperName: 'row',
|
|
7061
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-row-wrapper.component-
|
|
4788
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-row-wrapper.component-BlVa801t.mjs'),
|
|
7062
4789
|
},
|
|
7063
4790
|
];
|
|
7064
4791
|
|
|
4792
|
+
function isDynamicFormFeature(value) {
|
|
4793
|
+
return (typeof value === 'object' &&
|
|
4794
|
+
value !== null &&
|
|
4795
|
+
'ɵkind' in value &&
|
|
4796
|
+
typeof value.ɵkind === 'string' &&
|
|
4797
|
+
'ɵproviders' in value &&
|
|
4798
|
+
Array.isArray(value.ɵproviders));
|
|
4799
|
+
}
|
|
4800
|
+
function createFeature(kind, providers) {
|
|
4801
|
+
return {
|
|
4802
|
+
ɵkind: kind,
|
|
4803
|
+
ɵproviders: providers,
|
|
4804
|
+
};
|
|
4805
|
+
}
|
|
4806
|
+
|
|
7065
4807
|
const ADDON_KIND_DEFINITIONS = new InjectionToken('ADDON_KIND_DEFINITIONS');
|
|
7066
4808
|
|
|
4809
|
+
class ConsoleLogger {
|
|
4810
|
+
prefix = '[Dynamic Forms]';
|
|
4811
|
+
debug(message, ...args) {
|
|
4812
|
+
console.debug(this.prefix, message, ...args);
|
|
4813
|
+
}
|
|
4814
|
+
info(message, ...args) {
|
|
4815
|
+
console.info(this.prefix, message, ...args);
|
|
4816
|
+
}
|
|
4817
|
+
warn(message, ...args) {
|
|
4818
|
+
console.warn(this.prefix, message, ...args);
|
|
4819
|
+
}
|
|
4820
|
+
error(message, ...args) {
|
|
4821
|
+
console.error(this.prefix, message, ...args);
|
|
4822
|
+
}
|
|
4823
|
+
}
|
|
4824
|
+
|
|
7067
4825
|
function createWrappers(...registrations) {
|
|
7068
4826
|
return {
|
|
7069
4827
|
ɵkind: 'wrappers',
|
|
@@ -7166,6 +4924,28 @@ function provideDynamicForm(...items) {
|
|
|
7166
4924
|
]);
|
|
7167
4925
|
}
|
|
7168
4926
|
|
|
4927
|
+
const DERIVATION_LOG_CONFIG = new InjectionToken('DERIVATION_LOG_CONFIG', {
|
|
4928
|
+
providedIn: 'root',
|
|
4929
|
+
factory: createDefaultDerivationLogConfig,
|
|
4930
|
+
});
|
|
4931
|
+
function withLoggerConfig(config = true) {
|
|
4932
|
+
if (typeof config === 'boolean' || typeof config === 'function') {
|
|
4933
|
+
const isEnabled = typeof config === 'function' ? config() : config;
|
|
4934
|
+
const logger = isEnabled ? new ConsoleLogger() : new NoopLogger();
|
|
4935
|
+
return createFeature('logger', [{ provide: DynamicFormLogger, useValue: logger }]);
|
|
4936
|
+
}
|
|
4937
|
+
const isEnabled = config.enabled !== false;
|
|
4938
|
+
const logger = isEnabled ? new ConsoleLogger() : new NoopLogger();
|
|
4939
|
+
const providers = [{ provide: DynamicFormLogger, useValue: logger }];
|
|
4940
|
+
if (config.derivations !== undefined) {
|
|
4941
|
+
providers.push({
|
|
4942
|
+
provide: DERIVATION_LOG_CONFIG,
|
|
4943
|
+
useValue: { level: config.derivations },
|
|
4944
|
+
});
|
|
4945
|
+
}
|
|
4946
|
+
return createFeature('logger', providers);
|
|
4947
|
+
}
|
|
4948
|
+
|
|
7169
4949
|
function withEventFormValue() {
|
|
7170
4950
|
return createFeature('event-form-value', [{ provide: EMIT_FORM_VALUE_ON_EVENTS, useValue: true }]);
|
|
7171
4951
|
}
|
|
@@ -7198,21 +4978,29 @@ class DfAddonSlot {
|
|
|
7198
4978
|
registry = injectAddonKindRegistry();
|
|
7199
4979
|
logger = inject(DynamicFormLogger);
|
|
7200
4980
|
hostInjector = inject(Injector);
|
|
7201
|
-
addon = input.required(
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
4981
|
+
addon = input.required(/* @ts-ignore */
|
|
4982
|
+
...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
|
|
4983
|
+
fieldInputs = input(/* @ts-ignore */
|
|
4984
|
+
...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
|
|
4985
|
+
hidden = input(undefined, /* @ts-ignore */
|
|
4986
|
+
...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
|
|
4987
|
+
slotAttr = computed(() => this.addon().slot, /* @ts-ignore */
|
|
4988
|
+
...(ngDevMode ? [{ debugName: "slotAttr" }] : /* istanbul ignore next */ []));
|
|
4989
|
+
className = computed(() => this.addon().className ?? null, /* @ts-ignore */
|
|
4990
|
+
...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
|
|
7206
4991
|
_hiddenByValue = new WeakMap();
|
|
7207
|
-
_resolvedHidden = signal(undefined,
|
|
4992
|
+
_resolvedHidden = signal(undefined, /* @ts-ignore */
|
|
4993
|
+
...(ngDevMode ? [{ debugName: "_resolvedHidden" }] : /* istanbul ignore next */ []));
|
|
7208
4994
|
isHidden = computed(() => {
|
|
7209
4995
|
const pre = this.hidden();
|
|
7210
4996
|
if (pre)
|
|
7211
4997
|
return pre();
|
|
7212
4998
|
const local = this._resolvedHidden();
|
|
7213
4999
|
return local ? local() : false;
|
|
7214
|
-
},
|
|
7215
|
-
|
|
5000
|
+
}, /* @ts-ignore */
|
|
5001
|
+
...(ngDevMode ? [{ debugName: "isHidden" }] : /* istanbul ignore next */ []));
|
|
5002
|
+
asyncLoaded = signal(undefined, /* @ts-ignore */
|
|
5003
|
+
...(ngDevMode ? [{ debugName: "asyncLoaded" }] : /* istanbul ignore next */ []));
|
|
7216
5004
|
resolvedComponent = computed(() => {
|
|
7217
5005
|
const kind = this.addon().kind;
|
|
7218
5006
|
const cached = this.registry.getLoadedKindComponent(kind);
|
|
@@ -7220,11 +5008,13 @@ class DfAddonSlot {
|
|
|
7220
5008
|
return cached;
|
|
7221
5009
|
const loaded = this.asyncLoaded();
|
|
7222
5010
|
return loaded?.kind === kind ? loaded.component : undefined;
|
|
7223
|
-
},
|
|
5011
|
+
}, /* @ts-ignore */
|
|
5012
|
+
...(ngDevMode ? [{ debugName: "resolvedComponent" }] : /* istanbul ignore next */ []));
|
|
7224
5013
|
kindInputs = computed(() => ({
|
|
7225
5014
|
addon: this.addon(),
|
|
7226
5015
|
fieldInputs: this.fieldInputs(),
|
|
7227
|
-
}),
|
|
5016
|
+
}), /* @ts-ignore */
|
|
5017
|
+
...(ngDevMode ? [{ debugName: "kindInputs" }] : /* istanbul ignore next */ []));
|
|
7228
5018
|
constructor() {
|
|
7229
5019
|
explicitEffect([this.addon], ([addon]) => {
|
|
7230
5020
|
const h = addon.hidden;
|
|
@@ -7249,14 +5039,14 @@ class DfAddonSlot {
|
|
|
7249
5039
|
});
|
|
7250
5040
|
});
|
|
7251
5041
|
}
|
|
7252
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
7253
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
5042
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfAddonSlot, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5043
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: DfAddonSlot, isStandalone: true, selector: "df-addon-slot", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.slot": "slotAttr()", "class": "className()", "style.display": "isHidden() ? \"none\" : null" } }, ngImport: i0, template: `
|
|
7254
5044
|
@if (resolvedComponent(); as cmp) {
|
|
7255
5045
|
<ng-container *ngComponentOutlet="cmp; inputs: kindInputs()" />
|
|
7256
5046
|
}
|
|
7257
5047
|
`, isInline: true, dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7258
5048
|
}
|
|
7259
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
5049
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfAddonSlot, decorators: [{
|
|
7260
5050
|
type: Component,
|
|
7261
5051
|
args: [{
|
|
7262
5052
|
selector: 'df-addon-slot',
|
|
@@ -7276,12 +5066,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
7276
5066
|
}], ctorParameters: () => [], propDecorators: { addon: [{ type: i0.Input, args: [{ isSignal: true, alias: "addon", required: true }] }], fieldInputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldInputs", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }] } });
|
|
7277
5067
|
|
|
7278
5068
|
class TextAddonComponent {
|
|
7279
|
-
addon = input.required(
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
}
|
|
7284
|
-
i0.ɵɵ
|
|
5069
|
+
addon = input.required(/* @ts-ignore */
|
|
5070
|
+
...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
|
|
5071
|
+
fieldInputs = input(/* @ts-ignore */
|
|
5072
|
+
...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
|
|
5073
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TextAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5074
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.0", type: TextAddonComponent, isStandalone: true, selector: "df-text-addon", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-hidden": "\"true\"" } }, ngImport: i0, template: `<span>{{ addon().text | dynamicText | async }}</span>`, isInline: true, dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5075
|
+
}
|
|
5076
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TextAddonComponent, decorators: [{
|
|
7285
5077
|
type: Component,
|
|
7286
5078
|
args: [{
|
|
7287
5079
|
selector: 'df-text-addon',
|
|
@@ -7302,14 +5094,18 @@ var textAddon_component = /*#__PURE__*/Object.freeze({
|
|
|
7302
5094
|
class TemplateAddonComponent {
|
|
7303
5095
|
templates = inject(DF_FIELD_TEMPLATES, { optional: true });
|
|
7304
5096
|
logger = inject(DynamicFormLogger);
|
|
7305
|
-
addon = input.required(
|
|
7306
|
-
|
|
5097
|
+
addon = input.required(/* @ts-ignore */
|
|
5098
|
+
...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
|
|
5099
|
+
fieldInputs = input(/* @ts-ignore */
|
|
5100
|
+
...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
|
|
7307
5101
|
template = computed(() => {
|
|
7308
5102
|
const map = this.templates?.();
|
|
7309
5103
|
const key = this.addon().templateKey;
|
|
7310
5104
|
return map?.get(key) ?? null;
|
|
7311
|
-
},
|
|
7312
|
-
|
|
5105
|
+
}, /* @ts-ignore */
|
|
5106
|
+
...(ngDevMode ? [{ debugName: "template" }] : /* istanbul ignore next */ []));
|
|
5107
|
+
outletContext = computed(() => ({ $implicit: this.fieldInputs() }), /* @ts-ignore */
|
|
5108
|
+
...(ngDevMode ? [{ debugName: "outletContext" }] : /* istanbul ignore next */ []));
|
|
7313
5109
|
constructor() {
|
|
7314
5110
|
explicitEffect([this.template, this.addon], ([tpl, addon]) => {
|
|
7315
5111
|
if (!tpl) {
|
|
@@ -7317,14 +5113,14 @@ class TemplateAddonComponent {
|
|
|
7317
5113
|
}
|
|
7318
5114
|
});
|
|
7319
5115
|
}
|
|
7320
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
7321
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
5116
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TemplateAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5117
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: TemplateAddonComponent, isStandalone: true, selector: "df-template-addon", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
7322
5118
|
@if (template(); as tpl) {
|
|
7323
5119
|
<ng-container *ngTemplateOutlet="tpl; context: outletContext()" />
|
|
7324
5120
|
}
|
|
7325
5121
|
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7326
5122
|
}
|
|
7327
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
5123
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TemplateAddonComponent, decorators: [{
|
|
7328
5124
|
type: Component,
|
|
7329
5125
|
args: [{
|
|
7330
5126
|
selector: 'df-template-addon',
|
|
@@ -7345,15 +5141,20 @@ var templateAddon_component = /*#__PURE__*/Object.freeze({
|
|
|
7345
5141
|
|
|
7346
5142
|
class ComponentAddonComponent {
|
|
7347
5143
|
logger = inject(DynamicFormLogger);
|
|
7348
|
-
addon = input.required(
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
5144
|
+
addon = input.required(/* @ts-ignore */
|
|
5145
|
+
...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
|
|
5146
|
+
fieldInputs = input(/* @ts-ignore */
|
|
5147
|
+
...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
|
|
5148
|
+
inputs = computed(() => (this.addon().inputs ?? {}), /* @ts-ignore */
|
|
5149
|
+
...(ngDevMode ? [{ debugName: "inputs" }] : /* istanbul ignore next */ []));
|
|
5150
|
+
asyncLoaded = signal(undefined, /* @ts-ignore */
|
|
5151
|
+
...(ngDevMode ? [{ debugName: "asyncLoaded" }] : /* istanbul ignore next */ []));
|
|
7352
5152
|
resolvedComponent = computed(() => {
|
|
7353
5153
|
const current = this.addon();
|
|
7354
5154
|
const loaded = this.asyncLoaded();
|
|
7355
5155
|
return loaded?.addon === current ? loaded.component : undefined;
|
|
7356
|
-
},
|
|
5156
|
+
}, /* @ts-ignore */
|
|
5157
|
+
...(ngDevMode ? [{ debugName: "resolvedComponent" }] : /* istanbul ignore next */ []));
|
|
7357
5158
|
constructor() {
|
|
7358
5159
|
explicitEffect([this.addon], ([addon]) => {
|
|
7359
5160
|
Promise.resolve(addon.component())
|
|
@@ -7367,14 +5168,14 @@ class ComponentAddonComponent {
|
|
|
7367
5168
|
});
|
|
7368
5169
|
});
|
|
7369
5170
|
}
|
|
7370
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
7371
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
5171
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ComponentAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5172
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: ComponentAddonComponent, isStandalone: true, selector: "df-component-addon", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
7372
5173
|
@if (resolvedComponent(); as cmp) {
|
|
7373
5174
|
<ng-container *ngComponentOutlet="cmp; inputs: inputs()" />
|
|
7374
5175
|
}
|
|
7375
5176
|
`, isInline: true, dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7376
5177
|
}
|
|
7377
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
5178
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ComponentAddonComponent, decorators: [{
|
|
7378
5179
|
type: Component,
|
|
7379
5180
|
args: [{
|
|
7380
5181
|
selector: 'df-component-addon',
|
|
@@ -7620,5 +5421,5 @@ const field = createField;
|
|
|
7620
5421
|
|
|
7621
5422
|
|
|
7622
5423
|
|
|
7623
|
-
export {
|
|
7624
|
-
//# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-
|
|
5424
|
+
export { injectAddonKindRegistry as $, ArrayItemRegistryService as A, BUILT_IN_FIELDS as B, CONTAINER_FIELD_PROCESSORS as C, DfFieldOutlet as D, EventDispatcher as E, FormStateManager as F, FormClearEvent as G, FormResetEvent as H, InsertArrayItemEvent as I, PageChangeEvent as J, PopArrayItemEvent as K, PrependArrayItemEvent as L, MoveArrayItemEvent as M, NextPageEvent as N, PreviousPageEvent as O, PROPERTY_OVERRIDE_STORE as P, SubmitEvent as Q, RemoveAtIndexEvent as R, ShiftArrayItemEvent as S, TemplateAddonComponent as T, TextAddonComponent as U, arrayEvent as V, createField as W, createWrappers as X, field as Y, formConfig as Z, formatAddonWarning as _, isSameWrapperChain as a, injectFieldsSupportingAddons as a0, isWrappersBundle as a1, logAddonWarnings as a2, provideAddonActions as a3, provideDynamicForm as a4, runPresetAction as a5, sanitizeFormConfig as a6, sanitizeFormConfigPure as a7, validateFieldAddons as a8, walkAndValidateAddons as a9, withCustomAddon as aa, withEventFormValue as ab, withLegacyStatusClasses as ac, withLoggerConfig as ad, withValidationExecutionDefaults as ae, withValueExclusionDefaults as af, wrapperProps as ag, createWrapperChainController as b, createFieldResolutionPipe as c, createSchemaFromFields as d, createHiddenSignal as e, createRenderReadySignal as f, getNormalizedArrayMetadata as g, getFieldDefaultValue as h, injectFieldRegistry as i, DEV_MODE as j, DEFAULT_DEBOUNCE_MS as k, DERIVATION_WARNING_TRACKER as l, mapFieldToInputs as m, DERIVATION_LOG_CONFIG as n, ADDON_ACTION_HANDLERS as o, ADDON_ACTION_REGISTRY as p, ADDON_KIND_COMPONENT_CACHE as q, resolveWrappers as r, ADDON_KIND_DEFINITIONS as s, traverseFieldsWithContext as t, AppendArrayItemEvent as u, ComponentAddonComponent as v, ConsoleLogger as w, DfAddonSlot as x, DfTemplate as y, DynamicForm as z };
|
|
5425
|
+
//# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-DTkMZcnb.mjs.map
|