@ng-forge/dynamic-forms 0.2.0 → 0.3.0

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.
Files changed (20) hide show
  1. package/README.md +6 -4
  2. package/fesm2022/ng-forge-dynamic-forms-integration.mjs +79 -6
  3. package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
  4. package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-CZBdtTAU.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-CMSmISSp.mjs} +301 -72
  5. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CMSmISSp.mjs.map +1 -0
  6. package/fesm2022/{ng-forge-dynamic-forms-page-field.component-BM32StxL.mjs → ng-forge-dynamic-forms-page-field.component-rdGD8A9d.mjs} +5 -5
  7. package/fesm2022/{ng-forge-dynamic-forms-page-field.component-BM32StxL.mjs.map → ng-forge-dynamic-forms-page-field.component-rdGD8A9d.mjs.map} +1 -1
  8. package/fesm2022/{ng-forge-dynamic-forms-testing-test-button.harness-CbctXjtc.mjs → ng-forge-dynamic-forms-testing-test-button.harness-0iXHLraC.mjs} +7 -7
  9. package/fesm2022/ng-forge-dynamic-forms-testing-test-button.harness-0iXHLraC.mjs.map +1 -0
  10. package/fesm2022/ng-forge-dynamic-forms-testing.mjs +25 -25
  11. package/fesm2022/ng-forge-dynamic-forms-testing.mjs.map +1 -1
  12. package/fesm2022/{ng-forge-dynamic-forms-text-field.component-C0I8jf9T.mjs → ng-forge-dynamic-forms-text-field.component-CDxka2KD.mjs} +5 -5
  13. package/fesm2022/{ng-forge-dynamic-forms-text-field.component-C0I8jf9T.mjs.map → ng-forge-dynamic-forms-text-field.component-CDxka2KD.mjs.map} +1 -1
  14. package/fesm2022/ng-forge-dynamic-forms.mjs +1 -1
  15. package/package.json +4 -4
  16. package/types/ng-forge-dynamic-forms-integration.d.ts +225 -9
  17. package/types/ng-forge-dynamic-forms-testing.d.ts +32 -1
  18. package/types/ng-forge-dynamic-forms.d.ts +202 -15
  19. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CZBdtTAU.mjs.map +0 -1
  20. package/fesm2022/ng-forge-dynamic-forms-testing-test-button.harness-CbctXjtc.mjs.map +0 -1
@@ -1,11 +1,12 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { isSignal, computed, runInInjectionContext, InjectionToken, inject, signal, Injectable, untracked, input, linkedSignal, ChangeDetectionStrategy, Component, DestroyRef, Injector, model, afterNextRender, makeEnvironmentProviders, Pipe } from '@angular/core';
3
3
  import { NgComponentOutlet } from '@angular/common';
4
- import { submit, email, min, max, minLength, maxLength, pattern, required, validate, validateAsync, validateHttp, disabled, readonly, hidden, applyEach, applyWhenValue, applyWhen, apply, schema, validateTree, form } from '@angular/forms/signals';
4
+ import { submit, email, min, max, minLength, maxLength, pattern, required, validate, validateAsync, validateHttp, disabled, readonly, hidden, applyEach, applyWhenValue, applyWhen, apply, schema, validateTree, form, provideSignalFormsConfig } from '@angular/forms/signals';
5
5
  import { toObservable, toSignal, takeUntilDestroyed, outputFromObservable } from '@angular/core/rxjs-interop';
6
6
  import { defer, combineLatest, from, map, catchError, of, pipe, switchMap, forkJoin, scan, isObservable, firstValueFrom, EMPTY, Subject, filter } from 'rxjs';
7
7
  import { scan as scan$1, map as map$1, filter as filter$1, take, switchMap as switchMap$1, shareReplay } from 'rxjs/operators';
8
8
  import { explicitEffect } from 'ngxtension/explicit-effect';
9
+ import { NG_STATUS_CLASSES } from '@angular/forms/signals/compat';
9
10
 
10
11
  /**
11
12
  * Type guard to check if a value is an array of Signals.
@@ -93,7 +94,7 @@ function getGridClassString(fieldDef) {
93
94
  * @returns Record of input names to values
94
95
  */
95
96
  function buildBaseInputs(fieldDef) {
96
- const { key, label, className, tabIndex, props } = fieldDef;
97
+ const { key, label, className, tabIndex, props, meta } = fieldDef;
97
98
  const inputs = {};
98
99
  // Always include key - required by components for accessibility and identification
99
100
  inputs['key'] = key;
@@ -118,6 +119,9 @@ function buildBaseInputs(fieldDef) {
118
119
  if (props !== undefined) {
119
120
  inputs['props'] = props;
120
121
  }
122
+ if (meta !== undefined) {
123
+ inputs['meta'] = meta;
124
+ }
121
125
  return inputs;
122
126
  }
123
127
  /**
@@ -135,6 +139,43 @@ function baseFieldMapper(fieldDef) {
135
139
  return computed(() => buildBaseInputs(fieldDef));
136
140
  }
137
141
 
142
+ /**
143
+ * Merges forwarded props into meta, with meta taking precedence.
144
+ *
145
+ * @param inputs The current inputs record
146
+ * @param propsToMeta Array of prop keys to forward to meta
147
+ * @returns Updated inputs record with merged meta
148
+ */
149
+ function mergeForwardedPropsToMeta(inputs, propsToMeta) {
150
+ const props = inputs['props'];
151
+ if (!props || propsToMeta.length === 0) {
152
+ return inputs;
153
+ }
154
+ // Extract values to forward from props
155
+ // Values are typed as FieldMeta-compatible since they're forwarded attributes (type, rows, cols, etc.)
156
+ const forwardedValues = {};
157
+ for (const key of propsToMeta) {
158
+ const value = props[key];
159
+ if (value !== undefined && value !== null) {
160
+ // Props being forwarded are known HTML attributes (string, number, boolean)
161
+ forwardedValues[key] = value;
162
+ }
163
+ }
164
+ // If nothing to forward, return inputs unchanged
165
+ if (Object.keys(forwardedValues).length === 0) {
166
+ return inputs;
167
+ }
168
+ // Merge: forwarded props first, then meta (meta wins)
169
+ const existingMeta = inputs['meta'];
170
+ const mergedMeta = {
171
+ ...forwardedValues,
172
+ ...existingMeta,
173
+ };
174
+ return {
175
+ ...inputs,
176
+ meta: mergedMeta,
177
+ };
178
+ }
138
179
  /**
139
180
  * Main field mapper function that uses the field registry to get the appropriate mapper
140
181
  * based on the field's type property.
@@ -142,30 +183,49 @@ function baseFieldMapper(fieldDef) {
142
183
  * This function must be called within an injection context where FIELD_SIGNAL_CONTEXT
143
184
  * is provided, as mappers inject the context to access form state.
144
185
  *
186
+ * For componentless fields (no loadComponent and no mapper), returns undefined
187
+ * since there's no component to bind inputs to. Callers should check for undefined
188
+ * and skip rendering logic for such fields.
189
+ *
190
+ * If the field type definition specifies `propsToMeta`, the specified props
191
+ * will be merged into the meta object (with meta taking precedence).
192
+ *
145
193
  * @param fieldDef The field definition to map
146
194
  * @param fieldRegistry The registry of field type definitions
147
- * @returns Signal containing Record of input names to values for ngComponentOutlet
195
+ * @returns Signal containing Record of input names to values, or undefined for componentless fields
148
196
  */
149
197
  function mapFieldToInputs(fieldDef, fieldRegistry) {
150
198
  // Get the field type definition from registry
151
199
  const fieldType = fieldRegistry.get(fieldDef.type);
152
- if (fieldType?.mapper) {
153
- // Use the registered mapper for this field type
154
- // Mapper will inject FIELD_SIGNAL_CONTEXT internally
155
- return fieldType.mapper(fieldDef);
200
+ // Componentless field (no mapper and no loadComponent) - nothing to map
201
+ if (fieldType && !fieldType.loadComponent && !fieldType.mapper) {
202
+ return undefined;
203
+ }
204
+ // Get the base mapper result
205
+ const mapperResult = fieldType?.mapper ? fieldType.mapper(fieldDef) : baseFieldMapper(fieldDef);
206
+ // If no props forwarding configured, return as-is
207
+ const propsToMeta = fieldType?.propsToMeta;
208
+ if (!propsToMeta || propsToMeta.length === 0) {
209
+ return mapperResult;
156
210
  }
157
- // Fallback to base mapper
158
- // Mapper will inject FIELD_SIGNAL_CONTEXT internally
159
- return baseFieldMapper(fieldDef);
211
+ // Wrap in computed to merge forwarded props into meta
212
+ return computed(() => {
213
+ const inputs = mapperResult();
214
+ return mergeForwardedPropsToMeta(inputs, propsToMeta);
215
+ });
160
216
  }
161
217
 
162
218
  /**
163
219
  * Resolves a single field definition to a ResolvedField using RxJS.
164
220
  * Loads the component asynchronously and maps inputs in the injection context.
165
221
  *
222
+ * For componentless fields (e.g., hidden fields), returns undefined since
223
+ * there's nothing to render. These fields still contribute to form values
224
+ * through the form schema.
225
+ *
166
226
  * @param fieldDef - The field definition to resolve
167
227
  * @param context - The context containing dependencies for resolution
168
- * @returns Observable that emits ResolvedField or undefined on error
228
+ * @returns Observable that emits ResolvedField or undefined (for componentless fields or on error)
169
229
  */
170
230
  function resolveField(fieldDef, context) {
171
231
  return from(context.loadTypeComponent(fieldDef.type)).pipe(map((component) => {
@@ -173,8 +233,16 @@ function resolveField(fieldDef, context) {
173
233
  if (context.destroyRef.destroyed) {
174
234
  return undefined;
175
235
  }
236
+ // Componentless fields (e.g., hidden) return undefined - nothing to render
237
+ if (!component) {
238
+ return undefined;
239
+ }
176
240
  // Run mapper in injection context
177
241
  const inputs = runInInjectionContext(context.injector, () => mapFieldToInputs(fieldDef, context.registry));
242
+ // Fields with components should always have inputs (componentless fields are handled above)
243
+ if (!inputs) {
244
+ return undefined;
245
+ }
178
246
  return {
179
247
  key: fieldDef.key,
180
248
  component,
@@ -646,18 +714,20 @@ function injectFieldRegistry() {
646
714
  * Handles both synchronous component references and asynchronous
647
715
  * dynamic imports. Automatically extracts default exports from ES modules.
648
716
  *
717
+ * Returns `undefined` for componentless field types (e.g., hidden fields)
718
+ * that only contribute to form values without rendering UI.
719
+ *
649
720
  * @param name - The name of the field type to load
650
- * @returns Promise resolving to the component constructor
721
+ * @returns Promise resolving to the component constructor, or undefined for componentless fields
651
722
  * @throws {Error} When field type is not registered or loading fails
652
723
  *
653
724
  * @example
654
725
  * ```typescript
655
- * try {
656
- * const InputComponent = await fieldRegistry.loadTypeComponent('input');
657
- * const componentRef = vcr.createComponent(InputComponent);
658
- * } catch (error) {
659
- * console.error('Failed to load component:', error);
726
+ * const component = await fieldRegistry.loadTypeComponent('input');
727
+ * if (component) {
728
+ * const componentRef = vcr.createComponent(component);
660
729
  * }
730
+ * // For componentless fields like 'hidden', component will be undefined
661
731
  * ```
662
732
  */
663
733
  async loadTypeComponent(name) {
@@ -665,19 +735,20 @@ function injectFieldRegistry() {
665
735
  if (!fieldType) {
666
736
  throw new DynamicFormError(`Field type "${name}" is not registered`);
667
737
  }
738
+ // Componentless field types (e.g., hidden) don't have loadComponent
739
+ if (!fieldType.loadComponent) {
740
+ return undefined;
741
+ }
668
742
  // Handle async loading
669
- if (fieldType.loadComponent) {
670
- try {
671
- const result = await fieldType.loadComponent();
672
- // Handle ES module imports - extract default export if needed
673
- const moduleResult = result;
674
- return (typeof moduleResult === 'object' && 'default' in moduleResult && moduleResult.default) || result;
675
- }
676
- catch (error) {
677
- throw new DynamicFormError(`Failed to load component for field type "${name}": ${error}`);
678
- }
743
+ try {
744
+ const result = await fieldType.loadComponent();
745
+ // Handle ES module imports - extract default export if needed
746
+ const moduleResult = result;
747
+ return (typeof moduleResult === 'object' && 'default' in moduleResult && moduleResult.default) || result;
748
+ }
749
+ catch (error) {
750
+ throw new DynamicFormError(`Failed to load component for field type "${name}": ${error}`);
679
751
  }
680
- throw new DynamicFormError(`Field type "${name}" has no component or loadComponent function`);
681
752
  },
682
753
  /**
683
754
  * Gets all registered field type definitions.
@@ -901,10 +972,10 @@ class RootFormRegistryService {
901
972
  this._formValueSignals.set(new Map());
902
973
  this.formContexts.clear();
903
974
  }
904
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RootFormRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
905
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RootFormRegistryService });
975
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: RootFormRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
976
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: RootFormRegistryService });
906
977
  }
907
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RootFormRegistryService, decorators: [{
978
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: RootFormRegistryService, decorators: [{
908
979
  type: Injectable
909
980
  }] });
910
981
 
@@ -1028,10 +1099,10 @@ class FieldContextRegistryService {
1028
1099
  logger: this.logger,
1029
1100
  };
1030
1101
  }
1031
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FieldContextRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1032
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FieldContextRegistryService });
1102
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: FieldContextRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1103
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: FieldContextRegistryService });
1033
1104
  }
1034
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FieldContextRegistryService, decorators: [{
1105
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: FieldContextRegistryService, decorators: [{
1035
1106
  type: Injectable
1036
1107
  }] });
1037
1108
 
@@ -1371,10 +1442,10 @@ class FunctionRegistryService {
1371
1442
  this.clearAsyncValidators();
1372
1443
  this.clearHttpValidators();
1373
1444
  }
1374
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FunctionRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1375
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FunctionRegistryService });
1445
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: FunctionRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1446
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: FunctionRegistryService });
1376
1447
  }
1377
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FunctionRegistryService, decorators: [{
1448
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: FunctionRegistryService, decorators: [{
1378
1449
  type: Injectable
1379
1450
  }] });
1380
1451
 
@@ -3059,10 +3130,10 @@ class SchemaRegistryService {
3059
3130
  }
3060
3131
  return schema;
3061
3132
  }
3062
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SchemaRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3063
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SchemaRegistryService });
3133
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SchemaRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3134
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SchemaRegistryService });
3064
3135
  }
3065
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SchemaRegistryService, decorators: [{
3136
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SchemaRegistryService, decorators: [{
3066
3137
  type: Injectable
3067
3138
  }] });
3068
3139
 
@@ -3183,6 +3254,16 @@ function isArrayField(field) {
3183
3254
  return field.type === 'array' && 'fields' in field;
3184
3255
  }
3185
3256
 
3257
+ /**
3258
+ * Type guard to check if a field is a hidden field.
3259
+ *
3260
+ * @param field - The field to check
3261
+ * @returns True if the field is a HiddenField
3262
+ */
3263
+ function isHiddenField(field) {
3264
+ return field.type === 'hidden';
3265
+ }
3266
+
3186
3267
  /**
3187
3268
  * Type guard for RowField with proper type narrowing
3188
3269
  */
@@ -3190,6 +3271,36 @@ function isArrayField(field) {
3190
3271
  function isRowField(field) {
3191
3272
  return field.type === 'row' && 'fields' in field && Array.isArray(field.fields);
3192
3273
  }
3274
+ /**
3275
+ * Validates that a row field doesn't contain hidden fields.
3276
+ * Hidden fields are not allowed in rows because rows are for horizontal layouts,
3277
+ * and hidden fields don't render anything.
3278
+ *
3279
+ * @param rowField The row field to validate
3280
+ * @returns true if valid (no hidden fields), false otherwise
3281
+ */
3282
+ function validateRowNesting(rowField) {
3283
+ return !hasHiddenFields(rowField.fields);
3284
+ }
3285
+ /**
3286
+ * Recursively checks if fields contain any hidden fields
3287
+ * @param fields Array of field definitions to check
3288
+ * @returns true if hidden fields found, false otherwise
3289
+ */
3290
+ function hasHiddenFields(fields) {
3291
+ for (const field of fields) {
3292
+ if (isHiddenField(field)) {
3293
+ return true;
3294
+ }
3295
+ // Check group fields for nested hidden fields
3296
+ if (isGroupField(field)) {
3297
+ if (hasHiddenFields(field.fields)) {
3298
+ return true;
3299
+ }
3300
+ }
3301
+ }
3302
+ return false;
3303
+ }
3193
3304
 
3194
3305
  /**
3195
3306
  * Type guard for PageField with proper type narrowing
@@ -3993,10 +4104,10 @@ class EventBus {
3993
4104
  }
3994
4105
  return this.pipeline$.pipe(filter((event) => event.type === eventType));
3995
4106
  }
3996
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: EventBus, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3997
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: EventBus });
4107
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: EventBus, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4108
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: EventBus });
3998
4109
  }
3999
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: EventBus, decorators: [{
4110
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: EventBus, decorators: [{
4000
4111
  type: Injectable
4001
4112
  }] });
4002
4113
 
@@ -4536,7 +4647,8 @@ function convertBuiltInToCustomValidator(config) {
4536
4647
  * Comprehensive form configuration validator that checks:
4537
4648
  * 1. Form mode consistency (paged vs non-paged)
4538
4649
  * 2. Page nesting rules
4539
- * 3. Field placement constraints
4650
+ * 3. Row nesting rules (no hidden fields in rows)
4651
+ * 4. Field placement constraints
4540
4652
  */
4541
4653
  class FormModeValidator {
4542
4654
  /**
@@ -4603,6 +4715,47 @@ class FormModeValidator {
4603
4715
  }
4604
4716
  }
4605
4717
  }
4718
+ // Warn about hidden fields in rows (they work but don't render, which may be confusing)
4719
+ const rowWarnings = this.collectRowHiddenFieldWarnings(fields);
4720
+ warnings.push(...rowWarnings);
4721
+ return warnings;
4722
+ }
4723
+ /**
4724
+ * Recursively collects warnings for hidden fields inside rows
4725
+ * @param fields The form field definitions to check
4726
+ * @param path Current path for warning messages
4727
+ * @returns Array of warning messages
4728
+ */
4729
+ static collectRowHiddenFieldWarnings(fields, path = '') {
4730
+ if (!fields) {
4731
+ return [];
4732
+ }
4733
+ const warnings = [];
4734
+ for (let i = 0; i < fields.length; i++) {
4735
+ const field = fields[i];
4736
+ const fieldPath = path ? `${path}.fields[${i}]` : `fields[${i}]`;
4737
+ const fieldKey = field.key || 'unknown';
4738
+ if (isRowField(field)) {
4739
+ if (!validateRowNesting(field)) {
4740
+ warnings.push(`Row field at ${fieldPath} (key: "${fieldKey}") contains hidden fields. ` +
4741
+ `Hidden fields in rows don't render anything - consider placing them outside the row.`);
4742
+ }
4743
+ // Continue checking nested rows within the row's children
4744
+ warnings.push(...this.collectRowHiddenFieldWarnings(field.fields, fieldPath));
4745
+ }
4746
+ else if (isPageField(field)) {
4747
+ // Check rows within pages
4748
+ warnings.push(...this.collectRowHiddenFieldWarnings(field.fields, fieldPath));
4749
+ }
4750
+ else if (isGroupField(field)) {
4751
+ // Check rows within groups
4752
+ warnings.push(...this.collectRowHiddenFieldWarnings(field.fields, fieldPath));
4753
+ }
4754
+ else if (isArrayField(field)) {
4755
+ // Check rows within array templates
4756
+ warnings.push(...this.collectRowHiddenFieldWarnings(field.fields, fieldPath));
4757
+ }
4758
+ }
4606
4759
  return warnings;
4607
4760
  }
4608
4761
  }
@@ -5074,8 +5227,8 @@ class PageOrchestratorComponent {
5074
5227
  }
5075
5228
  return false;
5076
5229
  }
5077
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PageOrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5078
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", 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: [
5230
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: PageOrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5231
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", 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: [
5079
5232
  {
5080
5233
  provide: FIELD_SIGNAL_CONTEXT,
5081
5234
  useFactory: (orchestrator) => orchestrator.extendedFieldSignalContext(),
@@ -5105,9 +5258,9 @@ class PageOrchestratorComponent {
5105
5258
  }
5106
5259
  }
5107
5260
  }
5108
- `, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [import('./ng-forge-dynamic-forms-page-field.component-BM32StxL.mjs').then(m => m.default)], () => [import('./ng-forge-dynamic-forms-page-field.component-BM32StxL.mjs').then(m => m.default)]] });
5261
+ `, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [import('./ng-forge-dynamic-forms-page-field.component-rdGD8A9d.mjs').then(m => m.default)], () => [import('./ng-forge-dynamic-forms-page-field.component-rdGD8A9d.mjs').then(m => m.default)]] });
5109
5262
  }
5110
- i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "21.0.6", ngImport: i0, type: PageOrchestratorComponent, resolveDeferredDeps: () => [import('./ng-forge-dynamic-forms-page-field.component-BM32StxL.mjs').then(m => m.default)], resolveMetadata: PageFieldComponent => ({ decorators: [{
5263
+ i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "21.0.8", ngImport: i0, type: PageOrchestratorComponent, resolveDeferredDeps: () => [import('./ng-forge-dynamic-forms-page-field.component-rdGD8A9d.mjs').then(m => m.default)], resolveMetadata: PageFieldComponent => ({ decorators: [{
5111
5264
  type: Component,
5112
5265
  args: [{ selector: 'div[page-orchestrator]', imports: [PageFieldComponent], template: `
5113
5266
  @for (pageField of pageFields(); track pageField.key; let i = $index) {
@@ -5560,8 +5713,8 @@ class DynamicForm {
5560
5713
  ngOnDestroy() {
5561
5714
  this.rootFormRegistry.unregisterForm();
5562
5715
  }
5563
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DynamicForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
5564
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", 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 } }, outputs: { value: "valueChange", validityChange: "validityChange", dirtyChange: "dirtyChange", submitted: "submitted", reset: "reset", cleared: "cleared", events: "events", initialized: "initialized", onPageChange: "onPageChange", onPageNavigationStateChange: "onPageNavigationStateChange" }, host: { 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" }, classAttribute: "df-dynamic-form df-form" }, providers: [EventBus, SchemaRegistryService, FunctionRegistryService, RootFormRegistryService, FieldContextRegistryService], ngImport: i0, template: `
5716
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DynamicForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
5717
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", 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 } }, 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" }, classAttribute: "df-dynamic-form df-form" }, providers: [EventBus, SchemaRegistryService, FunctionRegistryService, RootFormRegistryService, FieldContextRegistryService], ngImport: i0, template: `
5565
5718
  @if (formModeDetection().mode === 'paged') {
5566
5719
  <div page-orchestrator [pageFields]="pageFieldDefinitions()" [form]="form()" [fieldSignalContext]="fieldSignalContext()"></div>
5567
5720
  } @else {
@@ -5571,7 +5724,7 @@ class DynamicForm {
5571
5724
  }
5572
5725
  `, isInline: true, styles: [":host,.df-form{--df-grid-columns: 12;--df-grid-gap: .5rem;--df-grid-row-gap: .5rem;--df-breakpoint-sm: 576px;--df-breakpoint-md: 768px;--df-breakpoint-lg: 992px;--df-breakpoint-xl: 1200px;--df-grid-gap-sm: .5rem;--df-grid-gap-md: .5rem;--df-grid-gap-lg: .5rem;--df-grid-gap-xl: .5rem;--df-grid-row-gap-sm: .5rem;--df-grid-row-gap-md: .5rem;--df-grid-row-gap-lg: .5rem;--df-grid-row-gap-xl: .5rem;--df-group-gap: var(--df-grid-gap);--df-group-padding: var(--df-grid-gap)}.df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap);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);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);--df-grid-row-gap: var(--df-grid-row-gap-sm)}.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),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);--df-grid-row-gap: var(--df-grid-row-gap-md)}.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);--df-grid-row-gap: var(--df-grid-row-gap-lg)}.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);--df-grid-row-gap: var(--df-grid-row-gap-xl)}.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);width:100%}:host>*{grid-column:1/-1}:host.df-form-spacing-custom{--df-grid-row-gap: var(--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{--df-grid-row-gap: 0}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: PageOrchestratorComponent, selector: "div[page-orchestrator]", inputs: ["pageFields", "form", "fieldSignalContext"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5573
5726
  }
5574
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DynamicForm, decorators: [{
5727
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DynamicForm, decorators: [{
5575
5728
  type: Component,
5576
5729
  args: [{ selector: 'form[dynamic-form]', imports: [NgComponentOutlet, PageOrchestratorComponent], template: `
5577
5730
  @if (formModeDetection().mode === 'paged') {
@@ -5583,6 +5736,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
5583
5736
  }
5584
5737
  `, providers: [EventBus, SchemaRegistryService, FunctionRegistryService, RootFormRegistryService, FieldContextRegistryService], host: {
5585
5738
  class: 'df-dynamic-form df-form',
5739
+ novalidate: '', // Disable browser validation - Angular Signal Forms handles validation
5586
5740
  '[class.disabled]': 'disabled()',
5587
5741
  '[class.df-form-paged]': 'formModeDetection().mode === "paged"',
5588
5742
  '[class.df-form-non-paged]': 'formModeDetection().mode === "non-paged"',
@@ -5756,16 +5910,21 @@ const BUILT_IN_FIELDS = [
5756
5910
  },
5757
5911
  {
5758
5912
  name: 'page',
5759
- loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-BM32StxL.mjs'),
5913
+ loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-rdGD8A9d.mjs'),
5760
5914
  mapper: pageFieldMapper,
5761
5915
  valueHandling: 'flatten',
5762
5916
  },
5763
5917
  {
5764
5918
  name: 'text',
5765
- loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-C0I8jf9T.mjs'),
5919
+ loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-CDxka2KD.mjs'),
5766
5920
  mapper: textFieldMapper,
5767
5921
  valueHandling: 'exclude',
5768
5922
  },
5923
+ {
5924
+ name: 'hidden',
5925
+ // Componentless field - no loadComponent or mapper needed
5926
+ valueHandling: 'include',
5927
+ },
5769
5928
  ];
5770
5929
 
5771
5930
  /**
@@ -5879,6 +6038,8 @@ function provideDynamicForm(...items) {
5879
6038
  const defaultLoggerProvider = hasLoggerFeature ? [] : [{ provide: DynamicFormLogger, useValue: new ConsoleLogger() }];
5880
6039
  return makeEnvironmentProviders([
5881
6040
  ...defaultLoggerProvider,
6041
+ // Always provide default Signal Forms classes (ng-touched, ng-invalid, etc.)
6042
+ provideSignalFormsConfig({ classes: NG_STATUS_CLASSES }),
5882
6043
  {
5883
6044
  provide: FIELD_REGISTRY,
5884
6045
  useFactory: () => {
@@ -6224,6 +6385,51 @@ function dynamicTextToObservable(value, injector) {
6224
6385
  return of(String(value));
6225
6386
  }
6226
6387
 
6388
+ /**
6389
+ * Applies meta attributes to a DOM element and tracks which attributes were applied.
6390
+ *
6391
+ * This utility handles:
6392
+ * - Removing previously applied attributes that are no longer in meta
6393
+ * - Setting new attributes from meta
6394
+ * - Converting all values to strings via String()
6395
+ *
6396
+ * @param element - The DOM element to apply attributes to
6397
+ * @param meta - The meta object containing attributes to apply
6398
+ * @param previouslyApplied - Set of attribute names that were previously applied
6399
+ * @returns A new Set containing the attribute names that were applied
6400
+ *
6401
+ * @example
6402
+ * ```typescript
6403
+ * private appliedAttrs = new Set<string>();
6404
+ *
6405
+ * explicitEffect([this.meta], ([meta]) => {
6406
+ * const input = this.el.nativeElement.querySelector('input');
6407
+ * if (input) {
6408
+ * this.appliedAttrs = applyMetaToElement(input, meta, this.appliedAttrs);
6409
+ * }
6410
+ * });
6411
+ * ```
6412
+ */
6413
+ function applyMetaToElement(element, meta, previouslyApplied) {
6414
+ const newApplied = new Set();
6415
+ // Remove old attributes no longer in meta
6416
+ for (const attr of previouslyApplied) {
6417
+ if (!meta || !(attr in meta) || meta[attr] === undefined) {
6418
+ element.removeAttribute(attr);
6419
+ }
6420
+ }
6421
+ if (!meta)
6422
+ return newApplied;
6423
+ // Apply new attributes
6424
+ for (const [key, value] of Object.entries(meta)) {
6425
+ if (value === undefined || value === null)
6426
+ continue;
6427
+ element.setAttribute(key, String(value));
6428
+ newApplied.add(key);
6429
+ }
6430
+ return newApplied;
6431
+ }
6432
+
6227
6433
  /**
6228
6434
  * Pipe that handles dynamic text resolution with support for static strings,
6229
6435
  * Observables, and Signals.
@@ -6264,10 +6470,10 @@ class DynamicTextPipe {
6264
6470
  }
6265
6471
  return of(value || '');
6266
6472
  }
6267
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DynamicTextPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
6268
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: DynamicTextPipe, isStandalone: true, name: "dynamicText" });
6473
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DynamicTextPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
6474
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.8", ngImport: i0, type: DynamicTextPipe, isStandalone: true, name: "dynamicText" });
6269
6475
  }
6270
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DynamicTextPipe, decorators: [{
6476
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DynamicTextPipe, decorators: [{
6271
6477
  type: Pipe,
6272
6478
  args: [{
6273
6479
  name: 'dynamicText',
@@ -6370,14 +6576,14 @@ class RowFieldComponent {
6370
6576
  }
6371
6577
  });
6372
6578
  }
6373
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RowFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6374
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: RowFieldComponent, isStandalone: true, selector: "div[row-field]", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.disabled": "disabled()", "id": "`${key()}`", "attr.data-testid": "key()" }, classAttribute: "df-field df-row" }, ngImport: i0, template: `
6579
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: RowFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6580
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: RowFieldComponent, isStandalone: true, selector: "div[row-field]", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.disabled": "disabled()", "id": "`${key()}`", "attr.data-testid": "key()" }, classAttribute: "df-field df-row" }, ngImport: i0, template: `
6375
6581
  @for (field of resolvedFields(); track field.key) {
6376
6582
  <ng-container *ngComponentOutlet="field.component; injector: field.injector; inputs: field.inputs()" />
6377
6583
  }
6378
6584
  `, isInline: true, styles: [":host,.df-form{--df-grid-columns: 12;--df-grid-gap: .5rem;--df-grid-row-gap: .5rem;--df-breakpoint-sm: 576px;--df-breakpoint-md: 768px;--df-breakpoint-lg: 992px;--df-breakpoint-xl: 1200px;--df-grid-gap-sm: .5rem;--df-grid-gap-md: .5rem;--df-grid-gap-lg: .5rem;--df-grid-gap-xl: .5rem;--df-grid-row-gap-sm: .5rem;--df-grid-row-gap-md: .5rem;--df-grid-row-gap-lg: .5rem;--df-grid-row-gap-xl: .5rem;--df-group-gap: var(--df-grid-gap);--df-group-padding: var(--df-grid-gap)}.df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap);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);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);--df-grid-row-gap: var(--df-grid-row-gap-sm)}.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),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);--df-grid-row-gap: var(--df-grid-row-gap-md)}.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);--df-grid-row-gap: var(--df-grid-row-gap-lg)}.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);--df-grid-row-gap: var(--df-grid-row-gap-xl)}.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:flex;flex-wrap:wrap;gap:var(--df-grid-gap);align-items:flex-start;width:100%}:host ::ng-deep>*:not([class*=df-col-]){flex:0 0 auto}:host ::ng-deep>.df-col-1{flex:0 0 calc(8.3333333333% - var(--df-grid-gap) * .9166666667);max-width:calc(8.3333333333% - var(--df-grid-gap) * .9166666667)}:host ::ng-deep>.df-col-2{flex:0 0 calc(16.6666666667% - var(--df-grid-gap) * .8333333333);max-width:calc(16.6666666667% - var(--df-grid-gap) * .8333333333)}:host ::ng-deep>.df-col-3{flex:0 0 calc(25% - var(--df-grid-gap) * .75);max-width:calc(25% - var(--df-grid-gap) * .75)}:host ::ng-deep>.df-col-4{flex:0 0 calc(33.3333333333% - var(--df-grid-gap) * .6666666667);max-width:calc(33.3333333333% - var(--df-grid-gap) * .6666666667)}:host ::ng-deep>.df-col-5{flex:0 0 calc(41.6666666667% - var(--df-grid-gap) * .5833333333);max-width:calc(41.6666666667% - var(--df-grid-gap) * .5833333333)}:host ::ng-deep>.df-col-6{flex:0 0 calc(50% - var(--df-grid-gap) * .5);max-width:calc(50% - var(--df-grid-gap) * .5)}:host ::ng-deep>.df-col-7{flex:0 0 calc(58.3333333333% - var(--df-grid-gap) * .4166666667);max-width:calc(58.3333333333% - var(--df-grid-gap) * .4166666667)}:host ::ng-deep>.df-col-8{flex:0 0 calc(66.6666666667% - var(--df-grid-gap) * .3333333333);max-width:calc(66.6666666667% - var(--df-grid-gap) * .3333333333)}:host ::ng-deep>.df-col-9{flex:0 0 calc(75% - var(--df-grid-gap) * .25);max-width:calc(75% - var(--df-grid-gap) * .25)}:host ::ng-deep>.df-col-10{flex:0 0 calc(83.3333333333% - var(--df-grid-gap) * .1666666667);max-width:calc(83.3333333333% - var(--df-grid-gap) * .1666666667)}:host ::ng-deep>.df-col-11{flex:0 0 calc(91.6666666667% - var(--df-grid-gap) * .0833333333);max-width:calc(91.6666666667% - var(--df-grid-gap) * .0833333333)}:host ::ng-deep>.df-col-12{flex:0 0 calc(100% - var(--df-grid-gap) * 0);max-width:calc(100% - var(--df-grid-gap) * 0)}:host.df-gap-custom{--df-grid-gap: var(--row-custom-gap, 1rem)}@media(max-width:576px){:host.df-row-mobile-stack{flex-direction:column}:host.df-row-mobile-stack ::ng-deep>*{flex:0 0 100%!important;max-width:100%!important}}@media(min-width:577px)and (max-width:768px){:host.df-row-tablet-2col ::ng-deep>*{flex:0 0 calc(50% - var(--df-grid-gap) / 2);max-width:calc(50% - var(--df-grid-gap) / 2)}}@media(max-width:576px){:host{flex-direction:column}:host ::ng-deep>*{flex:0 0 100%!important;max-width:100%!important}:host.df-row-mobile-keep-cols{flex-direction:row}:host.df-row-mobile-keep-cols ::ng-deep>*:not([class*=df-col-]){flex:0 0 auto!important;max-width:none!important}}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6379
6585
  }
6380
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RowFieldComponent, decorators: [{
6586
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: RowFieldComponent, decorators: [{
6381
6587
  type: Component,
6382
6588
  args: [{ selector: 'div[row-field]', imports: [NgComponentOutlet], template: `
6383
6589
  @for (field of resolvedFields(); track field.key) {
@@ -6547,14 +6753,14 @@ class GroupFieldComponent {
6547
6753
  }
6548
6754
  });
6549
6755
  }
6550
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: GroupFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6551
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", 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 } }, outputs: { validityChange: "validityChange", dirtyChange: "dirtyChange", submitted: "submitted" }, host: { attributes: { "role": "group" }, properties: { "class.disabled": "disabled()", "id": "`${key()}`", "attr.data-testid": "key()" }, classAttribute: "df-field df-group" }, ngImport: i0, template: `
6756
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: GroupFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6757
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", 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 } }, outputs: { validityChange: "validityChange", dirtyChange: "dirtyChange", submitted: "submitted" }, host: { attributes: { "role": "group" }, properties: { "class.disabled": "disabled()", "id": "`${key()}`", "attr.data-testid": "key()" }, classAttribute: "df-field df-group" }, ngImport: i0, template: `
6552
6758
  @for (field of resolvedFields(); track field.key) {
6553
6759
  <ng-container *ngComponentOutlet="field.component; injector: field.injector; inputs: field.inputs()" />
6554
6760
  }
6555
6761
  `, isInline: true, styles: [":host,.df-form{--df-grid-columns: 12;--df-grid-gap: .5rem;--df-grid-row-gap: .5rem;--df-breakpoint-sm: 576px;--df-breakpoint-md: 768px;--df-breakpoint-lg: 992px;--df-breakpoint-xl: 1200px;--df-grid-gap-sm: .5rem;--df-grid-gap-md: .5rem;--df-grid-gap-lg: .5rem;--df-grid-gap-xl: .5rem;--df-grid-row-gap-sm: .5rem;--df-grid-row-gap-md: .5rem;--df-grid-row-gap-lg: .5rem;--df-grid-row-gap-xl: .5rem;--df-group-gap: var(--df-grid-gap);--df-group-padding: var(--df-grid-gap)}.df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap);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);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);--df-grid-row-gap: var(--df-grid-row-gap-sm)}.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),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);--df-grid-row-gap: var(--df-grid-row-gap-md)}.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);--df-grid-row-gap: var(--df-grid-row-gap-lg)}.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);--df-grid-row-gap: var(--df-grid-row-gap-xl)}.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}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6556
6762
  }
6557
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: GroupFieldComponent, decorators: [{
6763
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: GroupFieldComponent, decorators: [{
6558
6764
  type: Component,
6559
6765
  args: [{ selector: 'fieldset[group-field]', imports: [NgComponentOutlet], template: `
6560
6766
  @for (field of resolvedFields(); track field.key) {
@@ -6756,6 +6962,10 @@ function resolveArrayItem(options) {
6756
6962
  if (destroyRef.destroyed) {
6757
6963
  return undefined;
6758
6964
  }
6965
+ // Componentless fields (e.g., hidden) return undefined - nothing to render
6966
+ if (!component) {
6967
+ return undefined;
6968
+ }
6759
6969
  const itemId = generateArrayItemId();
6760
6970
  const indexSignal = linkedSignal(() => {
6761
6971
  const order = itemOrderSignal();
@@ -6771,9 +6981,13 @@ function resolveArrayItem(options) {
6771
6981
  registry,
6772
6982
  arrayField,
6773
6983
  });
6984
+ // Array item templates should always have inputs (componentless fields are handled above)
6985
+ if (!inputs) {
6986
+ return undefined;
6987
+ }
6774
6988
  return {
6775
6989
  id: itemId,
6776
- component: component,
6990
+ component,
6777
6991
  injector,
6778
6992
  inputs,
6779
6993
  };
@@ -6979,14 +7193,14 @@ class ArrayFieldComponent {
6979
7193
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
6980
7194
  parentForm().value.set({ ...currentValue, [arrayKey]: newArray });
6981
7195
  }
6982
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ArrayFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6983
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ArrayFieldComponent, isStandalone: true, selector: "array-field", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "attr.data-testid": "key()" }, classAttribute: "df-field df-array" }, ngImport: i0, template: `
7196
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ArrayFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7197
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: ArrayFieldComponent, isStandalone: true, selector: "array-field", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "attr.data-testid": "key()" }, classAttribute: "df-field df-array" }, ngImport: i0, template: `
6984
7198
  @for (item of resolvedItems(); track item.id) {
6985
7199
  <ng-container *ngComponentOutlet="item.component; injector: item.injector; inputs: item.inputs()" />
6986
7200
  }
6987
7201
  `, isInline: true, styles: [":host,.df-form{--df-grid-columns: 12;--df-grid-gap: .5rem;--df-grid-row-gap: .5rem;--df-breakpoint-sm: 576px;--df-breakpoint-md: 768px;--df-breakpoint-lg: 992px;--df-breakpoint-xl: 1200px;--df-grid-gap-sm: .5rem;--df-grid-gap-md: .5rem;--df-grid-gap-lg: .5rem;--df-grid-gap-xl: .5rem;--df-grid-row-gap-sm: .5rem;--df-grid-row-gap-md: .5rem;--df-grid-row-gap-lg: .5rem;--df-grid-row-gap-xl: .5rem;--df-group-gap: var(--df-grid-gap);--df-group-padding: var(--df-grid-gap)}.df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap);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);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);--df-grid-row-gap: var(--df-grid-row-gap-sm)}.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),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);--df-grid-row-gap: var(--df-grid-row-gap-md)}.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);--df-grid-row-gap: var(--df-grid-row-gap-lg)}.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);--df-grid-row-gap: var(--df-grid-row-gap-xl)}.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}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6988
7202
  }
6989
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ArrayFieldComponent, decorators: [{
7203
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ArrayFieldComponent, decorators: [{
6990
7204
  type: Component,
6991
7205
  args: [{ selector: 'array-field', imports: [NgComponentOutlet], template: `
6992
7206
  @for (item of resolvedItems(); track item.id) {
@@ -7082,16 +7296,31 @@ function extractErrorParams(error) {
7082
7296
  params.min = error.min;
7083
7297
  if ('max' in error)
7084
7298
  params.max = error.max;
7085
- if ('requiredLength' in error)
7086
- params.requiredLength = error.requiredLength;
7087
- if ('actualLength' in error)
7088
- params.actualLength = error.actualLength;
7089
7299
  if ('pattern' in error)
7090
7300
  params.pattern = error.pattern;
7091
7301
  if ('actual' in error)
7092
7302
  params.actual = error.actual;
7093
7303
  if ('expected' in error)
7094
7304
  params.expected = error.expected;
7305
+ // Handle length validation parameters
7306
+ // Angular Signal Forms uses 'minLength'/'maxLength' properties
7307
+ // Angular Classic Forms uses 'requiredLength'/'actualLength' properties
7308
+ // We support both formats and normalize to 'requiredLength' for message templates
7309
+ if ('requiredLength' in error) {
7310
+ params.requiredLength = error.requiredLength;
7311
+ }
7312
+ else if ('minLength' in error) {
7313
+ // Angular Signal Forms: minLength property maps to requiredLength for templates
7314
+ params.requiredLength = error.minLength;
7315
+ params.minLength = error.minLength;
7316
+ }
7317
+ else if ('maxLength' in error) {
7318
+ // Angular Signal Forms: maxLength property maps to requiredLength for templates
7319
+ params.requiredLength = error.maxLength;
7320
+ params.maxLength = error.maxLength;
7321
+ }
7322
+ if ('actualLength' in error)
7323
+ params.actualLength = error.actualLength;
7095
7324
  // Include all custom properties from error object (for custom validators)
7096
7325
  // Skip 'kind' as it's the error type identifier, not a parameter
7097
7326
  Object.keys(error).forEach((key) => {
@@ -7133,5 +7362,5 @@ function extractErrorParams(error) {
7133
7362
  * Generated bundle index. Do not edit.
7134
7363
  */
7135
7364
 
7136
- export { BUILT_IN_FIELDS as $, AddArrayItemEvent as A, baseFieldMapper as B, ConsoleLogger as C, DynamicFormLogger as D, EventBus as E, FormClearEvent as F, buildBaseInputs as G, arrayFieldMapper as H, groupFieldMapper as I, pageFieldMapper as J, rowFieldMapper as K, textFieldMapper as L, FIELD_REGISTRY as M, NoopLogger as N, ARRAY_CONTEXT as O, PageChangeEvent as P, FIELD_SIGNAL_CONTEXT as Q, RemoveArrayItemEvent as R, SubmitEvent as S, dynamicTextToObservable as T, ArrayFieldComponent as U, GroupFieldComponent as V, RowFieldComponent as W, applyValidator as X, applyValidators as Y, getArrayLength as Z, resolveTokens as _, DynamicTextPipe as a, omit as a0, interpolateParams as a1, DynamicForm as b, createFieldResolutionPipe as c, derivedFromDeferred as d, emitComponentInitialized as e, isFormStateCondition as f, resolveNextButtonDisabled as g, isCheckedField as h, injectFieldRegistry as i, isValueField as j, isRowField as k, isArrayField as l, isContainerField as m, isDisplayOnlyField as n, isGroupField as o, provideDynamicForm as p, isLeafField as q, resolveSubmitButtonDisabled as r, isPageField as s, isValueBearingField as t, FormResetEvent as u, validatePageNesting as v, withLoggerConfig as w, NextPageEvent as x, PreviousPageEvent as y, DynamicFormError as z };
7137
- //# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-CZBdtTAU.mjs.map
7365
+ export { BUILT_IN_FIELDS as $, AddArrayItemEvent as A, baseFieldMapper as B, ConsoleLogger as C, DynamicFormLogger as D, EventBus as E, FormClearEvent as F, buildBaseInputs as G, arrayFieldMapper as H, groupFieldMapper as I, pageFieldMapper as J, rowFieldMapper as K, textFieldMapper as L, FIELD_REGISTRY as M, NoopLogger as N, ARRAY_CONTEXT as O, PageChangeEvent as P, FIELD_SIGNAL_CONTEXT as Q, RemoveArrayItemEvent as R, SubmitEvent as S, dynamicTextToObservable as T, ArrayFieldComponent as U, GroupFieldComponent as V, RowFieldComponent as W, applyValidator as X, applyValidators as Y, getArrayLength as Z, resolveTokens as _, DynamicTextPipe as a, omit as a0, interpolateParams as a1, applyMetaToElement as a2, DynamicForm as b, createFieldResolutionPipe as c, derivedFromDeferred as d, emitComponentInitialized as e, isFormStateCondition as f, resolveNextButtonDisabled as g, isCheckedField as h, injectFieldRegistry as i, isValueField as j, isRowField as k, isArrayField as l, isContainerField as m, isDisplayOnlyField as n, isGroupField as o, provideDynamicForm as p, isLeafField as q, resolveSubmitButtonDisabled as r, isPageField as s, isValueBearingField as t, FormResetEvent as u, validatePageNesting as v, withLoggerConfig as w, NextPageEvent as x, PreviousPageEvent as y, DynamicFormError as z };
7366
+ //# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-CMSmISSp.mjs.map