@ng-forge/dynamic-forms 1.2.0-next.6 → 1.2.0-next.8
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-NTHLPjau.mjs → ng-forge-dynamic-forms-array-field.component-6yyDriVB.mjs} +6 -10
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-6yyDriVB.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CNCeWAeJ.mjs → ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-BB8jAcY4.mjs} +29 -7
- package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-BB8jAcY4.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-container-field.component-ByGdzAsk.mjs → ng-forge-dynamic-forms-container-field.component-DfQ0y-77.mjs} +7 -11
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-DfQ0y-77.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-debounce-C9F4v1Ke.mjs +17 -0
- package/fesm2022/ng-forge-dynamic-forms-debounce-C9F4v1Ke.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-form-schema.service-BzbBv-r0.mjs +24 -0
- package/fesm2022/ng-forge-dynamic-forms-form-schema.service-BzbBv-r0.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-group-field.component-DnLZdj8v.mjs → ng-forge-dynamic-forms-group-field.component-DxHD5I8E.mjs} +6 -10
- package/fesm2022/ng-forge-dynamic-forms-group-field.component-DxHD5I8E.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-internal.mjs +36 -5
- package/fesm2022/ng-forge-dynamic-forms-internal.mjs.map +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-B2C_WAMP.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-tA8ZkP8i.mjs} +1399 -1354
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-tA8ZkP8i.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-page-field.component-CwH22lRl.mjs → ng-forge-dynamic-forms-page-field.component-DCdBvJ-h.mjs} +54 -13
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-DCdBvJ-h.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-schema-builder-IsjNBo0h.mjs +570 -0
- package/fesm2022/ng-forge-dynamic-forms-schema-builder-IsjNBo0h.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms.mjs +1 -1
- package/package.json +2 -2
- package/types/ng-forge-dynamic-forms-internal.d.ts +25 -6
- package/types/ng-forge-dynamic-forms.d.ts +80 -19
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-NTHLPjau.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CNCeWAeJ.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-ByGdzAsk.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs +0 -24
- package/fesm2022/ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-emit-initialization-DhHwU6FJ.mjs +0 -17
- package/fesm2022/ng-forge-dynamic-forms-emit-initialization-DhHwU6FJ.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-group-field.component-DnLZdj8v.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-B2C_WAMP.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-CwH22lRl.mjs.map +0 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Service } from '@angular/core';
|
|
3
|
+
import { DynamicFormError } from '@ng-forge/dynamic-forms/internal';
|
|
4
|
+
import { c as createSchemaFromFields, a as createFormLevelSchema } from './ng-forge-dynamic-forms-schema-builder-IsjNBo0h.mjs';
|
|
5
|
+
|
|
6
|
+
class FormSchemaService {
|
|
7
|
+
create({ fields, registry, formLevelSchema, validateWhenHidden }) {
|
|
8
|
+
if (fields.length > 0) {
|
|
9
|
+
return createSchemaFromFields(fields, registry, { formLevelSchema, validateWhenHidden });
|
|
10
|
+
}
|
|
11
|
+
if (formLevelSchema) {
|
|
12
|
+
return createFormLevelSchema(formLevelSchema);
|
|
13
|
+
}
|
|
14
|
+
throw new DynamicFormError('The form schema service was requested without fields or a form-level schema.');
|
|
15
|
+
}
|
|
16
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: FormSchemaService, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
17
|
+
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.1", ngImport: i0, type: FormSchemaService });
|
|
18
|
+
}
|
|
19
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: FormSchemaService, decorators: [{
|
|
20
|
+
type: Service
|
|
21
|
+
}] });
|
|
22
|
+
|
|
23
|
+
export { FormSchemaService as default };
|
|
24
|
+
//# sourceMappingURL=ng-forge-dynamic-forms-form-schema.service-BzbBv-r0.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ng-forge-dynamic-forms-form-schema.service-BzbBv-r0.mjs","mappings":";;;;;AAsBc;;AAEV;AACE;;;AAIA;;AAGF","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms/src/lib/core/form-schema.service.ts"],"sourcesContent":["import { Service } from '@angular/core';\nimport type { Schema } from '@angular/forms/signals';\nimport { DynamicFormError } from '@ng-forge/dynamic-forms/internal';\nimport type { FieldDef, FieldTypeDefinition } from '@ng-forge/dynamic-forms/internal';\nimport type { FormSchema } from '@ng-forge/dynamic-forms/schema';\nimport { createFormLevelSchema } from './form-schema-merger';\nimport { createSchemaFromFields } from './schema-builder';\n\nexport interface FormSchemaRequest {\n readonly fields: FieldDef<unknown>[];\n readonly registry: Map<string, FieldTypeDefinition>;\n readonly formLevelSchema: FormSchema<unknown> | undefined;\n readonly validateWhenHidden: boolean | undefined;\n}\n\n/**\n * Lazy schema compiler requested by a form when its first value-bearing field is encountered.\n *\n * The service is stateless and auto-provided so Angular's `injectAsync()` can keep this module,\n * including form mapping, validators, schemas, and expression logic, out of the eager graph.\n */\n@Service()\nexport default class FormSchemaService {\n create({ fields, registry, formLevelSchema, validateWhenHidden }: FormSchemaRequest): Schema<unknown> {\n if (fields.length > 0) {\n return createSchemaFromFields(fields, registry, { formLevelSchema, validateWhenHidden });\n }\n\n if (formLevelSchema) {\n return createFormLevelSchema(formLevelSchema);\n }\n\n throw new DynamicFormError('The form schema service was requested without fields or a form-level schema.');\n }\n}\n"]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { inject, DestroyRef, Injector, EnvironmentInjector, input, computed, linkedSignal, runInInjectionContext, untracked, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
-
import { i as injectFieldRegistry, C as CONTAINER_FIELD_PROCESSORS,
|
|
3
|
+
import { i as injectFieldRegistry, C as CONTAINER_FIELD_PROCESSORS, c as computeContainerHostClasses, a as createFieldResolutionPipe, s as setupContainerInitEffect, d as initializationComponentPath, D as DfFieldOutlet } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-tA8ZkP8i.mjs';
|
|
4
4
|
import { outputFromObservable, toObservable } from '@angular/core/rxjs-interop';
|
|
5
5
|
import { explicitEffect } from 'ngxtension/explicit-effect';
|
|
6
6
|
import { injectFieldSignalContext, GROUP_CONTEXT, EventBus, DynamicFormLogger, isEqual, FIELD_SIGNAL_CONTEXT, derivedFromDeferred } from '@ng-forge/dynamic-forms/internal';
|
|
7
|
-
import { c as computeContainerHostClasses, s as setupContainerInitEffect } from './ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs';
|
|
8
7
|
import { form } from '@angular/forms/signals';
|
|
8
|
+
import { c as createSchemaFromFields } from './ng-forge-dynamic-forms-schema-builder-IsjNBo0h.mjs';
|
|
9
9
|
|
|
10
10
|
class GroupFieldComponent {
|
|
11
11
|
destroyRef = inject(DestroyRef);
|
|
@@ -142,7 +142,7 @@ class GroupFieldComponent {
|
|
|
142
142
|
this.setupEffects();
|
|
143
143
|
}
|
|
144
144
|
setupEffects() {
|
|
145
|
-
setupContainerInitEffect(this.resolvedFields, this.eventBus, 'group', () => this.field().key, this.injector);
|
|
145
|
+
setupContainerInitEffect(this.resolvedFields, this.eventBus, 'group', () => initializationComponentPath(this.field().key, this.parentGroupContext?.groupPath()), this.injector);
|
|
146
146
|
explicitEffect([this.nestedFieldTree, this.field], ([tree, field]) => {
|
|
147
147
|
if (!tree) {
|
|
148
148
|
this.logger.warn(`Group field "${field.key}" not found in parent form. ` + `Ensure the parent form schema includes this group field.`);
|
|
@@ -152,9 +152,7 @@ class GroupFieldComponent {
|
|
|
152
152
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: GroupFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
153
153
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: GroupFieldComponent, isStandalone: true, selector: "fieldset[group-field]", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { validityChange: "validityChange", dirtyChange: "dirtyChange", submitted: "submitted" }, host: { attributes: { "role": "group" }, properties: { "class": "hostClasses()", "class.disabled": "disabled()", "class.df-container-hidden": "hidden()", "attr.aria-hidden": "hidden() || null", "id": "`${key()}`", "attr.data-testid": "key()" } }, ngImport: i0, template: `
|
|
154
154
|
@for (field of resolvedFields(); track field.key) {
|
|
155
|
-
|
|
156
|
-
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
|
|
157
|
-
}
|
|
155
|
+
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
|
|
158
156
|
}
|
|
159
157
|
`, 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{border:none;margin:0;padding:0;min-width:0;display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}:host>*{grid-column:1/-1}:host.df-container-hidden{display:none}\n"], dependencies: [{ kind: "directive", type: DfFieldOutlet, selector: "[dfFieldOutlet]", inputs: ["dfFieldOutlet", "dfFieldOutletEnvironmentInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
160
158
|
}
|
|
@@ -162,9 +160,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
|
|
|
162
160
|
type: Component,
|
|
163
161
|
args: [{ selector: 'fieldset[group-field]', imports: [DfFieldOutlet], template: `
|
|
164
162
|
@for (field of resolvedFields(); track field.key) {
|
|
165
|
-
|
|
166
|
-
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
|
|
167
|
-
}
|
|
163
|
+
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
|
|
168
164
|
}
|
|
169
165
|
`, host: {
|
|
170
166
|
'[class]': 'hostClasses()',
|
|
@@ -178,4 +174,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
|
|
|
178
174
|
}], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], validityChange: [{ type: i0.Output, args: ["validityChange"] }], dirtyChange: [{ type: i0.Output, args: ["dirtyChange"] }], submitted: [{ type: i0.Output, args: ["submitted"] }] } });
|
|
179
175
|
|
|
180
176
|
export { GroupFieldComponent, GroupFieldComponent as default };
|
|
181
|
-
//# sourceMappingURL=ng-forge-dynamic-forms-group-field.component-
|
|
177
|
+
//# sourceMappingURL=ng-forge-dynamic-forms-group-field.component-DxHD5I8E.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ng-forge-dynamic-forms-group-field.component-DxHD5I8E.mjs","mappings":";;;;;;;;;AA4Dc;YAKK;;;;AAIA;AACE;AACF;AACA;YAMA;;;;;AAQjB;;;;YAOS;;;;AAIQ;AACf;AACA;AAEA;AACE;AACA;;AAEA;;AAGE;AACA;;;;;;;AAQF;AACA;AACA;AACA;;;;;;;AAOJ,IAKiB;;;AAIb;;AAEA;AACF;AAEE;AACA;AACD;AAGc;AACf;AACE;;AAGE;AACA;;AAGF;AACF;;;;;AAQO;;AACA;;AACA;;AACA;;AACA;;AACA;;AAEQ;AACf;;AAEA;;;;AAIA;;;AAGE;;AAEA;AACE;;;;AAKN,IAMiB;AACf;;;AAGE;AACF;;AAGe;;AAEf;;;AAGC;AACF;;;AAQQ;;;AAQU;AAGf;AACA;;;AAGA;AACE;;;;AAOH;YAQH;;;;AASE;AAQA;;AAIM;;AAIJ;;;AArNe;;;;AAhBlB;;;;AANS;;;;;AASR;AACA;AACA;AACA;AACA;wEAIA;AACA","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms/src/lib/fields/group/group-field.component.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n EnvironmentInjector,\n inject,\n Injector,\n input,\n linkedSignal,\n runInInjectionContext,\n untracked,\n} from '@angular/core';\nimport { DfFieldOutlet } from '../../directives/df-field-outlet/df-field-outlet.directive';\nimport { outputFromObservable, toObservable } from '@angular/core/rxjs-interop';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { derivedFromDeferred } from '@ng-forge/dynamic-forms/internal';\nimport { createFieldResolutionPipe, ResolvedField } from '../../utils/resolve-field/resolve-field';\nimport {\n computeContainerHostClasses,\n initializationComponentPath,\n setupContainerInitEffect,\n} from '../../utils/container-utils/container-utils';\nimport { CONTAINER_FIELD_PROCESSORS } from '../../utils/container-utils/container-field-processors';\nimport { isEqual } from '@ng-forge/dynamic-forms/internal';\nimport { DynamicFormLogger } from '@ng-forge/dynamic-forms/internal';\nimport { GroupField } from '@ng-forge/dynamic-forms/internal';\nimport { injectFieldRegistry } from '../../utils/inject-field-registry/inject-field-registry';\nimport { FieldTree, form } from '@angular/forms/signals';\nimport { FieldDef } from '@ng-forge/dynamic-forms/internal';\nimport { FieldSignalContext, GroupContext } from '@ng-forge/dynamic-forms/internal';\nimport { FIELD_SIGNAL_CONTEXT, GROUP_CONTEXT, injectFieldSignalContext } from '@ng-forge/dynamic-forms/internal';\nimport { createSchemaFromFields } from '../../core/schema-builder';\nimport { EventBus } from '@ng-forge/dynamic-forms/internal';\nimport { FormSubmitEvent } from '../../events/constants/submit.event';\n\n/** Container component for rendering nested form groups. */\n@Component({\n selector: 'fieldset[group-field]',\n imports: [DfFieldOutlet],\n template: `\n @for (field of resolvedFields(); track field.key) {\n <ng-container *dfFieldOutlet=\"field; environmentInjector: environmentInjector\" />\n }\n `,\n styleUrl: './group-field.component.scss',\n host: {\n '[class]': 'hostClasses()',\n role: 'group',\n '[class.disabled]': 'disabled()',\n '[class.df-container-hidden]': 'hidden()',\n '[attr.aria-hidden]': 'hidden() || null',\n // The group element's own id is the plain `key()` — only its DESCENDANTS\n // compose scoped ids via GROUP_CONTEXT (e.g. children of an `address`\n // group render as `address_street`). The group selector is `#address`.\n '[id]': '`${key()}`',\n '[attr.data-testid]': 'key()',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class GroupFieldComponent<TModel extends Record<string, unknown> = Record<string, unknown>> {\n // ─────────────────────────────────────────────────────────────────────────────\n // Dependencies\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly destroyRef = inject(DestroyRef);\n private readonly fieldRegistry = injectFieldRegistry();\n private readonly parentFieldSignalContext = injectFieldSignalContext<TModel>();\n private readonly parentGroupContext = inject(GROUP_CONTEXT, { optional: true });\n private readonly injector = inject(Injector);\n protected readonly environmentInjector = inject(EnvironmentInjector);\n private readonly eventBus = inject(EventBus);\n private readonly logger = inject(DynamicFormLogger);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Memoized Functions\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly fieldProcessors = inject(CONTAINER_FIELD_PROCESSORS);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Inputs\n // ─────────────────────────────────────────────────────────────────────────────\n\n field = input.required<GroupField>();\n key = input.required<string>();\n className = input<string>();\n hidden = input(false);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Computed Signals\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly hostClasses = computed(() => computeContainerHostClasses('group', this.className()));\n\n private readonly rawFieldRegistry = computed(() => this.fieldRegistry.raw);\n\n private readonly formSetup = computed(() => {\n const groupField = this.field();\n const registry = this.rawFieldRegistry();\n\n if (groupField.fields && groupField.fields.length > 0) {\n const flattenedFields = this.fieldProcessors.memoizedFlattenFields(groupField.fields, registry);\n const flattenedFieldsForRendering = this.fieldProcessors.memoizedFlattenFieldsForRendering(groupField.fields, registry);\n const fieldsById = this.fieldProcessors.memoizedKeyBy(flattenedFields);\n const defaultValues = this.fieldProcessors.memoizedDefaultValues(fieldsById, registry);\n\n return {\n fields: flattenedFieldsForRendering,\n schemaFields: flattenedFields,\n originalFields: groupField.fields,\n defaultValues,\n registry,\n };\n }\n\n return {\n fields: [],\n schemaFields: [],\n originalFields: [],\n defaultValues: {},\n registry,\n };\n });\n\n readonly defaultValues = linkedSignal(() => this.formSetup().defaultValues);\n\n /**\n * Entity computed from parent value, group key, and defaults.\n * Uses deep equality check to prevent unnecessary updates when\n * object spread creates new references with identical values.\n */\n private readonly entity = linkedSignal(\n () => {\n const parentValue = this.parentFieldSignalContext.value();\n const groupKey = this.field().key;\n const defaults = this.defaultValues();\n const groupValue = (parentValue as Record<string, unknown>)?.[groupKey] || {};\n return { ...defaults, ...groupValue };\n },\n {\n debugName: 'GroupFieldComponent.entity',\n equal: isEqual,\n },\n );\n\n private readonly form = computed(() => {\n return runInInjectionContext(this.injector, () => {\n const setup = this.formSetup();\n\n if (setup.schemaFields.length > 0) {\n const schema = createSchemaFromFields(setup.schemaFields, setup.registry);\n return untracked(() => form(this.entity, schema));\n }\n\n return untracked(() => form(this.entity));\n });\n });\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Public State Signals\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly formValue = computed(() => this.entity());\n readonly valid = computed(() => this.form()().valid());\n readonly invalid = computed(() => this.form()().invalid());\n readonly dirty = computed(() => this.form()().dirty());\n readonly touched = computed(() => this.form()().touched());\n readonly errors = computed(() => this.form()().errors());\n readonly disabled = computed(() => this.form()().disabled());\n\n private readonly nestedFieldTree = computed((): FieldTree<Record<string, unknown>> | undefined => {\n const parentForm = this.parentFieldSignalContext.form as Record<string, FieldTree<Record<string, unknown>>>;\n const groupKey = this.field().key;\n return parentForm[groupKey];\n });\n\n private readonly groupFieldSignalContext: FieldSignalContext<Record<string, unknown>> = (() => {\n const nestedFieldTree = this.nestedFieldTree;\n return {\n injector: this.injector,\n value: this.parentFieldSignalContext.value,\n defaultValues: this.defaultValues,\n get form() {\n return nestedFieldTree() ?? ({} as FieldTree<Record<string, unknown>>);\n },\n };\n })();\n\n /**\n * Group context propagated to descendants — carries the dot-separated path of\n * group ancestors so child fields can scope their DOM IDs (issue #401).\n * Reactive via `computed()`: a nested group composes its path from the parent\n * context signal + its own key, so the chain stays consistent if either changes.\n */\n private readonly groupContext: GroupContext = {\n groupPath: computed(() => {\n const ownKey = this.field().key;\n const parentPath = this.parentGroupContext?.groupPath();\n return parentPath ? `${parentPath}.${ownKey}` : ownKey;\n }),\n };\n\n private readonly groupInjector = Injector.create({\n parent: this.injector,\n providers: [\n { provide: FIELD_SIGNAL_CONTEXT, useValue: this.groupFieldSignalContext },\n { provide: GROUP_CONTEXT, useValue: this.groupContext },\n ],\n });\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Outputs\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly validityChange = outputFromObservable(toObservable(this.valid));\n readonly dirtyChange = outputFromObservable(toObservable(this.dirty));\n readonly submitted = outputFromObservable(this.eventBus.on<FormSubmitEvent>('submit'));\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Field Resolution\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly fieldsSource = computed(() => this.formSetup().fields);\n\n protected readonly resolvedFields = derivedFromDeferred(\n this.fieldsSource,\n createFieldResolutionPipe(() => ({\n loadTypeComponent: (type: string) => this.fieldRegistry.loadTypeComponent(type),\n registry: this.rawFieldRegistry(),\n injector: this.groupInjector,\n destroyRef: this.destroyRef,\n onError: (fieldDef: FieldDef<unknown>, error: unknown) => {\n const fieldKey = fieldDef.key || '<no key>';\n this.logger.error(\n `Failed to load component for field type '${fieldDef.type}' (key: ${fieldKey}) ` +\n `within group '${this.field().key}'. Ensure the field type is registered in your field registry.`,\n error,\n );\n },\n })),\n { initialValue: [] as ResolvedField[], injector: this.injector },\n );\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Constructor\n // ─────────────────────────────────────────────────────────────────────────────\n\n constructor() {\n this.setupEffects();\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Private Methods\n // ─────────────────────────────────────────────────────────────────────────────\n\n private setupEffects(): void {\n setupContainerInitEffect(\n this.resolvedFields,\n this.eventBus,\n 'group',\n () => initializationComponentPath(this.field().key, this.parentGroupContext?.groupPath()),\n this.injector,\n );\n\n explicitEffect(\n [this.nestedFieldTree, this.field],\n ([tree, field]) => {\n if (!tree) {\n this.logger.warn(\n `Group field \"${field.key}\" not found in parent form. ` + `Ensure the parent form schema includes this group field.`,\n );\n }\n },\n { defer: true },\n );\n }\n}\n\nexport { GroupFieldComponent };\n"]}
|
|
@@ -1576,10 +1576,14 @@ function safeReadPathKeys(fieldContext) {
|
|
|
1576
1576
|
}
|
|
1577
1577
|
|
|
1578
1578
|
function createFieldValueProxy(getRootForm, getRootFormValue) {
|
|
1579
|
-
const
|
|
1579
|
+
const resolveAccessor = (key) => {
|
|
1580
1580
|
const root = getRootForm();
|
|
1581
1581
|
const accessor = root?.[key];
|
|
1582
|
-
|
|
1582
|
+
return typeof accessor === 'function' ? accessor : undefined;
|
|
1583
|
+
};
|
|
1584
|
+
const readField = (key) => {
|
|
1585
|
+
const accessor = resolveAccessor(key);
|
|
1586
|
+
if (accessor) {
|
|
1583
1587
|
const state = untracked(() => accessor());
|
|
1584
1588
|
const valueSig = state && state.value;
|
|
1585
1589
|
if (typeof valueSig === 'function') {
|
|
@@ -1598,8 +1602,7 @@ function createFieldValueProxy(getRootForm, getRootFormValue) {
|
|
|
1598
1602
|
has(_target, prop) {
|
|
1599
1603
|
if (typeof prop === 'symbol')
|
|
1600
1604
|
return false;
|
|
1601
|
-
|
|
1602
|
-
if (root && prop in root)
|
|
1605
|
+
if (resolveAccessor(prop))
|
|
1603
1606
|
return true;
|
|
1604
1607
|
return prop in getRootFormValue();
|
|
1605
1608
|
},
|
|
@@ -3560,6 +3563,10 @@ const WRAPPER_COMPONENT_CACHE = new InjectionToken('WRAPPER_COMPONENT_CACHE', {
|
|
|
3560
3563
|
providedIn: 'root',
|
|
3561
3564
|
factory: () => new Map(),
|
|
3562
3565
|
});
|
|
3566
|
+
const WRAPPER_COMPONENT_LOAD_CACHE = new InjectionToken('WRAPPER_COMPONENT_LOAD_CACHE', {
|
|
3567
|
+
providedIn: 'root',
|
|
3568
|
+
factory: () => new Map(),
|
|
3569
|
+
});
|
|
3563
3570
|
const WRAPPER_AUTO_ASSOCIATIONS = new InjectionToken('WRAPPER_AUTO_ASSOCIATIONS', {
|
|
3564
3571
|
providedIn: 'root',
|
|
3565
3572
|
factory: () => new Map(),
|
|
@@ -3735,6 +3742,14 @@ function setupInitializationTracking(options) {
|
|
|
3735
3742
|
}
|
|
3736
3743
|
|
|
3737
3744
|
function isEqual(a, b) {
|
|
3745
|
+
if (a === b)
|
|
3746
|
+
return true;
|
|
3747
|
+
if (typeof a === 'number' && typeof b === 'number')
|
|
3748
|
+
return Object.is(a, b);
|
|
3749
|
+
if (a == null || b == null)
|
|
3750
|
+
return false;
|
|
3751
|
+
if (typeof a !== 'object' || typeof b !== 'object')
|
|
3752
|
+
return Object.is(a, b);
|
|
3738
3753
|
return isEqualInternal(a, b, new WeakMap(), new WeakMap());
|
|
3739
3754
|
}
|
|
3740
3755
|
function isEqualInternal(a, b, seenA, seenB) {
|
|
@@ -3896,6 +3911,22 @@ function memoize(fn, resolverOrOptions) {
|
|
|
3896
3911
|
function normalizeFieldsArray(fields) {
|
|
3897
3912
|
return Array.isArray(fields) ? [...fields] : Object.values(fields);
|
|
3898
3913
|
}
|
|
3914
|
+
function getChangedKeysWithin(previous, current, keys) {
|
|
3915
|
+
const changed = new Set();
|
|
3916
|
+
if (keys.size === 0)
|
|
3917
|
+
return changed;
|
|
3918
|
+
const before = previous ?? {};
|
|
3919
|
+
const after = current ?? {};
|
|
3920
|
+
for (const key of keys) {
|
|
3921
|
+
const inBefore = key in before;
|
|
3922
|
+
const inAfter = key in after;
|
|
3923
|
+
if (!inBefore && !inAfter)
|
|
3924
|
+
continue;
|
|
3925
|
+
if (inBefore !== inAfter || !isEqual(before[key], after[key]))
|
|
3926
|
+
changed.add(key);
|
|
3927
|
+
}
|
|
3928
|
+
return changed;
|
|
3929
|
+
}
|
|
3899
3930
|
function getChangedKeys(previous, current) {
|
|
3900
3931
|
const changedKeys = new Set();
|
|
3901
3932
|
if (!previous && !current)
|
|
@@ -3952,5 +3983,5 @@ function provideFieldErrorDisplay(wrapper) {
|
|
|
3952
3983
|
|
|
3953
3984
|
|
|
3954
3985
|
|
|
3955
|
-
export { ADDON_ACTION_HANDLERS, ADDON_ACTION_REGISTRY, ADDON_TYPE_COMPONENT_CACHE, ADDON_TYPE_DEFINITIONS, ADDON_TYPE_REGISTRY, ARRAY_CONTEXT, AsyncConditionFunctionCacheService, ComponentInitializedEvent, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, DEFAULT_WRAPPERS, DEPRECATION_WARNING_TRACKER, DF_FIELD_TEMPLATES, DfAddonSlot, DynamicFormError, DynamicFormLogger, DynamicTextPipe, DynamicValueFunctionCacheService, EMIT_FORM_VALUE_ON_EVENTS, EXTERNAL_DATA, EventBus, ExpressionParser, ExpressionParserError, FIELD_ERROR_DISPLAY, FIELD_REGISTRY, FIELD_SIGNAL_CONTEXT, FORM_ID_PREFIX, FORM_OPTIONS, FieldContextRegistryService, FieldErrorsWrapperBase, FunctionRegistryService, GROUP_CONTEXT, HTTP_CONDITION_CACHE, HttpConditionCache, HttpConditionFunctionCacheService, INITIALIZATION_TIMEOUT_MS, LogicFunctionCacheService, NoopLogger, READONLY_FIELD_TREE_CACHE, RootFormRegistryService, TokenType, WRAPPER_AUTO_ASSOCIATIONS, WRAPPER_COMPONENT_CACHE, WRAPPER_REGISTRY, applyMetaToElement, applyValidator, applyValidators, arrayFieldMapper, assertNever, baseFieldMapper, buildBaseInputs, buildClassName, compareValues, containerFieldMapper, createAddonActionRegistry, createDebouncedLogicFunction, createDefaultDerivationLogConfig, createDynamicValueFunction, createFieldValueProxy, createFormFieldStateMap, createFormStateLogicFunction, createInitializationTracker, createLogicFunction, createResolvedErrorsSignal, createWarningTracker, deepMergeDefaults, derivedFromDeferred, detectFormMode, dynamicTextToObservable, evaluateCondition, evaluateNonFieldDisabled, evaluateNonFieldHidden, extractExpressionDependencies, extractStringDependencies, getArrayLength, getChangedKeys, getFieldValueHandling, getGridClassString, getNestedValue, groupFieldMapper, hasChildFields, hasCrossFieldWhenCondition, hasFormValue, hasNestedProperty, hasTargetProperty, injectAddonTypeRegistry, injectFieldErrors, injectFieldSignalContext, injectFieldsSupportingAddons, injectNonFieldEvaluationContext, interpolateParams, isArrayField, isCheckedField, isContainerField, isCrossFieldBuiltInValidator, isCrossFieldExpression, isCrossFieldSchema, isCrossFieldStateLogic, isCrossFieldValidator, isDerivationLogicConfig, isDisplayOnlyField, isEqual, isFieldBoundContext, isFormStateCondition, isFunctionHttpValidator, isGenericContainerField, isGroupField, isHiddenField, isLeafField, isNonPagedForm, isPageField, isPagedForm, isResourceBasedValidator, isRowField, isSimplifiedArrayField, isStateLogicConfig, isValidNonPagedForm, isValidPagedForm, isValueBearingField, isValueField, isWrapperTypeDefinition, keyBy, mapValues, memoize, normalizeFieldsArray, omit, pageFieldMapper, provideFieldErrorDisplay, readFieldStateInfo, resolveDynamicValue, resolveHttpRequest, resolveNextButtonDisabled, resolveNonFieldDisabled, resolveNonFieldHidden, resolveSubmitButtonDisabled, resolveTokens, rowError, rowFieldMapper, runPresetAction, setupInitializationTracking, shouldLog, shouldShowErrors, textFieldMapper, toReadonlyFieldTree, toReadonlyFieldTreeCached, validatePageNesting, withPreviousValue, writeToFieldValue };
|
|
3986
|
+
export { ADDON_ACTION_HANDLERS, ADDON_ACTION_REGISTRY, ADDON_TYPE_COMPONENT_CACHE, ADDON_TYPE_DEFINITIONS, ADDON_TYPE_REGISTRY, ARRAY_CONTEXT, AsyncConditionFunctionCacheService, ComponentInitializedEvent, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, DEFAULT_WRAPPERS, DEPRECATION_WARNING_TRACKER, DF_FIELD_TEMPLATES, DfAddonSlot, DynamicFormError, DynamicFormLogger, DynamicTextPipe, DynamicValueFunctionCacheService, EMIT_FORM_VALUE_ON_EVENTS, EXTERNAL_DATA, EventBus, ExpressionParser, ExpressionParserError, FIELD_ERROR_DISPLAY, FIELD_REGISTRY, FIELD_SIGNAL_CONTEXT, FORM_ID_PREFIX, FORM_OPTIONS, FieldContextRegistryService, FieldErrorsWrapperBase, FunctionRegistryService, GROUP_CONTEXT, HTTP_CONDITION_CACHE, HttpConditionCache, HttpConditionFunctionCacheService, INITIALIZATION_TIMEOUT_MS, LogicFunctionCacheService, NoopLogger, READONLY_FIELD_TREE_CACHE, RootFormRegistryService, TokenType, WRAPPER_AUTO_ASSOCIATIONS, WRAPPER_COMPONENT_CACHE, WRAPPER_COMPONENT_LOAD_CACHE, WRAPPER_REGISTRY, applyMetaToElement, applyValidator, applyValidators, arrayFieldMapper, assertNever, baseFieldMapper, buildBaseInputs, buildClassName, compareValues, containerFieldMapper, createAddonActionRegistry, createDebouncedLogicFunction, createDefaultDerivationLogConfig, createDynamicValueFunction, createFieldValueProxy, createFormFieldStateMap, createFormStateLogicFunction, createInitializationTracker, createLogicFunction, createResolvedErrorsSignal, createWarningTracker, deepMergeDefaults, derivedFromDeferred, detectFormMode, dynamicTextToObservable, evaluateCondition, evaluateNonFieldDisabled, evaluateNonFieldHidden, extractExpressionDependencies, extractStringDependencies, getArrayLength, getChangedKeys, getChangedKeysWithin, getFieldValueHandling, getGridClassString, getNestedValue, groupFieldMapper, hasChildFields, hasCrossFieldWhenCondition, hasFormValue, hasNestedProperty, hasTargetProperty, injectAddonTypeRegistry, injectFieldErrors, injectFieldSignalContext, injectFieldsSupportingAddons, injectNonFieldEvaluationContext, interpolateParams, isArrayField, isCheckedField, isContainerField, isCrossFieldBuiltInValidator, isCrossFieldExpression, isCrossFieldSchema, isCrossFieldStateLogic, isCrossFieldValidator, isDerivationLogicConfig, isDisplayOnlyField, isEqual, isFieldBoundContext, isFormStateCondition, isFunctionHttpValidator, isGenericContainerField, isGroupField, isHiddenField, isLeafField, isNonPagedForm, isPageField, isPagedForm, isResourceBasedValidator, isRowField, isSimplifiedArrayField, isStateLogicConfig, isValidNonPagedForm, isValidPagedForm, isValueBearingField, isValueField, isWrapperTypeDefinition, keyBy, mapValues, memoize, normalizeFieldsArray, omit, pageFieldMapper, provideFieldErrorDisplay, readFieldStateInfo, resolveDynamicValue, resolveHttpRequest, resolveNextButtonDisabled, resolveNonFieldDisabled, resolveNonFieldHidden, resolveSubmitButtonDisabled, resolveTokens, rowError, rowFieldMapper, runPresetAction, setupInitializationTracking, shouldLog, shouldShowErrors, textFieldMapper, toReadonlyFieldTree, toReadonlyFieldTreeCached, validatePageNesting, withPreviousValue, writeToFieldValue };
|
|
3956
3987
|
//# sourceMappingURL=ng-forge-dynamic-forms-internal.mjs.map
|