@ng-forge/dynamic-forms 0.5.1 → 0.6.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.
- package/README.md +10 -9
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs +416 -23
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-BWFdMPyw.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-Bf6Qnz0T.mjs} +12247 -7827
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-Bf6Qnz0T.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-_mRHaw7x.mjs +109 -0
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-_mRHaw7x.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-DIVyioMX.mjs → ng-forge-dynamic-forms-text-field.component-CmkBZk4p.mjs} +5 -5
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-DIVyioMX.mjs.map → ng-forge-dynamic-forms-text-field.component-CmkBZk4p.mjs.map} +1 -1
- package/fesm2022/ng-forge-dynamic-forms.mjs +1 -1
- package/package.json +1 -1
- package/types/ng-forge-dynamic-forms-integration.d.ts +234 -17
- package/types/ng-forge-dynamic-forms.d.ts +2296 -698
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-BWFdMPyw.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-RN1hmG69.mjs +0 -120
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-RN1hmG69.mjs.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as rxjs from 'rxjs';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
|
+
import * as _angular_core from '@angular/core';
|
|
4
|
+
import { Signal, Injector, WritableSignal, InjectionToken, Type, ResourceRef, Provider, EnvironmentProviders, PipeTransform } from '@angular/core';
|
|
3
5
|
import * as _angular_forms_signals from '@angular/forms/signals';
|
|
4
6
|
import { FieldTree, FieldContext, ValidationError as ValidationError$1, TreeValidationResult, SchemaPath, SchemaPathTree } from '@angular/forms/signals';
|
|
5
7
|
import * as _angular_forms from '@angular/forms';
|
|
6
8
|
import * as _ng_forge_dynamic_forms from '@ng-forge/dynamic-forms';
|
|
7
|
-
import * as _angular_core from '@angular/core';
|
|
8
|
-
import { Signal, Injector, WritableSignal, InjectionToken, Type, ResourceRef, OnDestroy, InputSignal, Provider, EnvironmentProviders, PipeTransform } from '@angular/core';
|
|
9
9
|
import { FormSchema } from '@ng-forge/dynamic-forms/schema';
|
|
10
10
|
|
|
11
11
|
type WithInputSignals<T> = {
|
|
@@ -251,6 +251,30 @@ interface FieldDef<TProps, TMeta extends FieldMeta = FieldMeta> {
|
|
|
251
251
|
* ```
|
|
252
252
|
*/
|
|
253
253
|
tabIndex?: number | undefined;
|
|
254
|
+
/**
|
|
255
|
+
* Whether to exclude this field's value from submission output when hidden.
|
|
256
|
+
*
|
|
257
|
+
* Overrides both the global `withValueExclusionDefaults()` and form-level `FormOptions` settings.
|
|
258
|
+
*
|
|
259
|
+
* @default undefined (uses form-level or global setting)
|
|
260
|
+
*/
|
|
261
|
+
excludeValueIfHidden?: boolean;
|
|
262
|
+
/**
|
|
263
|
+
* Whether to exclude this field's value from submission output when disabled.
|
|
264
|
+
*
|
|
265
|
+
* Overrides both the global `withValueExclusionDefaults()` and form-level `FormOptions` settings.
|
|
266
|
+
*
|
|
267
|
+
* @default undefined (uses form-level or global setting)
|
|
268
|
+
*/
|
|
269
|
+
excludeValueIfDisabled?: boolean;
|
|
270
|
+
/**
|
|
271
|
+
* Whether to exclude this field's value from submission output when readonly.
|
|
272
|
+
*
|
|
273
|
+
* Overrides both the global `withValueExclusionDefaults()` and form-level `FormOptions` settings.
|
|
274
|
+
*
|
|
275
|
+
* @default undefined (uses form-level or global setting)
|
|
276
|
+
*/
|
|
277
|
+
excludeValueIfReadonly?: boolean;
|
|
254
278
|
/**
|
|
255
279
|
* Column sizing configuration for responsive grid layout.
|
|
256
280
|
*
|
|
@@ -447,6 +471,249 @@ interface ResolvedField {
|
|
|
447
471
|
inputs: Signal<Record<string, unknown>>;
|
|
448
472
|
}
|
|
449
473
|
|
|
474
|
+
/**
|
|
475
|
+
* Configuration for an HTTP request used by declarative HTTP validators, derivations, and conditions.
|
|
476
|
+
*
|
|
477
|
+
* All string values in `queryParams` are treated as expressions evaluated by ExpressionParser.
|
|
478
|
+
* When `evaluateBodyExpressions` is true, top-level string values in `body` are also evaluated as expressions (shallow only).
|
|
479
|
+
*/
|
|
480
|
+
interface HttpRequestConfig {
|
|
481
|
+
/** URL to send the request to */
|
|
482
|
+
url: string;
|
|
483
|
+
/** HTTP method. Defaults to 'GET' */
|
|
484
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
485
|
+
/**
|
|
486
|
+
* URL path parameters. Keys correspond to `:key` placeholders in the URL.
|
|
487
|
+
* Values are expressions evaluated by ExpressionParser against the current EvaluationContext.
|
|
488
|
+
*
|
|
489
|
+
* @example
|
|
490
|
+
* ```typescript
|
|
491
|
+
* {
|
|
492
|
+
* url: '/api/users/:userId/orders/:orderId',
|
|
493
|
+
* params: { userId: 'formValue.userId', orderId: 'fieldValue' }
|
|
494
|
+
* }
|
|
495
|
+
* // With userId=42 and fieldValue='abc' → '/api/users/42/orders/abc'
|
|
496
|
+
* ```
|
|
497
|
+
*/
|
|
498
|
+
params?: Record<string, string>;
|
|
499
|
+
/**
|
|
500
|
+
* Query parameters appended to the URL.
|
|
501
|
+
* Values are expressions evaluated by ExpressionParser against the current EvaluationContext.
|
|
502
|
+
*/
|
|
503
|
+
queryParams?: Record<string, string>;
|
|
504
|
+
/** Request body (for POST/PUT/PATCH) */
|
|
505
|
+
body?: Record<string, unknown>;
|
|
506
|
+
/**
|
|
507
|
+
* When true, top-level string values in `body` are treated as expressions
|
|
508
|
+
* and evaluated by ExpressionParser (shallow only — nested objects are passed through as-is).
|
|
509
|
+
*/
|
|
510
|
+
evaluateBodyExpressions?: boolean;
|
|
511
|
+
/** HTTP headers to include in the request */
|
|
512
|
+
headers?: Record<string, string>;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Comparison operators for field value and form value conditions.
|
|
517
|
+
*
|
|
518
|
+
* @public
|
|
519
|
+
*/
|
|
520
|
+
type ComparisonOperator = 'equals' | 'notEquals' | 'greater' | 'less' | 'greaterOrEqual' | 'lessOrEqual' | 'contains' | 'startsWith' | 'endsWith' | 'matches';
|
|
521
|
+
/**
|
|
522
|
+
* Condition that compares a specific field's value against an expected value.
|
|
523
|
+
*
|
|
524
|
+
* @public
|
|
525
|
+
*/
|
|
526
|
+
interface FieldValueCondition {
|
|
527
|
+
type: 'fieldValue';
|
|
528
|
+
/** Path to the field whose value to compare */
|
|
529
|
+
fieldPath: string;
|
|
530
|
+
/** Comparison operator */
|
|
531
|
+
operator: ComparisonOperator;
|
|
532
|
+
/** Value to compare against */
|
|
533
|
+
value?: unknown;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Condition that compares the entire form value against an expected value.
|
|
537
|
+
*
|
|
538
|
+
* @deprecated Use `FieldValueCondition` for specific field checks, or
|
|
539
|
+
* `JavascriptCondition` with an expression for complex form-level comparisons.
|
|
540
|
+
* Will be removed in a future minor version.
|
|
541
|
+
*
|
|
542
|
+
* @public
|
|
543
|
+
*/
|
|
544
|
+
interface FormValueCondition {
|
|
545
|
+
type: 'formValue';
|
|
546
|
+
/** Comparison operator */
|
|
547
|
+
operator: ComparisonOperator;
|
|
548
|
+
/** Value to compare against */
|
|
549
|
+
value?: unknown;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Condition that invokes a registered custom function by name (new API).
|
|
553
|
+
*
|
|
554
|
+
* Register functions via `customFnConfig.customFunctions`.
|
|
555
|
+
*
|
|
556
|
+
* @internal
|
|
557
|
+
*/
|
|
558
|
+
interface CustomConditionNew {
|
|
559
|
+
type: 'custom';
|
|
560
|
+
/** Name of the registered custom function to invoke */
|
|
561
|
+
functionName: string;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* @deprecated Use `CustomCondition` with `functionName` instead.
|
|
565
|
+
* The `expression` field was misnamed — it holds a function registry key, not a JS expression.
|
|
566
|
+
* Will be removed in a future minor version.
|
|
567
|
+
*
|
|
568
|
+
* @internal
|
|
569
|
+
*/
|
|
570
|
+
interface CustomConditionDeprecated {
|
|
571
|
+
type: 'custom';
|
|
572
|
+
/**
|
|
573
|
+
* @deprecated Use `functionName` instead. Will be removed in a future minor version.
|
|
574
|
+
*/
|
|
575
|
+
expression: string;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Condition that invokes a registered custom function by name.
|
|
579
|
+
*
|
|
580
|
+
* Use `functionName` (new API). The `expression` field is deprecated.
|
|
581
|
+
* Register functions via `customFnConfig.customFunctions`.
|
|
582
|
+
*
|
|
583
|
+
* @public
|
|
584
|
+
*/
|
|
585
|
+
type CustomCondition = CustomConditionNew | CustomConditionDeprecated;
|
|
586
|
+
/**
|
|
587
|
+
* Condition that evaluates a JavaScript expression using the secure AST-based parser.
|
|
588
|
+
*
|
|
589
|
+
* The expression has access to `formValue`, `fieldValue`, `externalData`, etc.
|
|
590
|
+
*
|
|
591
|
+
* @public
|
|
592
|
+
*/
|
|
593
|
+
interface JavascriptCondition {
|
|
594
|
+
type: 'javascript';
|
|
595
|
+
/** JavaScript expression string to evaluate */
|
|
596
|
+
expression: string;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Condition that evaluates based on an HTTP response from a remote server.
|
|
600
|
+
*
|
|
601
|
+
* The HTTP request is resolved reactively — when dependent form values change,
|
|
602
|
+
* the request is re-evaluated (with debouncing). The result is cached per
|
|
603
|
+
* resolved request to avoid redundant network calls.
|
|
604
|
+
*
|
|
605
|
+
* Since `LogicFn` must return `boolean` synchronously, this condition uses
|
|
606
|
+
* a signal-based async resolution pattern internally.
|
|
607
|
+
*
|
|
608
|
+
* @public
|
|
609
|
+
*/
|
|
610
|
+
interface HttpCondition {
|
|
611
|
+
type: 'http';
|
|
612
|
+
/** HTTP request configuration */
|
|
613
|
+
http: HttpRequestConfig;
|
|
614
|
+
/**
|
|
615
|
+
* Expression to extract boolean from response (scope: `{ response }`).
|
|
616
|
+
* e.g. `'response.allowed'`, `'response.permissions.canEdit'`
|
|
617
|
+
* When omitted, the entire response is coerced to boolean: `!!response`
|
|
618
|
+
*/
|
|
619
|
+
responseExpression?: string;
|
|
620
|
+
/**
|
|
621
|
+
* Value to return while the HTTP request is in-flight.
|
|
622
|
+
*
|
|
623
|
+
* Choose based on the logic type and desired UX:
|
|
624
|
+
* - For `hidden`: `false` = visible while loading, `true` = hidden while loading
|
|
625
|
+
* - For `disabled`: `false` = enabled while loading, `true` = disabled while loading
|
|
626
|
+
* - For `required`: `false` = optional while loading, `true` = required while loading
|
|
627
|
+
*
|
|
628
|
+
* @default false
|
|
629
|
+
*/
|
|
630
|
+
pendingValue?: boolean;
|
|
631
|
+
/** Cache duration in ms for HTTP responses. @default 30000 */
|
|
632
|
+
cacheDurationMs?: number;
|
|
633
|
+
/**
|
|
634
|
+
* Debounce time in milliseconds for re-evaluation when dependent form values change.
|
|
635
|
+
* @default 300
|
|
636
|
+
*/
|
|
637
|
+
debounceMs?: number;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Condition that evaluates based on a registered async function.
|
|
641
|
+
*
|
|
642
|
+
* The function is resolved reactively — when dependent form values change,
|
|
643
|
+
* the function is re-evaluated (with debouncing). The result is cached per
|
|
644
|
+
* evaluation to avoid redundant calls.
|
|
645
|
+
*
|
|
646
|
+
* Since `LogicFn` must return `boolean` synchronously, this condition uses
|
|
647
|
+
* a signal-based async resolution pattern internally.
|
|
648
|
+
*
|
|
649
|
+
* @public
|
|
650
|
+
*/
|
|
651
|
+
interface AsyncCondition {
|
|
652
|
+
type: 'async';
|
|
653
|
+
/** Name of the registered async condition function */
|
|
654
|
+
asyncFunctionName: string;
|
|
655
|
+
/**
|
|
656
|
+
* Value to return while async resolution is pending.
|
|
657
|
+
*
|
|
658
|
+
* Choose based on the logic type and desired UX:
|
|
659
|
+
* - For `hidden`: `false` = visible while loading, `true` = hidden while loading
|
|
660
|
+
* - For `disabled`: `false` = enabled while loading, `true` = disabled while loading
|
|
661
|
+
* - For `required`: `false` = optional while loading, `true` = required while loading
|
|
662
|
+
*
|
|
663
|
+
* @default false
|
|
664
|
+
*/
|
|
665
|
+
pendingValue?: boolean;
|
|
666
|
+
/**
|
|
667
|
+
* Debounce ms for re-evaluation.
|
|
668
|
+
* @default 300
|
|
669
|
+
*/
|
|
670
|
+
debounceMs?: number;
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Logical AND — all sub-conditions must be true.
|
|
674
|
+
*
|
|
675
|
+
* @public
|
|
676
|
+
*/
|
|
677
|
+
interface AndCondition {
|
|
678
|
+
type: 'and';
|
|
679
|
+
/** Sub-conditions that must all evaluate to true */
|
|
680
|
+
conditions: ConditionalExpression[];
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Logical OR — at least one sub-condition must be true.
|
|
684
|
+
*
|
|
685
|
+
* @public
|
|
686
|
+
*/
|
|
687
|
+
interface OrCondition {
|
|
688
|
+
type: 'or';
|
|
689
|
+
/** Sub-conditions where at least one must evaluate to true */
|
|
690
|
+
conditions: ConditionalExpression[];
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Discriminated union of all conditional expression types.
|
|
694
|
+
*
|
|
695
|
+
* Each variant only allows the properties relevant to its type,
|
|
696
|
+
* providing compile-time safety against invalid property combinations.
|
|
697
|
+
*
|
|
698
|
+
* @public
|
|
699
|
+
*/
|
|
700
|
+
type ConditionalExpression = FieldValueCondition | FormValueCondition | CustomCondition | JavascriptCondition | HttpCondition | AsyncCondition | AndCondition | OrCondition;
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Logic configuration for container fields (page, group, row, array).
|
|
704
|
+
* Containers only support the 'hidden' logic type since they are layout containers,
|
|
705
|
+
* not form controls (they can't be readonly, disabled, or required).
|
|
706
|
+
*/
|
|
707
|
+
interface ContainerLogicConfig {
|
|
708
|
+
/** Only 'hidden' is supported for container fields */
|
|
709
|
+
type: 'hidden';
|
|
710
|
+
/**
|
|
711
|
+
* Condition that determines when this container is hidden.
|
|
712
|
+
* Can be a boolean or a conditional expression evaluated against form values.
|
|
713
|
+
*/
|
|
714
|
+
condition: ConditionalExpression | boolean;
|
|
715
|
+
}
|
|
716
|
+
|
|
450
717
|
/**
|
|
451
718
|
* Row field interface for creating horizontal layouts
|
|
452
719
|
* This is a special field type that contains other definitions arranged horizontally
|
|
@@ -467,6 +734,11 @@ interface RowField<TFields extends readonly RowAllowedChildren[] = readonly RowA
|
|
|
467
734
|
readonly label?: never;
|
|
468
735
|
/** Rows do not support meta - they have no native form element **/
|
|
469
736
|
readonly meta?: never;
|
|
737
|
+
/**
|
|
738
|
+
* Logic configurations for conditional row visibility.
|
|
739
|
+
* Only 'hidden' type logic is supported for rows.
|
|
740
|
+
*/
|
|
741
|
+
readonly logic?: ContainerLogicConfig[];
|
|
470
742
|
}
|
|
471
743
|
/**
|
|
472
744
|
* Type guard for RowField with proper type narrowing
|
|
@@ -491,6 +763,11 @@ interface GroupField<TFields extends readonly GroupAllowedChildren[] = readonly
|
|
|
491
763
|
readonly label?: never;
|
|
492
764
|
/** Groups do not support meta - they have no native form element **/
|
|
493
765
|
readonly meta?: never;
|
|
766
|
+
/**
|
|
767
|
+
* Logic configurations for conditional group visibility.
|
|
768
|
+
* Only 'hidden' type logic is supported for groups.
|
|
769
|
+
*/
|
|
770
|
+
readonly logic?: ContainerLogicConfig[];
|
|
494
771
|
}
|
|
495
772
|
/**
|
|
496
773
|
* Type guard for GroupField with proper type narrowing
|
|
@@ -559,13 +836,13 @@ interface HiddenField<TValue extends HiddenValue = HiddenValue> extends FieldDef
|
|
|
559
836
|
* Fields that are allowed as children of Page fields
|
|
560
837
|
* Pages can contain: rows, groups, arrays, and leaf fields (but NOT other pages)
|
|
561
838
|
*/
|
|
562
|
-
type PageAllowedChildren = LeafFieldTypes | RowField | GroupField | ArrayField;
|
|
839
|
+
type PageAllowedChildren = LeafFieldTypes | RowField | GroupField | ArrayField | SimplifiedArrayField;
|
|
563
840
|
/**
|
|
564
841
|
* Fields that are allowed as children of Row fields
|
|
565
842
|
* Rows can contain: groups, arrays, and leaf fields (but NOT pages, other rows, or hidden fields)
|
|
566
843
|
* Hidden fields are excluded because rows are for horizontal layouts and hidden fields don't render
|
|
567
844
|
*/
|
|
568
|
-
type RowAllowedChildren = Exclude<LeafFieldTypes, HiddenField> | GroupField | ArrayField;
|
|
845
|
+
type RowAllowedChildren = Exclude<LeafFieldTypes, HiddenField> | GroupField | ArrayField | SimplifiedArrayField;
|
|
569
846
|
/**
|
|
570
847
|
* Fields that are allowed as children of Group fields
|
|
571
848
|
* Groups can contain: rows and leaf fields (but NOT pages or other groups)
|
|
@@ -578,92 +855,216 @@ type GroupAllowedChildren = LeafFieldTypes | RowField;
|
|
|
578
855
|
*/
|
|
579
856
|
type ArrayAllowedChildren = LeafFieldTypes | RowField | GroupField;
|
|
580
857
|
|
|
858
|
+
/**
|
|
859
|
+
* An array item template is an array of allowed children that defines one OBJECT array item's fields.
|
|
860
|
+
* Used for object arrays where each item is `{ key1: value1, key2: value2, ... }`.
|
|
861
|
+
*/
|
|
862
|
+
type ArrayItemTemplate = readonly ArrayAllowedChildren[];
|
|
863
|
+
/**
|
|
864
|
+
* An array item definition can be either:
|
|
865
|
+
* - A single field (ArrayAllowedChildren) for PRIMITIVE array items - extracts field value directly
|
|
866
|
+
* - An array of fields (ArrayItemTemplate) for OBJECT array items - merges fields into object
|
|
867
|
+
*
|
|
868
|
+
* This allows support for:
|
|
869
|
+
* - Primitive arrays: `['tag1', 'tag2']`
|
|
870
|
+
* - Object arrays: `[{ name: 'Alice', email: '...' }]`
|
|
871
|
+
* - Heterogeneous arrays: `[{ value: 'x' }, 'y']` (mixed primitives and objects)
|
|
872
|
+
*/
|
|
873
|
+
type ArrayItemDefinition = ArrayAllowedChildren | ArrayItemTemplate;
|
|
581
874
|
/**
|
|
582
875
|
* Array field interface for creating dynamic field collections that map to array values.
|
|
583
876
|
*
|
|
584
877
|
* Key concepts:
|
|
585
|
-
* - The `fields` array defines
|
|
586
|
-
* -
|
|
587
|
-
*
|
|
588
|
-
*
|
|
878
|
+
* - The outer `fields` array defines INITIAL ITEMS (each element is one array item to render)
|
|
879
|
+
* - Each element can be either:
|
|
880
|
+
* - A single FieldDef (primitive item) - the field's value is extracted directly
|
|
881
|
+
* - An array of FieldDefs (object item) - fields are merged into an object
|
|
882
|
+
* - Items can have different field configurations (heterogeneous arrays supported)
|
|
883
|
+
* - Empty `fields: []` means no initial items - user adds via buttons with explicit templates
|
|
884
|
+
* - Initial values are declared via `value` property on each field definition
|
|
589
885
|
*
|
|
590
886
|
* @example
|
|
591
|
-
* // Primitive array
|
|
887
|
+
* // Primitive array: ['angular', 'typescript']
|
|
592
888
|
* {
|
|
593
889
|
* key: 'tags',
|
|
594
890
|
* type: 'array',
|
|
595
|
-
* fields: [
|
|
891
|
+
* fields: [
|
|
892
|
+
* { key: 'tag', type: 'input', value: 'angular' }, // Single field = primitive
|
|
893
|
+
* { key: 'tag', type: 'input', value: 'typescript' },
|
|
894
|
+
* ]
|
|
596
895
|
* }
|
|
597
|
-
* // Creates: { tags: ['tag1', 'tag2', 'tag3'] }
|
|
598
896
|
*
|
|
599
897
|
* @example
|
|
600
|
-
* // Object array
|
|
898
|
+
* // Object array: [{ name: 'Alice', email: '...' }]
|
|
601
899
|
* {
|
|
602
900
|
* key: 'contacts',
|
|
603
901
|
* type: 'array',
|
|
604
|
-
* fields: [
|
|
605
|
-
*
|
|
606
|
-
*
|
|
607
|
-
* { key: '
|
|
608
|
-
*
|
|
609
|
-
*
|
|
610
|
-
*
|
|
902
|
+
* fields: [
|
|
903
|
+
* [ // Array = object
|
|
904
|
+
* { key: 'name', type: 'input', value: 'Alice' },
|
|
905
|
+
* { key: 'email', type: 'input', value: 'alice@example.com' }
|
|
906
|
+
* ],
|
|
907
|
+
* ]
|
|
908
|
+
* }
|
|
909
|
+
*
|
|
910
|
+
* @example
|
|
911
|
+
* // Heterogeneous array: [{ label: 'Structured' }, 'Simple']
|
|
912
|
+
* {
|
|
913
|
+
* key: 'items',
|
|
914
|
+
* type: 'array',
|
|
915
|
+
* fields: [
|
|
916
|
+
* [{ key: 'label', type: 'input', value: 'Structured' }], // Object item
|
|
917
|
+
* { key: 'value', type: 'input', value: 'Simple' }, // Primitive item
|
|
918
|
+
* ]
|
|
919
|
+
* }
|
|
920
|
+
*
|
|
921
|
+
* @example
|
|
922
|
+
* // Empty array (add items via buttons)
|
|
923
|
+
* {
|
|
924
|
+
* key: 'tags',
|
|
925
|
+
* type: 'array',
|
|
926
|
+
* fields: [] // No initial items - user adds via button with template
|
|
611
927
|
* }
|
|
612
|
-
* // Creates: { contacts: [{name: '', email: ''}, {name: '', email: ''}] }
|
|
613
928
|
*
|
|
614
929
|
* Nesting constraints: Arrays can contain rows, leaf fields, or groups (for object arrays),
|
|
615
930
|
* but NOT pages or other arrays. Runtime validation enforces these rules.
|
|
616
931
|
*
|
|
617
932
|
* Note: Arrays are container fields and do not support `meta` since they have no native form element.
|
|
618
933
|
*/
|
|
619
|
-
interface ArrayField<TFields extends readonly
|
|
934
|
+
interface ArrayField<TFields extends readonly ArrayItemDefinition[] = readonly ArrayItemDefinition[]> extends FieldDef<never> {
|
|
620
935
|
type: 'array';
|
|
621
936
|
/**
|
|
622
|
-
*
|
|
623
|
-
*
|
|
624
|
-
* -
|
|
625
|
-
* -
|
|
937
|
+
* Array of item definitions. Each element defines one array item:
|
|
938
|
+
* - Single FieldDef = primitive item (field's value is extracted directly)
|
|
939
|
+
* - Array of FieldDefs = object item (fields are merged into an object)
|
|
940
|
+
* - Empty array `[]` = no initial items (add via buttons with explicit templates)
|
|
941
|
+
* - Items can have different configurations (heterogeneous arrays supported)
|
|
942
|
+
* - Initial values are set via `value` property on each field definition
|
|
626
943
|
*/
|
|
627
944
|
readonly fields: TFields;
|
|
628
945
|
/** Array fields do not have a label property **/
|
|
629
946
|
readonly label?: never;
|
|
630
947
|
/** Arrays do not support meta - they have no native form element **/
|
|
631
948
|
readonly meta?: never;
|
|
949
|
+
/**
|
|
950
|
+
* Logic configurations for conditional array visibility.
|
|
951
|
+
* Only 'hidden' type logic is supported for arrays.
|
|
952
|
+
*/
|
|
953
|
+
readonly logic?: ContainerLogicConfig[];
|
|
954
|
+
/**
|
|
955
|
+
* Minimum number of items required in the array.
|
|
956
|
+
* Validation fails if the array has fewer items than this value.
|
|
957
|
+
*/
|
|
958
|
+
readonly minLength?: number;
|
|
959
|
+
/**
|
|
960
|
+
* Maximum number of items allowed in the array.
|
|
961
|
+
* Validation fails if the array has more items than this value.
|
|
962
|
+
*/
|
|
963
|
+
readonly maxLength?: number;
|
|
632
964
|
}
|
|
633
965
|
/**
|
|
634
|
-
* Type guard for ArrayField with proper type narrowing
|
|
966
|
+
* Type guard for ArrayField with proper type narrowing.
|
|
967
|
+
* Validates that the field is an array type with a fields property that is an array.
|
|
968
|
+
* Fields can contain either single FieldDefs (primitive items) or arrays of FieldDefs (object items).
|
|
635
969
|
*/
|
|
636
970
|
declare function isArrayField(field: FieldDef<any>): field is ArrayField;
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
/**
|
|
642
|
-
|
|
643
|
-
/**
|
|
644
|
-
|
|
645
|
-
/** Value to compare against */
|
|
646
|
-
value?: unknown;
|
|
647
|
-
/** JavaScript expression for custom logic */
|
|
648
|
-
expression?: string;
|
|
649
|
-
/** Array of sub-conditions for 'and' and 'or' types */
|
|
650
|
-
conditions?: ConditionalExpression[];
|
|
971
|
+
/**
|
|
972
|
+
* Configuration for auto-generated add/remove buttons in simplified array fields.
|
|
973
|
+
*/
|
|
974
|
+
interface ArrayButtonConfig {
|
|
975
|
+
/** Custom label for the button */
|
|
976
|
+
readonly label?: string;
|
|
977
|
+
/** Additional properties passed to the button component */
|
|
978
|
+
readonly props?: Record<string, unknown>;
|
|
651
979
|
}
|
|
652
|
-
|
|
653
980
|
/**
|
|
654
|
-
*
|
|
655
|
-
*
|
|
656
|
-
*
|
|
981
|
+
* Simplified array field interface for common use cases.
|
|
982
|
+
*
|
|
983
|
+
* Instead of manually specifying each item in `fields`, provide a `template` that defines
|
|
984
|
+
* the structure of a single item, and a `value` array with initial data.
|
|
985
|
+
* The library auto-generates add/remove buttons.
|
|
986
|
+
*
|
|
987
|
+
* Discriminant: `template` presence → simplified API; `Array.isArray(template)` → object vs primitive.
|
|
988
|
+
*
|
|
989
|
+
* @example
|
|
990
|
+
* // Primitive array: ['angular', 'typescript']
|
|
991
|
+
* {
|
|
992
|
+
* key: 'tags',
|
|
993
|
+
* type: 'array',
|
|
994
|
+
* template: { key: 'value', type: 'input', label: 'Tag', required: true },
|
|
995
|
+
* value: ['angular', 'typescript']
|
|
996
|
+
* }
|
|
997
|
+
*
|
|
998
|
+
* @example
|
|
999
|
+
* // Object array: [{ name: 'Jane', phone: '555' }]
|
|
1000
|
+
* {
|
|
1001
|
+
* key: 'contacts',
|
|
1002
|
+
* type: 'array',
|
|
1003
|
+
* template: [
|
|
1004
|
+
* { key: 'name', type: 'input', label: 'Contact Name', required: true },
|
|
1005
|
+
* { key: 'phone', type: 'input', label: 'Phone Number' },
|
|
1006
|
+
* ],
|
|
1007
|
+
* value: [{ name: 'Jane', phone: '555' }]
|
|
1008
|
+
* }
|
|
1009
|
+
*
|
|
1010
|
+
* @example
|
|
1011
|
+
* // Button customization / opt-out
|
|
1012
|
+
* {
|
|
1013
|
+
* key: 'tags',
|
|
1014
|
+
* type: 'array',
|
|
1015
|
+
* template: { key: 'value', type: 'input', label: 'Tag' },
|
|
1016
|
+
* addButton: { label: 'Add Tag', props: { color: 'primary' } },
|
|
1017
|
+
* removeButton: false
|
|
1018
|
+
* }
|
|
657
1019
|
*/
|
|
658
|
-
interface
|
|
659
|
-
|
|
660
|
-
type: 'hidden';
|
|
1020
|
+
interface SimplifiedArrayField extends FieldDef<never> {
|
|
1021
|
+
type: 'array';
|
|
661
1022
|
/**
|
|
662
|
-
*
|
|
663
|
-
*
|
|
1023
|
+
* Template defining the structure of a single array item.
|
|
1024
|
+
* - Single field (ArrayAllowedChildren) → primitive array (each item is a single value)
|
|
1025
|
+
* - Array of fields (readonly ArrayAllowedChildren[]) → object array (each item is an object)
|
|
664
1026
|
*/
|
|
665
|
-
|
|
1027
|
+
readonly template: ArrayAllowedChildren | readonly ArrayAllowedChildren[];
|
|
1028
|
+
/** Initial values for the array. Each element creates one array item. */
|
|
1029
|
+
readonly value?: readonly unknown[];
|
|
1030
|
+
/**
|
|
1031
|
+
* Configuration for the auto-generated "Add" button, or `false` to disable it.
|
|
1032
|
+
* Defaults to a button with label "Add".
|
|
1033
|
+
*/
|
|
1034
|
+
readonly addButton?: ArrayButtonConfig | false;
|
|
1035
|
+
/**
|
|
1036
|
+
* Configuration for the auto-generated "Remove" button on each item, or `false` to disable it.
|
|
1037
|
+
* Defaults to a button with label "Remove".
|
|
1038
|
+
*/
|
|
1039
|
+
readonly removeButton?: ArrayButtonConfig | false;
|
|
1040
|
+
/** Simplified arrays do not support the label property */
|
|
1041
|
+
readonly label?: never;
|
|
1042
|
+
/** Simplified arrays do not support meta */
|
|
1043
|
+
readonly meta?: never;
|
|
1044
|
+
/**
|
|
1045
|
+
* Logic configurations for conditional array visibility.
|
|
1046
|
+
* Only 'hidden' type logic is supported for arrays.
|
|
1047
|
+
*/
|
|
1048
|
+
readonly logic?: ContainerLogicConfig[];
|
|
1049
|
+
/**
|
|
1050
|
+
* Minimum number of items required in the array.
|
|
1051
|
+
* Validation fails if the array has fewer items than this value.
|
|
1052
|
+
*/
|
|
1053
|
+
readonly minLength?: number;
|
|
1054
|
+
/**
|
|
1055
|
+
* Maximum number of items allowed in the array.
|
|
1056
|
+
* Validation fails if the array has more items than this value.
|
|
1057
|
+
*/
|
|
1058
|
+
readonly maxLength?: number;
|
|
1059
|
+
/** Mutually exclusive with `template` — use `fields` for the full API instead */
|
|
1060
|
+
readonly fields?: never;
|
|
666
1061
|
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Type guard for SimplifiedArrayField.
|
|
1064
|
+
* Checks for `type: 'array'` with a `template` property (discriminant from full ArrayField).
|
|
1065
|
+
*/
|
|
1066
|
+
declare function isSimplifiedArrayField(field: FieldDef<any>): field is SimplifiedArrayField;
|
|
1067
|
+
|
|
667
1068
|
/**
|
|
668
1069
|
* Page field interface for creating top-level page layouts
|
|
669
1070
|
* This is a special field type that contains other field definitions
|
|
@@ -707,7 +1108,7 @@ interface PageField<TFields extends readonly PageAllowedChildren[] = PageAllowed
|
|
|
707
1108
|
* }
|
|
708
1109
|
* ```
|
|
709
1110
|
*/
|
|
710
|
-
readonly logic?:
|
|
1111
|
+
readonly logic?: ContainerLogicConfig[];
|
|
711
1112
|
}
|
|
712
1113
|
/**
|
|
713
1114
|
* Type guard for PageField with proper type narrowing
|
|
@@ -874,19 +1275,52 @@ interface DebouncedStateLogicConfig extends BaseStateLogicConfig {
|
|
|
874
1275
|
*/
|
|
875
1276
|
type StateLogicConfig = ImmediateStateLogicConfig | DebouncedStateLogicConfig;
|
|
876
1277
|
/**
|
|
877
|
-
*
|
|
878
|
-
*
|
|
879
|
-
* All derivations target the field they are defined on (self-targeting).
|
|
880
|
-
* This simplifies configuration by removing the need for `targetField` -
|
|
881
|
-
* derivation logic is placed on the field that should receive the computed value.
|
|
1278
|
+
* Shared fields that appear on all derivation logic config variants.
|
|
882
1279
|
*
|
|
883
1280
|
* @internal
|
|
884
1281
|
*/
|
|
885
|
-
interface
|
|
1282
|
+
interface SharedDerivationFields {
|
|
886
1283
|
/**
|
|
887
1284
|
* Logic type identifier for value derivation.
|
|
888
1285
|
*/
|
|
889
1286
|
type: 'derivation';
|
|
1287
|
+
/**
|
|
1288
|
+
* Target property name for property derivation.
|
|
1289
|
+
*
|
|
1290
|
+
* When set, this derivation targets a field property (like `minDate`, `options`,
|
|
1291
|
+
* `label`, `placeholder`) instead of the field's value. The derivation is routed
|
|
1292
|
+
* to the property derivation pipeline.
|
|
1293
|
+
*
|
|
1294
|
+
* When absent, the derivation targets the field's value (default behavior).
|
|
1295
|
+
*
|
|
1296
|
+
* **Depth limit (max 2 levels):** Only simple and single-dot-nested paths are
|
|
1297
|
+
* supported. Paths with 2+ dots (e.g., `'a.b.c'`) will throw at runtime.
|
|
1298
|
+
*
|
|
1299
|
+
* @example
|
|
1300
|
+
* ```typescript
|
|
1301
|
+
* // Property derivation (new unified API)
|
|
1302
|
+
* {
|
|
1303
|
+
* key: 'endDate',
|
|
1304
|
+
* type: 'datepicker',
|
|
1305
|
+
* logic: [{
|
|
1306
|
+
* type: 'derivation',
|
|
1307
|
+
* targetProperty: 'minDate',
|
|
1308
|
+
* expression: 'formValue.startDate'
|
|
1309
|
+
* }]
|
|
1310
|
+
* }
|
|
1311
|
+
*
|
|
1312
|
+
* // Value derivation (no targetProperty — existing behavior)
|
|
1313
|
+
* {
|
|
1314
|
+
* key: 'total',
|
|
1315
|
+
* type: 'input',
|
|
1316
|
+
* logic: [{
|
|
1317
|
+
* type: 'derivation',
|
|
1318
|
+
* expression: 'formValue.quantity * formValue.unitPrice'
|
|
1319
|
+
* }]
|
|
1320
|
+
* }
|
|
1321
|
+
* ```
|
|
1322
|
+
*/
|
|
1323
|
+
targetProperty?: string;
|
|
890
1324
|
/**
|
|
891
1325
|
* Optional name for this derivation for debugging purposes.
|
|
892
1326
|
*
|
|
@@ -934,74 +1368,199 @@ interface BaseDerivationLogicConfig {
|
|
|
934
1368
|
*/
|
|
935
1369
|
condition?: ConditionalExpression | boolean;
|
|
936
1370
|
/**
|
|
937
|
-
*
|
|
1371
|
+
* When true, the derivation stops running after the user manually
|
|
1372
|
+
* edits the target field.
|
|
938
1373
|
*
|
|
939
|
-
*
|
|
940
|
-
*
|
|
1374
|
+
* This is useful for "smart defaults" — values that should be
|
|
1375
|
+
* auto-filled initially but respected once the user explicitly changes them.
|
|
1376
|
+
*
|
|
1377
|
+
* Uses the field's `dirty()` signal to detect user modification.
|
|
1378
|
+
* Derivations write directly to `value.set()` which does not trigger
|
|
1379
|
+
* `markAsDirty()`, so `dirty === true` reliably indicates a user edit.
|
|
941
1380
|
*
|
|
942
1381
|
* @example
|
|
943
1382
|
* ```typescript
|
|
944
|
-
*
|
|
945
|
-
*
|
|
946
|
-
*
|
|
947
|
-
*
|
|
1383
|
+
* // Auto-fill display name from first + last name, but stop if user edits it
|
|
1384
|
+
* {
|
|
1385
|
+
* key: 'displayName',
|
|
1386
|
+
* logic: [{
|
|
1387
|
+
* type: 'derivation',
|
|
1388
|
+
* expression: 'formValue.firstName + " " + formValue.lastName',
|
|
1389
|
+
* stopOnUserOverride: true
|
|
1390
|
+
* }]
|
|
1391
|
+
* }
|
|
948
1392
|
* ```
|
|
949
1393
|
*/
|
|
950
|
-
|
|
1394
|
+
stopOnUserOverride?: boolean;
|
|
951
1395
|
/**
|
|
952
|
-
*
|
|
1396
|
+
* When true (and `stopOnUserOverride` is also true), clears the
|
|
1397
|
+
* user-override flag when any dependency of this derivation changes,
|
|
1398
|
+
* allowing the derivation to run again.
|
|
953
1399
|
*
|
|
954
|
-
*
|
|
955
|
-
*
|
|
956
|
-
*
|
|
957
|
-
* Mutually exclusive with `value` and `functionName`.
|
|
1400
|
+
* This is useful when a user override should only persist until the
|
|
1401
|
+
* underlying data changes — e.g., when switching countries, the
|
|
1402
|
+
* phone prefix should re-derive even if the user previously edited it.
|
|
958
1403
|
*
|
|
959
1404
|
* @example
|
|
960
1405
|
* ```typescript
|
|
961
|
-
*
|
|
962
|
-
*
|
|
963
|
-
*
|
|
1406
|
+
* {
|
|
1407
|
+
* key: 'phonePrefix',
|
|
1408
|
+
* logic: [{
|
|
1409
|
+
* type: 'derivation',
|
|
1410
|
+
* value: '+1',
|
|
1411
|
+
* condition: { type: 'fieldValue', fieldPath: 'country', operator: 'equals', value: 'USA' },
|
|
1412
|
+
* stopOnUserOverride: true,
|
|
1413
|
+
* reEngageOnDependencyChange: true,
|
|
1414
|
+
* dependsOn: ['country']
|
|
1415
|
+
* }]
|
|
1416
|
+
* }
|
|
964
1417
|
* ```
|
|
965
1418
|
*/
|
|
966
|
-
|
|
1419
|
+
reEngageOnDependencyChange?: boolean;
|
|
1420
|
+
}
|
|
1421
|
+
/**
|
|
1422
|
+
* Trigger variants for derivation timing.
|
|
1423
|
+
* @internal
|
|
1424
|
+
*/
|
|
1425
|
+
type ImmediateDerivationTrigger = {
|
|
1426
|
+
trigger?: 'onChange';
|
|
1427
|
+
debounceMs?: never;
|
|
1428
|
+
};
|
|
1429
|
+
type DebouncedDerivationTrigger = {
|
|
1430
|
+
trigger: 'debounced';
|
|
1431
|
+
debounceMs?: number;
|
|
1432
|
+
};
|
|
1433
|
+
/**
|
|
1434
|
+
* Base for HTTP derivations. `source: 'http'` is required.
|
|
1435
|
+
* TypeScript enforces `dependsOn` and `responseExpression` at the type level.
|
|
1436
|
+
*
|
|
1437
|
+
* @internal
|
|
1438
|
+
*/
|
|
1439
|
+
interface HttpDerivationBase extends SharedDerivationFields {
|
|
1440
|
+
/** Identifies this derivation as HTTP-driven. */
|
|
1441
|
+
source: 'http';
|
|
967
1442
|
/**
|
|
968
|
-
*
|
|
1443
|
+
* HTTP request configuration for server-driven derivations.
|
|
969
1444
|
*
|
|
970
|
-
* The
|
|
971
|
-
*
|
|
972
|
-
*
|
|
1445
|
+
* The request is sent when dependencies change, with automatic
|
|
1446
|
+
* debouncing and cancellation of in-flight requests.
|
|
1447
|
+
*
|
|
1448
|
+
* Configure debounce via the `trigger: 'debounced'` + `debounceMs` mechanism.
|
|
973
1449
|
*
|
|
974
1450
|
* @example
|
|
975
1451
|
* ```typescript
|
|
976
|
-
*
|
|
977
|
-
*
|
|
978
|
-
*
|
|
1452
|
+
* {
|
|
1453
|
+
* key: 'exchangeRate',
|
|
1454
|
+
* logic: [{
|
|
1455
|
+
* type: 'derivation',
|
|
1456
|
+
* source: 'http',
|
|
1457
|
+
* http: {
|
|
1458
|
+
* url: '/api/exchange-rate',
|
|
1459
|
+
* method: 'GET',
|
|
1460
|
+
* queryParams: {
|
|
1461
|
+
* from: 'formValue.sourceCurrency',
|
|
1462
|
+
* to: 'formValue.targetCurrency',
|
|
1463
|
+
* },
|
|
1464
|
+
* },
|
|
1465
|
+
* responseExpression: 'response.rate',
|
|
1466
|
+
* dependsOn: ['sourceCurrency', 'targetCurrency'],
|
|
1467
|
+
* }]
|
|
1468
|
+
* }
|
|
979
1469
|
* ```
|
|
980
1470
|
*/
|
|
981
|
-
|
|
1471
|
+
http: HttpRequestConfig;
|
|
1472
|
+
/**
|
|
1473
|
+
* Explicit field dependencies. Required for HTTP derivations to prevent
|
|
1474
|
+
* wildcard triggering on every keystroke.
|
|
1475
|
+
*/
|
|
1476
|
+
dependsOn: string[];
|
|
982
1477
|
/**
|
|
983
|
-
*
|
|
1478
|
+
* Expression to extract the derived value from the HTTP response.
|
|
984
1479
|
*
|
|
985
|
-
*
|
|
986
|
-
* the function depends on. This enables more efficient re-evaluation
|
|
987
|
-
* by only triggering when specific dependencies change.
|
|
1480
|
+
* Evaluated via `ExpressionParser` with `{ response }` as the evaluation scope.
|
|
988
1481
|
*
|
|
989
|
-
*
|
|
990
|
-
*
|
|
991
|
-
*
|
|
1482
|
+
* @example
|
|
1483
|
+
* ```typescript
|
|
1484
|
+
* responseExpression: 'response.rate'
|
|
1485
|
+
* responseExpression: 'response.data.suggestedPrice'
|
|
1486
|
+
* ```
|
|
1487
|
+
*/
|
|
1488
|
+
responseExpression: string;
|
|
1489
|
+
value?: never;
|
|
1490
|
+
expression?: never;
|
|
1491
|
+
functionName?: never;
|
|
1492
|
+
asyncFunctionName?: never;
|
|
1493
|
+
}
|
|
1494
|
+
/**
|
|
1495
|
+
* Base for async function derivations. `source: 'asyncFunction'` is required.
|
|
1496
|
+
* TypeScript enforces `dependsOn` at the type level.
|
|
1497
|
+
*
|
|
1498
|
+
* @internal
|
|
1499
|
+
*/
|
|
1500
|
+
interface AsyncFunctionDerivationBase extends SharedDerivationFields {
|
|
1501
|
+
/** Identifies this derivation as async-function-driven. */
|
|
1502
|
+
source: 'asyncFunction';
|
|
1503
|
+
/**
|
|
1504
|
+
* Name of a registered async derivation function.
|
|
1505
|
+
*
|
|
1506
|
+
* The function receives the evaluation context and returns a Promise or Observable
|
|
1507
|
+
* of the derived value. Register functions in `customFnConfig.asyncDerivations`.
|
|
992
1508
|
*
|
|
993
1509
|
* @example
|
|
994
1510
|
* ```typescript
|
|
995
|
-
* // Only re-evaluate when country changes
|
|
996
1511
|
* {
|
|
997
|
-
* key: '
|
|
1512
|
+
* key: 'suggestedPrice',
|
|
998
1513
|
* logic: [{
|
|
999
1514
|
* type: 'derivation',
|
|
1000
|
-
*
|
|
1001
|
-
*
|
|
1515
|
+
* source: 'asyncFunction',
|
|
1516
|
+
* asyncFunctionName: 'fetchSuggestedPrice',
|
|
1517
|
+
* dependsOn: ['productId', 'quantity'],
|
|
1002
1518
|
* }]
|
|
1003
1519
|
* }
|
|
1520
|
+
* ```
|
|
1521
|
+
*/
|
|
1522
|
+
asyncFunctionName: string;
|
|
1523
|
+
/**
|
|
1524
|
+
* Explicit field dependencies. Required for async derivations to prevent
|
|
1525
|
+
* wildcard triggering on every form change.
|
|
1526
|
+
*/
|
|
1527
|
+
dependsOn: string[];
|
|
1528
|
+
value?: never;
|
|
1529
|
+
expression?: never;
|
|
1530
|
+
functionName?: never;
|
|
1531
|
+
http?: never;
|
|
1532
|
+
responseExpression?: never;
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* Base for expression derivations.
|
|
1536
|
+
*
|
|
1537
|
+
* @internal
|
|
1538
|
+
*/
|
|
1539
|
+
interface ExpressionDerivationBase extends SharedDerivationFields {
|
|
1540
|
+
source?: never;
|
|
1541
|
+
/**
|
|
1542
|
+
* JavaScript expression to evaluate for the derived value.
|
|
1543
|
+
*
|
|
1544
|
+
* Has access to `formValue` object containing all form values.
|
|
1545
|
+
* For array fields, `formValue` is scoped to the current array item.
|
|
1546
|
+
* Uses the same secure AST-based parser as other expressions.
|
|
1547
|
+
*
|
|
1548
|
+
* @example
|
|
1549
|
+
* ```typescript
|
|
1550
|
+
* expression: 'formValue.quantity * formValue.unitPrice'
|
|
1551
|
+
* expression: 'formValue.firstName + " " + formValue.lastName'
|
|
1552
|
+
* expression: 'formValue.price * (1 - formValue.discount / 100)'
|
|
1553
|
+
* ```
|
|
1554
|
+
*/
|
|
1555
|
+
expression: string;
|
|
1556
|
+
/**
|
|
1557
|
+
* Explicit field dependencies for expressions.
|
|
1004
1558
|
*
|
|
1559
|
+
* For `expression`, dependencies are automatically extracted from the expression.
|
|
1560
|
+
* Provide `dependsOn` to override automatic detection for complex expressions.
|
|
1561
|
+
*
|
|
1562
|
+
* @example
|
|
1563
|
+
* ```typescript
|
|
1005
1564
|
* // Override automatic detection for complex expressions
|
|
1006
1565
|
* {
|
|
1007
1566
|
* key: 'total',
|
|
@@ -1014,41 +1573,144 @@ interface BaseDerivationLogicConfig {
|
|
|
1014
1573
|
* ```
|
|
1015
1574
|
*/
|
|
1016
1575
|
dependsOn?: string[];
|
|
1576
|
+
value?: never;
|
|
1577
|
+
functionName?: never;
|
|
1578
|
+
http?: never;
|
|
1579
|
+
asyncFunctionName?: never;
|
|
1580
|
+
responseExpression?: never;
|
|
1017
1581
|
}
|
|
1018
1582
|
/**
|
|
1019
|
-
*
|
|
1583
|
+
* Base for value derivations.
|
|
1020
1584
|
*
|
|
1021
1585
|
* @internal
|
|
1022
1586
|
*/
|
|
1023
|
-
interface
|
|
1587
|
+
interface ValueDerivationBase extends SharedDerivationFields {
|
|
1588
|
+
source?: never;
|
|
1024
1589
|
/**
|
|
1025
|
-
*
|
|
1026
|
-
*
|
|
1590
|
+
* Static value to set on this field.
|
|
1591
|
+
*
|
|
1592
|
+
* Use when the derived value is a constant.
|
|
1593
|
+
*
|
|
1594
|
+
* @example
|
|
1595
|
+
* ```typescript
|
|
1596
|
+
* value: '+1' // String
|
|
1597
|
+
* value: 100 // Number
|
|
1598
|
+
* value: true // Boolean
|
|
1599
|
+
* value: { code: 'US' } // Object
|
|
1600
|
+
* ```
|
|
1027
1601
|
*/
|
|
1028
|
-
|
|
1029
|
-
/**
|
|
1030
|
-
|
|
1602
|
+
value: unknown;
|
|
1603
|
+
/**
|
|
1604
|
+
* Explicit field dependencies for value derivations.
|
|
1605
|
+
*
|
|
1606
|
+
* For `value` (static), no dependencies are needed.
|
|
1607
|
+
* Provide `dependsOn` to conditionally re-evaluate when specific fields change.
|
|
1608
|
+
*/
|
|
1609
|
+
dependsOn?: string[];
|
|
1610
|
+
expression?: never;
|
|
1611
|
+
functionName?: never;
|
|
1612
|
+
http?: never;
|
|
1613
|
+
asyncFunctionName?: never;
|
|
1614
|
+
responseExpression?: never;
|
|
1031
1615
|
}
|
|
1032
1616
|
/**
|
|
1033
|
-
*
|
|
1034
|
-
*
|
|
1035
|
-
* Use this for self-transforms (lowercase, trim, format) to avoid
|
|
1036
|
-
* interrupting the user while they're actively typing.
|
|
1617
|
+
* Base for custom sync function derivations.
|
|
1037
1618
|
*
|
|
1038
1619
|
* @internal
|
|
1039
1620
|
*/
|
|
1040
|
-
interface
|
|
1621
|
+
interface FunctionDerivationBase extends SharedDerivationFields {
|
|
1622
|
+
source?: never;
|
|
1041
1623
|
/**
|
|
1042
|
-
*
|
|
1043
|
-
*
|
|
1624
|
+
* Name of a registered custom derivation function.
|
|
1625
|
+
*
|
|
1626
|
+
* The function receives the evaluation context and returns the derived value.
|
|
1627
|
+
* Register functions in `customFnConfig.derivations`.
|
|
1628
|
+
*
|
|
1629
|
+
* @example
|
|
1630
|
+
* ```typescript
|
|
1631
|
+
* functionName: 'getCurrencyForCountry'
|
|
1632
|
+
* functionName: 'calculateTax'
|
|
1633
|
+
* functionName: 'formatPhoneNumber'
|
|
1634
|
+
* ```
|
|
1044
1635
|
*/
|
|
1045
|
-
|
|
1636
|
+
functionName: string;
|
|
1046
1637
|
/**
|
|
1047
|
-
*
|
|
1048
|
-
*
|
|
1638
|
+
* Explicit field dependencies for function derivations.
|
|
1639
|
+
*
|
|
1640
|
+
* If not provided, defaults to all fields ('*').
|
|
1641
|
+
* Specify `dependsOn` for better performance when the function only
|
|
1642
|
+
* depends on a subset of fields.
|
|
1643
|
+
*
|
|
1644
|
+
* @example
|
|
1645
|
+
* ```typescript
|
|
1646
|
+
* // Only re-evaluate when country changes
|
|
1647
|
+
* {
|
|
1648
|
+
* key: 'currency',
|
|
1649
|
+
* logic: [{
|
|
1650
|
+
* type: 'derivation',
|
|
1651
|
+
* functionName: 'getCurrencyForCountry',
|
|
1652
|
+
* dependsOn: ['country']
|
|
1653
|
+
* }]
|
|
1654
|
+
* }
|
|
1655
|
+
* ```
|
|
1049
1656
|
*/
|
|
1050
|
-
|
|
1657
|
+
dependsOn?: string[];
|
|
1658
|
+
value?: never;
|
|
1659
|
+
expression?: never;
|
|
1660
|
+
http?: never;
|
|
1661
|
+
asyncFunctionName?: never;
|
|
1662
|
+
responseExpression?: never;
|
|
1051
1663
|
}
|
|
1664
|
+
/**
|
|
1665
|
+
* HTTP derivation that evaluates immediately on change (default).
|
|
1666
|
+
* @internal
|
|
1667
|
+
*/
|
|
1668
|
+
type OnChangeHttpDerivationLogicConfig = HttpDerivationBase & ImmediateDerivationTrigger;
|
|
1669
|
+
/**
|
|
1670
|
+
* HTTP derivation that evaluates after a debounce period.
|
|
1671
|
+
* @internal
|
|
1672
|
+
*/
|
|
1673
|
+
type DebouncedHttpDerivationLogicConfig = HttpDerivationBase & DebouncedDerivationTrigger;
|
|
1674
|
+
/**
|
|
1675
|
+
* Async function derivation that evaluates immediately on change (default).
|
|
1676
|
+
* @internal
|
|
1677
|
+
*/
|
|
1678
|
+
type OnChangeAsyncFunctionDerivationLogicConfig = AsyncFunctionDerivationBase & ImmediateDerivationTrigger;
|
|
1679
|
+
/**
|
|
1680
|
+
* Async function derivation that evaluates after a debounce period.
|
|
1681
|
+
* @internal
|
|
1682
|
+
*/
|
|
1683
|
+
type DebouncedAsyncFunctionDerivationLogicConfig = AsyncFunctionDerivationBase & DebouncedDerivationTrigger;
|
|
1684
|
+
/**
|
|
1685
|
+
* Expression derivation that evaluates immediately on change (default).
|
|
1686
|
+
* @internal
|
|
1687
|
+
*/
|
|
1688
|
+
type OnChangeExpressionDerivationLogicConfig = ExpressionDerivationBase & ImmediateDerivationTrigger;
|
|
1689
|
+
/**
|
|
1690
|
+
* Expression derivation that evaluates after a debounce period.
|
|
1691
|
+
* @internal
|
|
1692
|
+
*/
|
|
1693
|
+
type DebouncedExpressionDerivationLogicConfig = ExpressionDerivationBase & DebouncedDerivationTrigger;
|
|
1694
|
+
/**
|
|
1695
|
+
* Value derivation that evaluates immediately on change (default).
|
|
1696
|
+
* @internal
|
|
1697
|
+
*/
|
|
1698
|
+
type OnChangeValueDerivationLogicConfig = ValueDerivationBase & ImmediateDerivationTrigger;
|
|
1699
|
+
/**
|
|
1700
|
+
* Value derivation that evaluates after a debounce period.
|
|
1701
|
+
* @internal
|
|
1702
|
+
*/
|
|
1703
|
+
type DebouncedValueDerivationLogicConfig = ValueDerivationBase & DebouncedDerivationTrigger;
|
|
1704
|
+
/**
|
|
1705
|
+
* Function derivation that evaluates immediately on change (default).
|
|
1706
|
+
* @internal
|
|
1707
|
+
*/
|
|
1708
|
+
type OnChangeFunctionDerivationLogicConfig = FunctionDerivationBase & ImmediateDerivationTrigger;
|
|
1709
|
+
/**
|
|
1710
|
+
* Function derivation that evaluates after a debounce period.
|
|
1711
|
+
* @internal
|
|
1712
|
+
*/
|
|
1713
|
+
type DebouncedFunctionDerivationLogicConfig = FunctionDerivationBase & DebouncedDerivationTrigger;
|
|
1052
1714
|
/**
|
|
1053
1715
|
* Configuration for value derivation logic.
|
|
1054
1716
|
*
|
|
@@ -1109,16 +1771,162 @@ interface DebouncedDerivationLogicConfig extends BaseDerivationLogicConfig {
|
|
|
1109
1771
|
*
|
|
1110
1772
|
* @public
|
|
1111
1773
|
*/
|
|
1112
|
-
type DerivationLogicConfig =
|
|
1774
|
+
type DerivationLogicConfig = OnChangeHttpDerivationLogicConfig | DebouncedHttpDerivationLogicConfig | OnChangeAsyncFunctionDerivationLogicConfig | DebouncedAsyncFunctionDerivationLogicConfig | OnChangeExpressionDerivationLogicConfig | DebouncedExpressionDerivationLogicConfig | OnChangeValueDerivationLogicConfig | DebouncedValueDerivationLogicConfig | OnChangeFunctionDerivationLogicConfig | DebouncedFunctionDerivationLogicConfig;
|
|
1775
|
+
/**
|
|
1776
|
+
* Base configuration for property derivation logic.
|
|
1777
|
+
*
|
|
1778
|
+
* Property derivations compute values for field properties (like `minDate`, `options`,
|
|
1779
|
+
* `label`, `placeholder`) based on form values and external data. Unlike value derivations
|
|
1780
|
+
* which set the field's value, property derivations update component input properties.
|
|
1781
|
+
*
|
|
1782
|
+
* All property derivations are self-targeting: the logic is placed on the field whose
|
|
1783
|
+
* property should be derived.
|
|
1784
|
+
*
|
|
1785
|
+
* @internal
|
|
1786
|
+
*/
|
|
1787
|
+
interface BasePropertyDerivationLogicConfig {
|
|
1788
|
+
/**
|
|
1789
|
+
* Logic type identifier for property derivation.
|
|
1790
|
+
*/
|
|
1791
|
+
type: 'propertyDerivation';
|
|
1792
|
+
/**
|
|
1793
|
+
* The target property to set on the field component.
|
|
1794
|
+
*
|
|
1795
|
+
* **Depth limit (max 2 levels):** Only simple and single-dot-nested paths are
|
|
1796
|
+
* supported. Paths with 2+ dots (e.g., `'a.b.c'`) will throw a `DynamicFormError`
|
|
1797
|
+
* at runtime. This is an architectural constraint of the override merging strategy.
|
|
1798
|
+
*
|
|
1799
|
+
* Supported formats:
|
|
1800
|
+
* - Simple: `'minDate'`, `'options'`, `'label'`, `'placeholder'`
|
|
1801
|
+
* - Nested (1 dot): `'props.appearance'`, `'meta.autocomplete'`
|
|
1802
|
+
*
|
|
1803
|
+
* **Note:** There is no compile-time validation that the property name matches
|
|
1804
|
+
* an actual input on the field component. Typos will silently write to the
|
|
1805
|
+
* override store with no visible effect. A dev-mode warning is emitted when
|
|
1806
|
+
* the override key doesn't match any existing input property.
|
|
1807
|
+
*
|
|
1808
|
+
* @example
|
|
1809
|
+
* ```typescript
|
|
1810
|
+
* targetProperty: 'minDate' // ✅ Simple property
|
|
1811
|
+
* targetProperty: 'options' // ✅ Simple property
|
|
1812
|
+
* targetProperty: 'props.appearance' // ✅ Single-nested property
|
|
1813
|
+
* targetProperty: 'a.b.c' // ❌ Throws DynamicFormError (too deep)
|
|
1814
|
+
* ```
|
|
1815
|
+
*/
|
|
1816
|
+
targetProperty: string;
|
|
1817
|
+
/**
|
|
1818
|
+
* Optional name for this derivation for debugging purposes.
|
|
1819
|
+
*
|
|
1820
|
+
* When provided, this name appears in property derivation debug logs.
|
|
1821
|
+
*/
|
|
1822
|
+
debugName?: string;
|
|
1823
|
+
/**
|
|
1824
|
+
* Condition that determines when this property derivation applies.
|
|
1825
|
+
*
|
|
1826
|
+
* Defaults to `true` (always apply).
|
|
1827
|
+
*
|
|
1828
|
+
* Note: FormStateCondition is not supported for property derivations.
|
|
1829
|
+
*/
|
|
1830
|
+
condition?: ConditionalExpression | boolean;
|
|
1831
|
+
/**
|
|
1832
|
+
* Static value to set on the target property.
|
|
1833
|
+
*
|
|
1834
|
+
* Mutually exclusive with `expression` and `functionName`.
|
|
1835
|
+
*/
|
|
1836
|
+
value?: unknown;
|
|
1837
|
+
/**
|
|
1838
|
+
* JavaScript expression to evaluate for the derived property value.
|
|
1839
|
+
*
|
|
1840
|
+
* Has access to `formValue` object containing all form values.
|
|
1841
|
+
* For array fields, `formValue` is scoped to the current array item.
|
|
1842
|
+
*
|
|
1843
|
+
* Mutually exclusive with `value` and `functionName`.
|
|
1844
|
+
*
|
|
1845
|
+
* @example
|
|
1846
|
+
* ```typescript
|
|
1847
|
+
* expression: 'formValue.startDate'
|
|
1848
|
+
* expression: 'formValue.quantity > 10 ? "bulk" : "standard"'
|
|
1849
|
+
* ```
|
|
1850
|
+
*/
|
|
1851
|
+
expression?: string;
|
|
1852
|
+
/**
|
|
1853
|
+
* Name of a registered custom property derivation function.
|
|
1854
|
+
*
|
|
1855
|
+
* Register functions in `customFnConfig.propertyDerivations`.
|
|
1856
|
+
* Mutually exclusive with `value` and `expression`.
|
|
1857
|
+
*/
|
|
1858
|
+
functionName?: string;
|
|
1859
|
+
/**
|
|
1860
|
+
* Explicit field dependencies for property derivations.
|
|
1861
|
+
*
|
|
1862
|
+
* When using `functionName`, you can optionally specify which fields
|
|
1863
|
+
* the function depends on. If not provided with `functionName`, defaults
|
|
1864
|
+
* to all fields ('*').
|
|
1865
|
+
* For `expression`, dependencies are automatically extracted.
|
|
1866
|
+
* For `value`, no dependencies are needed (static value).
|
|
1867
|
+
*/
|
|
1868
|
+
dependsOn?: string[];
|
|
1869
|
+
}
|
|
1870
|
+
/**
|
|
1871
|
+
* Property derivation that evaluates immediately on change (default).
|
|
1872
|
+
*
|
|
1873
|
+
* @internal
|
|
1874
|
+
*/
|
|
1875
|
+
interface OnChangePropertyDerivationLogicConfig extends BasePropertyDerivationLogicConfig {
|
|
1876
|
+
/**
|
|
1877
|
+
* Trigger for immediate evaluation.
|
|
1878
|
+
* @default 'onChange'
|
|
1879
|
+
*/
|
|
1880
|
+
trigger?: 'onChange';
|
|
1881
|
+
/** Not allowed for onChange trigger */
|
|
1882
|
+
debounceMs?: never;
|
|
1883
|
+
}
|
|
1884
|
+
/**
|
|
1885
|
+
* Property derivation that evaluates after a debounce period.
|
|
1886
|
+
*
|
|
1887
|
+
* @internal
|
|
1888
|
+
*/
|
|
1889
|
+
interface DebouncedPropertyDerivationLogicConfig extends BasePropertyDerivationLogicConfig {
|
|
1890
|
+
/**
|
|
1891
|
+
* Trigger for debounced evaluation.
|
|
1892
|
+
*/
|
|
1893
|
+
trigger: 'debounced';
|
|
1894
|
+
/**
|
|
1895
|
+
* Debounce duration in milliseconds.
|
|
1896
|
+
* @default 500
|
|
1897
|
+
*/
|
|
1898
|
+
debounceMs?: number;
|
|
1899
|
+
}
|
|
1900
|
+
/**
|
|
1901
|
+
* Configuration for property derivation logic.
|
|
1902
|
+
*
|
|
1903
|
+
* @deprecated Use `DerivationLogicConfig` with `targetProperty` instead.
|
|
1904
|
+
* The unified `type: 'derivation'` with `targetProperty` provides the same
|
|
1905
|
+
* functionality with a simpler API surface.
|
|
1906
|
+
*
|
|
1907
|
+
* ```typescript
|
|
1908
|
+
* // Before (deprecated)
|
|
1909
|
+
* { type: 'propertyDerivation', targetProperty: 'minDate', expression: '...' }
|
|
1910
|
+
*
|
|
1911
|
+
* // After (preferred)
|
|
1912
|
+
* { type: 'derivation', targetProperty: 'minDate', expression: '...' }
|
|
1913
|
+
* ```
|
|
1914
|
+
*
|
|
1915
|
+
* Will be removed in a future minor version.
|
|
1916
|
+
*
|
|
1917
|
+
* @public
|
|
1918
|
+
*/
|
|
1919
|
+
type PropertyDerivationLogicConfig = OnChangePropertyDerivationLogicConfig | DebouncedPropertyDerivationLogicConfig;
|
|
1113
1920
|
/**
|
|
1114
1921
|
* Union type for all logic configurations.
|
|
1115
1922
|
*
|
|
1116
1923
|
* - `StateLogicConfig`: For field state changes (hidden, readonly, disabled, required)
|
|
1117
1924
|
* - `DerivationLogicConfig`: For value derivation
|
|
1925
|
+
* - `PropertyDerivationLogicConfig`: For property derivation
|
|
1118
1926
|
*
|
|
1119
1927
|
* @public
|
|
1120
1928
|
*/
|
|
1121
|
-
type LogicConfig = StateLogicConfig | DerivationLogicConfig;
|
|
1929
|
+
type LogicConfig = StateLogicConfig | DerivationLogicConfig | PropertyDerivationLogicConfig;
|
|
1122
1930
|
/**
|
|
1123
1931
|
* Log level for derivation debug output.
|
|
1124
1932
|
*
|
|
@@ -1138,6 +1946,221 @@ type DerivationLogLevel = 'none' | 'summary' | 'verbose';
|
|
|
1138
1946
|
* @public
|
|
1139
1947
|
*/
|
|
1140
1948
|
declare function isFormStateCondition(condition: StateLogicConfig['condition'] | DerivationLogicConfig['condition']): condition is FormStateCondition;
|
|
1949
|
+
/**
|
|
1950
|
+
* Type guard to check if a logic config is a PropertyDerivationLogicConfig.
|
|
1951
|
+
*
|
|
1952
|
+
* @deprecated Use `isDerivationLogicConfig(config) && hasTargetProperty(config)` instead.
|
|
1953
|
+
* Will be removed in a future minor version.
|
|
1954
|
+
*
|
|
1955
|
+
* @param config - The logic config to check
|
|
1956
|
+
* @returns true if the config is for property derivation
|
|
1957
|
+
*
|
|
1958
|
+
* @public
|
|
1959
|
+
*/
|
|
1960
|
+
declare function isPropertyDerivationLogicConfig(config: LogicConfig): config is PropertyDerivationLogicConfig;
|
|
1961
|
+
|
|
1962
|
+
/**
|
|
1963
|
+
* Interface for dynamic forms logger implementations.
|
|
1964
|
+
* Allows custom logging integrations (Sentry, DataDog, etc.)
|
|
1965
|
+
*/
|
|
1966
|
+
interface Logger {
|
|
1967
|
+
/**
|
|
1968
|
+
* Log a debug message.
|
|
1969
|
+
* Use for detailed diagnostic information during development.
|
|
1970
|
+
*/
|
|
1971
|
+
debug(message: string, ...args: unknown[]): void;
|
|
1972
|
+
/**
|
|
1973
|
+
* Log an info message.
|
|
1974
|
+
* Use for general operational information.
|
|
1975
|
+
*/
|
|
1976
|
+
info(message: string, ...args: unknown[]): void;
|
|
1977
|
+
/**
|
|
1978
|
+
* Log a warning message.
|
|
1979
|
+
* Use for potentially problematic situations that don't prevent operation.
|
|
1980
|
+
*/
|
|
1981
|
+
warn(message: string, ...args: unknown[]): void;
|
|
1982
|
+
/**
|
|
1983
|
+
* Log an error message.
|
|
1984
|
+
* Use for error conditions that may affect functionality.
|
|
1985
|
+
*/
|
|
1986
|
+
error(message: string, ...args: unknown[]): void;
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
/**
|
|
1990
|
+
* Context for resolving button disabled state.
|
|
1991
|
+
*
|
|
1992
|
+
* @public
|
|
1993
|
+
*/
|
|
1994
|
+
interface ButtonLogicContext {
|
|
1995
|
+
/** The form's FieldTree instance (supports both string and number keys for array indices) */
|
|
1996
|
+
form: FieldTree<unknown, string | number>;
|
|
1997
|
+
/** Form-level options */
|
|
1998
|
+
formOptions?: FormOptions;
|
|
1999
|
+
/** Field-level logic array (if provided) */
|
|
2000
|
+
fieldLogic?: LogicConfig[];
|
|
2001
|
+
/** Explicit disabled state from field definition */
|
|
2002
|
+
explicitlyDisabled?: boolean;
|
|
2003
|
+
/** Current page validity signal (for paged forms) */
|
|
2004
|
+
currentPageValid?: Signal<boolean>;
|
|
2005
|
+
/** Current form value for evaluating conditional expressions */
|
|
2006
|
+
formValue?: unknown;
|
|
2007
|
+
/** Optional logger for diagnostic output. Falls back to no-op logger if not provided. */
|
|
2008
|
+
logger?: Logger;
|
|
2009
|
+
}
|
|
2010
|
+
/**
|
|
2011
|
+
* Allowed logic types for non-form-bound elements (buttons, text fields, etc.).
|
|
2012
|
+
* These elements only support hidden and disabled states - not readonly or required
|
|
2013
|
+
* since they don't participate in form validation.
|
|
2014
|
+
*
|
|
2015
|
+
* @public
|
|
2016
|
+
*/
|
|
2017
|
+
type NonFieldLogicType = 'hidden' | 'disabled';
|
|
2018
|
+
/**
|
|
2019
|
+
* Logic config restricted to types valid for non-form-bound elements.
|
|
2020
|
+
* This is a subset of LogicConfig that only includes hidden and disabled types.
|
|
2021
|
+
*
|
|
2022
|
+
* @public
|
|
2023
|
+
*/
|
|
2024
|
+
type NonFieldLogicConfig = LogicConfig & {
|
|
2025
|
+
type: NonFieldLogicType;
|
|
2026
|
+
};
|
|
2027
|
+
/**
|
|
2028
|
+
* Context for resolving state (hidden/disabled) for non-form-bound elements.
|
|
2029
|
+
*
|
|
2030
|
+
* This is a generalized context that can be used by any field mapper (buttons, text fields,
|
|
2031
|
+
* or any non-form-bound elements) to evaluate logic from a `logic` array.
|
|
2032
|
+
*
|
|
2033
|
+
* Note: While this context accepts the full `LogicConfig[]` for flexibility, the resolver
|
|
2034
|
+
* functions only process hidden and disabled types. Other logic types (readonly, required,
|
|
2035
|
+
* derivation) are ignored for non-form-bound elements.
|
|
2036
|
+
*
|
|
2037
|
+
* @public
|
|
2038
|
+
*/
|
|
2039
|
+
interface NonFieldLogicContext {
|
|
2040
|
+
/** The form's FieldTree instance (supports both string and number keys for array indices) */
|
|
2041
|
+
form: FieldTree<unknown, string | number>;
|
|
2042
|
+
/**
|
|
2043
|
+
* Field-level logic array containing conditions.
|
|
2044
|
+
* Accepts full LogicConfig[] for compatibility, but only hidden and disabled types are processed.
|
|
2045
|
+
*/
|
|
2046
|
+
fieldLogic?: LogicConfig[];
|
|
2047
|
+
/** Explicit state from field definition (e.g., `hidden: true` or `disabled: true`) */
|
|
2048
|
+
explicitValue?: boolean;
|
|
2049
|
+
/** Current form value for evaluating conditional expressions */
|
|
2050
|
+
formValue?: unknown;
|
|
2051
|
+
/** Optional logger for diagnostic output. Falls back to no-op logger if not provided. */
|
|
2052
|
+
logger?: Logger;
|
|
2053
|
+
}
|
|
2054
|
+
/**
|
|
2055
|
+
* Resolves the disabled state for a submit button.
|
|
2056
|
+
*
|
|
2057
|
+
* The disabled state is determined by (in order of precedence):
|
|
2058
|
+
* 1. Explicit `disabled: true` on the field definition
|
|
2059
|
+
* 2. Field-level `logic` array (if present, overrides form-level defaults)
|
|
2060
|
+
* 3. Form-level `options.submitButton` defaults
|
|
2061
|
+
*
|
|
2062
|
+
* @param ctx - The button logic context
|
|
2063
|
+
* @returns A computed signal that returns true when the button should be disabled
|
|
2064
|
+
*
|
|
2065
|
+
* @example
|
|
2066
|
+
* ```typescript
|
|
2067
|
+
* const disabled = resolveSubmitButtonDisabled({
|
|
2068
|
+
* form: formInstance,
|
|
2069
|
+
* formOptions: config.options,
|
|
2070
|
+
* fieldLogic: buttonField.logic,
|
|
2071
|
+
* explicitlyDisabled: buttonField.disabled,
|
|
2072
|
+
* });
|
|
2073
|
+
*
|
|
2074
|
+
* // Use in template
|
|
2075
|
+
* <button [disabled]="disabled()">Submit</button>
|
|
2076
|
+
* ```
|
|
2077
|
+
*
|
|
2078
|
+
* @public
|
|
2079
|
+
*/
|
|
2080
|
+
declare function resolveSubmitButtonDisabled(ctx: ButtonLogicContext): Signal<boolean>;
|
|
2081
|
+
/**
|
|
2082
|
+
* Resolves the disabled state for a next page button.
|
|
2083
|
+
*
|
|
2084
|
+
* The disabled state is determined by (in order of precedence):
|
|
2085
|
+
* 1. Explicit `disabled: true` on the field definition
|
|
2086
|
+
* 2. Field-level `logic` array (if present, overrides form-level defaults)
|
|
2087
|
+
* 3. Form-level `options.nextButton` defaults
|
|
2088
|
+
*
|
|
2089
|
+
* @param ctx - The button logic context
|
|
2090
|
+
* @returns A computed signal that returns true when the button should be disabled
|
|
2091
|
+
*
|
|
2092
|
+
* @example
|
|
2093
|
+
* ```typescript
|
|
2094
|
+
* const disabled = resolveNextButtonDisabled({
|
|
2095
|
+
* form: formInstance,
|
|
2096
|
+
* formOptions: config.options,
|
|
2097
|
+
* fieldLogic: buttonField.logic,
|
|
2098
|
+
* explicitlyDisabled: buttonField.disabled,
|
|
2099
|
+
* currentPageValid: pageOrchestrator.currentPageValid,
|
|
2100
|
+
* });
|
|
2101
|
+
* ```
|
|
2102
|
+
*
|
|
2103
|
+
* @public
|
|
2104
|
+
*/
|
|
2105
|
+
declare function resolveNextButtonDisabled(ctx: ButtonLogicContext): Signal<boolean>;
|
|
2106
|
+
/**
|
|
2107
|
+
* Evaluates the hidden state for non-form-bound elements synchronously.
|
|
2108
|
+
*
|
|
2109
|
+
* This is a pure function (no signal allocation) that can be called directly inside
|
|
2110
|
+
* an existing `computed()` block without creating ephemeral signal allocations.
|
|
2111
|
+
*
|
|
2112
|
+
* The hidden state is determined by (in order of precedence):
|
|
2113
|
+
* 1. Explicit `hidden: true` on the field definition
|
|
2114
|
+
* 2. Field-level `logic` array with `type: 'hidden'` conditions
|
|
2115
|
+
*
|
|
2116
|
+
* @param ctx - The context containing form, logic array, and explicit hidden state
|
|
2117
|
+
* @returns true when the element should be hidden
|
|
2118
|
+
*
|
|
2119
|
+
* @public
|
|
2120
|
+
*/
|
|
2121
|
+
declare function evaluateNonFieldHidden(ctx: NonFieldLogicContext): boolean;
|
|
2122
|
+
/**
|
|
2123
|
+
* Resolves the hidden state for non-form-bound elements as a reactive signal.
|
|
2124
|
+
*
|
|
2125
|
+
* Wraps {@link evaluateNonFieldHidden} in a `computed()` for use cases where a standalone
|
|
2126
|
+
* signal is needed. When calling from inside an existing `computed()`, prefer
|
|
2127
|
+
* {@link evaluateNonFieldHidden} directly to avoid ephemeral signal allocation.
|
|
2128
|
+
*
|
|
2129
|
+
* @param ctx - The context containing form, logic array, and explicit hidden state
|
|
2130
|
+
* @returns A computed signal that returns true when the element should be hidden
|
|
2131
|
+
*
|
|
2132
|
+
* @public
|
|
2133
|
+
*/
|
|
2134
|
+
declare function resolveNonFieldHidden(ctx: NonFieldLogicContext): Signal<boolean>;
|
|
2135
|
+
/**
|
|
2136
|
+
* Evaluates the disabled state for non-form-bound elements synchronously.
|
|
2137
|
+
*
|
|
2138
|
+
* This is a pure function (no signal allocation) that can be called directly inside
|
|
2139
|
+
* an existing `computed()` block without creating ephemeral signal allocations.
|
|
2140
|
+
*
|
|
2141
|
+
* The disabled state is determined by (in order of precedence):
|
|
2142
|
+
* 1. Explicit `disabled: true` on the field definition
|
|
2143
|
+
* 2. Field-level `logic` array with `type: 'disabled'` conditions
|
|
2144
|
+
*
|
|
2145
|
+
* @param ctx - The context containing form, logic array, and explicit disabled state
|
|
2146
|
+
* @returns true when the element should be disabled
|
|
2147
|
+
*
|
|
2148
|
+
* @public
|
|
2149
|
+
*/
|
|
2150
|
+
declare function evaluateNonFieldDisabled(ctx: NonFieldLogicContext): boolean;
|
|
2151
|
+
/**
|
|
2152
|
+
* Resolves the disabled state for non-form-bound elements as a reactive signal.
|
|
2153
|
+
*
|
|
2154
|
+
* Wraps {@link evaluateNonFieldDisabled} in a `computed()` for use cases where a standalone
|
|
2155
|
+
* signal is needed. When calling from inside an existing `computed()`, prefer
|
|
2156
|
+
* {@link evaluateNonFieldDisabled} directly to avoid ephemeral signal allocation.
|
|
2157
|
+
*
|
|
2158
|
+
* @param ctx - The context containing form, logic array, and explicit disabled state
|
|
2159
|
+
* @returns A computed signal that returns true when the element should be disabled
|
|
2160
|
+
*
|
|
2161
|
+
* @public
|
|
2162
|
+
*/
|
|
2163
|
+
declare function resolveNonFieldDisabled(ctx: NonFieldLogicContext): Signal<boolean>;
|
|
1141
2164
|
|
|
1142
2165
|
/**
|
|
1143
2166
|
* Text element type for rendering different HTML text elements
|
|
@@ -1155,8 +2178,12 @@ type TextProps = {
|
|
|
1155
2178
|
*/
|
|
1156
2179
|
interface TextField extends FieldDef<TextProps> {
|
|
1157
2180
|
type: 'text';
|
|
1158
|
-
/**
|
|
1159
|
-
|
|
2181
|
+
/**
|
|
2182
|
+
* Logic rules for conditional visibility.
|
|
2183
|
+
* Text fields only support 'hidden' logic type since they are display-only
|
|
2184
|
+
* and don't participate in form validation (no disabled/readonly/required).
|
|
2185
|
+
*/
|
|
2186
|
+
readonly logic?: NonFieldLogicConfig[];
|
|
1160
2187
|
}
|
|
1161
2188
|
|
|
1162
2189
|
/**
|
|
@@ -1175,7 +2202,7 @@ interface FieldRegistryContainers {
|
|
|
1175
2202
|
page: PageField;
|
|
1176
2203
|
row: RowField;
|
|
1177
2204
|
group: GroupField;
|
|
1178
|
-
array: ArrayField;
|
|
2205
|
+
array: ArrayField | SimplifiedArrayField;
|
|
1179
2206
|
}
|
|
1180
2207
|
/**
|
|
1181
2208
|
* Leaf fields registry - augment this interface to add custom leaf fields
|
|
@@ -1290,18 +2317,28 @@ type UnionToIntersection<U> = (U extends U ? (k: U) => void : never) extends (k:
|
|
|
1290
2317
|
*/
|
|
1291
2318
|
type Depth = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
1292
2319
|
/**
|
|
1293
|
-
* Widens literal types to their primitive equivalents.
|
|
2320
|
+
* Widens literal types to their primitive equivalents, recursively for objects and arrays.
|
|
1294
2321
|
* This prevents `as const` from over-narrowing types like `''` to literal `''` instead of `string`.
|
|
1295
2322
|
*
|
|
2323
|
+
* Depth-limited to prevent slow type checking on deeply nested `as const` objects.
|
|
2324
|
+
* Falls back to `T` (unwidened) when depth is exhausted.
|
|
2325
|
+
*
|
|
2326
|
+
* Note: For object types, `readonly` modifiers are intentionally stripped (`-readonly`)
|
|
2327
|
+
* so that inferred form value types have mutable properties. This is by design since
|
|
2328
|
+
* `as const` adds `readonly` to all properties, but form values should be mutable.
|
|
2329
|
+
*
|
|
1296
2330
|
* @example
|
|
1297
2331
|
* ```typescript
|
|
1298
2332
|
* type A = Widen<''>; // string
|
|
1299
2333
|
* type B = Widen<false>; // boolean
|
|
1300
2334
|
* type C = Widen<42>; // number
|
|
1301
2335
|
* type D = Widen<string[]>; // string[]
|
|
2336
|
+
* type E = Widen<{ name: 'Jane' }>; // { name: string }
|
|
1302
2337
|
* ```
|
|
1303
2338
|
*/
|
|
1304
|
-
type Widen<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T
|
|
2339
|
+
type Widen<T, D extends number = 5> = [D] extends [never] ? T : T extends string ? string : T extends number ? number : T extends boolean ? boolean : T extends readonly (infer U)[] ? Widen<U, Depth[D]>[] : T extends Record<string, unknown> ? {
|
|
2340
|
+
-readonly [K in keyof T]: Widen<T[K], Depth[D]>;
|
|
2341
|
+
} : T;
|
|
1305
2342
|
/**
|
|
1306
2343
|
* Infer value type based on field type and props.
|
|
1307
2344
|
* - Slider fields: always number
|
|
@@ -1325,6 +2362,21 @@ type MaybeOptional<T, V> = T extends {
|
|
|
1325
2362
|
} ? V : T extends {
|
|
1326
2363
|
required: true;
|
|
1327
2364
|
} ? V : V | undefined;
|
|
2365
|
+
/**
|
|
2366
|
+
* Infers the value type for an array field.
|
|
2367
|
+
* Handles three cases:
|
|
2368
|
+
* 1. Simplified array with template + value → infer item type from value
|
|
2369
|
+
* 2. Simplified array with template only → unknown[]
|
|
2370
|
+
* 3. Full-API array with fields → infer from children
|
|
2371
|
+
*/
|
|
2372
|
+
type InferArrayValue<T, D extends number> = T extends {
|
|
2373
|
+
template: unknown;
|
|
2374
|
+
value: readonly (infer Item)[];
|
|
2375
|
+
} ? Widen<Item>[] : T extends {
|
|
2376
|
+
template: unknown;
|
|
2377
|
+
} ? unknown[] : T extends {
|
|
2378
|
+
fields: infer F;
|
|
2379
|
+
} ? F extends RegisteredFieldTypes[] ? InferFormValueWithDepth<F, Depth[D]>[] : unknown[] : unknown[];
|
|
1328
2380
|
/**
|
|
1329
2381
|
* Process a single field and determine its contribution to the form value type
|
|
1330
2382
|
*/
|
|
@@ -1342,11 +2394,8 @@ type ProcessField<T, D extends number = 5> = [D] extends [never] ? Record<string
|
|
|
1342
2394
|
} : never : T extends {
|
|
1343
2395
|
type: 'array';
|
|
1344
2396
|
key: infer K;
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
[P in K]: InferFormValueWithDepth<F, Depth[D]>[];
|
|
1348
|
-
} : {
|
|
1349
|
-
[P in K]: unknown[];
|
|
2397
|
+
} ? K extends string ? {
|
|
2398
|
+
[P in K]: InferArrayValue<T, D>;
|
|
1350
2399
|
} : never : T extends {
|
|
1351
2400
|
type: 'text';
|
|
1352
2401
|
} ? never : T extends {
|
|
@@ -1399,6 +2448,31 @@ type ExtractFields<T> = T extends {
|
|
|
1399
2448
|
*/
|
|
1400
2449
|
type InferFormValue$1<T> = ExtractFields<T> extends readonly RegisteredFieldTypes[] ? InferFormValueWithDepth<ExtractFields<T> extends RegisteredFieldTypes[] ? ExtractFields<T> : [...ExtractFields<T>], 5> : never;
|
|
1401
2450
|
|
|
2451
|
+
/**
|
|
2452
|
+
* Mapping configuration for interpreting HTTP responses as validation results.
|
|
2453
|
+
*
|
|
2454
|
+
* Expressions in `validWhen` and `errorParams` are evaluated with scope `{ response }` only —
|
|
2455
|
+
* they do NOT have access to `formValue`, `fieldValue`, or the full `EvaluationContext`.
|
|
2456
|
+
* This is intentional for v1: response mapping is purely about interpreting the HTTP response.
|
|
2457
|
+
*/
|
|
2458
|
+
interface HttpValidationResponseMapping {
|
|
2459
|
+
/**
|
|
2460
|
+
* Expression evaluated with scope `{ response }`. Truthy = valid (no error).
|
|
2461
|
+
*
|
|
2462
|
+
* NOTE: This is the OPPOSITE of the function-based HttpCustomValidator.onSuccess
|
|
2463
|
+
* convention, which maps successful HTTP responses to validation errors.
|
|
2464
|
+
* Here, truthy means "validation passed".
|
|
2465
|
+
*/
|
|
2466
|
+
validWhen: string;
|
|
2467
|
+
/** Error kind returned when validation fails — maps to field-level `validationMessages` */
|
|
2468
|
+
errorKind: string;
|
|
2469
|
+
/**
|
|
2470
|
+
* Parameters to include in the validation error for message interpolation.
|
|
2471
|
+
* Map of parameter names to expressions evaluated against `{ response }`.
|
|
2472
|
+
*/
|
|
2473
|
+
errorParams?: Record<string, string>;
|
|
2474
|
+
}
|
|
2475
|
+
|
|
1402
2476
|
/**
|
|
1403
2477
|
* Base configuration shared by all validators
|
|
1404
2478
|
*/
|
|
@@ -1450,34 +2524,80 @@ interface CustomValidatorConfig extends BaseValidatorConfig {
|
|
|
1450
2524
|
errorParams?: Record<string, string>;
|
|
1451
2525
|
}
|
|
1452
2526
|
/**
|
|
1453
|
-
* Async custom validator configuration using Angular's validateAsync API
|
|
1454
|
-
* Returns Observable<ValidationError | ValidationError[] | null
|
|
2527
|
+
* Async custom validator configuration using Angular's validateAsync API.
|
|
2528
|
+
* Returns Observable<ValidationError | ValidationError[] | null>.
|
|
2529
|
+
*
|
|
2530
|
+
* Accepts both `'async'` (preferred) and `'customAsync'` (deprecated) type literals.
|
|
1455
2531
|
*/
|
|
1456
2532
|
interface AsyncValidatorConfig extends BaseValidatorConfig {
|
|
1457
|
-
/**
|
|
1458
|
-
|
|
2533
|
+
/**
|
|
2534
|
+
* Validator type identifier.
|
|
2535
|
+
*
|
|
2536
|
+
* Use `'async'` for new code. `'customAsync'` is accepted for backward compatibility
|
|
2537
|
+
* but emits a deprecation warning in dev mode.
|
|
2538
|
+
*/
|
|
2539
|
+
type: 'async' | 'customAsync';
|
|
1459
2540
|
/** Name of registered async validator function */
|
|
1460
2541
|
functionName: string;
|
|
1461
2542
|
/** Optional parameters to pass to validator function */
|
|
1462
2543
|
params?: Record<string, unknown>;
|
|
1463
2544
|
}
|
|
1464
2545
|
/**
|
|
1465
|
-
* HTTP validator configuration
|
|
1466
|
-
*
|
|
2546
|
+
* Function-based HTTP validator configuration — requires a registered function.
|
|
2547
|
+
*
|
|
2548
|
+
* Uses Angular's `validateHttp` API. The function is registered via
|
|
2549
|
+
* `customFnConfig.httpValidators`.
|
|
2550
|
+
*
|
|
2551
|
+
* Discriminated from `DeclarativeHttpValidatorConfig` by the presence of `functionName`.
|
|
2552
|
+
*
|
|
2553
|
+
* Accepts both `'http'` (preferred) and `'customHttp'` (deprecated) type literals.
|
|
2554
|
+
*
|
|
2555
|
+
* @deprecated Prefer `DeclarativeHttpValidatorConfig` (`type: 'http'` with `http` + `responseMapping`)
|
|
2556
|
+
* for fully JSON-serializable validation. Use this form only when function registration is required.
|
|
1467
2557
|
*/
|
|
1468
|
-
interface
|
|
1469
|
-
/**
|
|
1470
|
-
|
|
2558
|
+
interface FunctionHttpValidatorConfig extends BaseValidatorConfig {
|
|
2559
|
+
/**
|
|
2560
|
+
* Validator type identifier.
|
|
2561
|
+
*
|
|
2562
|
+
* Use `'http'` for new code. `'customHttp'` is accepted for backward compatibility
|
|
2563
|
+
* but emits a deprecation warning in dev mode.
|
|
2564
|
+
*/
|
|
2565
|
+
type: 'http' | 'customHttp';
|
|
1471
2566
|
/** Name of registered HTTP validator configuration */
|
|
1472
2567
|
functionName: string;
|
|
1473
2568
|
/** Optional parameters to pass to HTTP validator */
|
|
1474
2569
|
params?: Record<string, unknown>;
|
|
1475
2570
|
}
|
|
1476
2571
|
/**
|
|
1477
|
-
*
|
|
1478
|
-
|
|
2572
|
+
* @deprecated Use `FunctionHttpValidatorConfig` instead. Will be removed in a future minor version.
|
|
2573
|
+
*/
|
|
2574
|
+
type HttpValidatorConfig = FunctionHttpValidatorConfig;
|
|
2575
|
+
/**
|
|
2576
|
+
* Declarative HTTP validator configuration — fully JSON-serializable, no function registration required.
|
|
2577
|
+
*
|
|
2578
|
+
* Uses `HttpRequestConfig` to define the HTTP request and `HttpValidationResponseMapping`
|
|
2579
|
+
* to interpret the response as a validation result. Powered by Angular's `validateHttp` API.
|
|
2580
|
+
*
|
|
2581
|
+
* Discriminated from `FunctionHttpValidatorConfig` by the presence of `http` + `responseMapping`
|
|
2582
|
+
* (and absence of `functionName`).
|
|
1479
2583
|
*/
|
|
1480
|
-
|
|
2584
|
+
interface DeclarativeHttpValidatorConfig extends BaseValidatorConfig {
|
|
2585
|
+
/** Validator type identifier */
|
|
2586
|
+
type: 'http';
|
|
2587
|
+
/** HTTP request configuration with expression-based query params and body */
|
|
2588
|
+
http: HttpRequestConfig;
|
|
2589
|
+
/** Mapping that interprets the HTTP response as a validation result */
|
|
2590
|
+
responseMapping: HttpValidationResponseMapping;
|
|
2591
|
+
}
|
|
2592
|
+
/**
|
|
2593
|
+
* Configuration for signal forms validator functions that can be serialized from API.
|
|
2594
|
+
* Discriminated union type for type-safe validator configuration.
|
|
2595
|
+
*
|
|
2596
|
+
* Note: `FunctionHttpValidatorConfig` and `DeclarativeHttpValidatorConfig` both use `type: 'http'`.
|
|
2597
|
+
* They are discriminated by property presence: `functionName` → function-based, `http` → declarative.
|
|
2598
|
+
* Use `isFunctionHttpValidator()` for type-safe narrowing when `type` alone is insufficient.
|
|
2599
|
+
*/
|
|
2600
|
+
type ValidatorConfig = BuiltInValidatorConfig | CustomValidatorConfig | AsyncValidatorConfig | FunctionHttpValidatorConfig | DeclarativeHttpValidatorConfig;
|
|
1481
2601
|
|
|
1482
2602
|
/**
|
|
1483
2603
|
* Configuration for applying predefined schemas
|
|
@@ -1774,31 +2894,74 @@ interface HttpCustomValidator<TValue = unknown, TResult = unknown> {
|
|
|
1774
2894
|
}
|
|
1775
2895
|
|
|
1776
2896
|
/**
|
|
1777
|
-
*
|
|
1778
|
-
*
|
|
2897
|
+
* Tracks deprecated API usages that have already been warned about to prevent log spam.
|
|
2898
|
+
* Scoped to a single form instance via DI.
|
|
2899
|
+
*
|
|
2900
|
+
* This follows the same pattern as DerivationWarningTracker — DI-scoped rather than
|
|
2901
|
+
* module-scoped to avoid SSR issues where state would be shared across requests.
|
|
2902
|
+
*
|
|
2903
|
+
* @public
|
|
2904
|
+
*/
|
|
2905
|
+
interface DeprecationWarningTracker {
|
|
2906
|
+
/** Set of deprecation keys we've already warned about */
|
|
2907
|
+
warnedKeys: Set<string>;
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2910
|
+
/**
|
|
2911
|
+
* Represents the state information of a single form field.
|
|
2912
|
+
*
|
|
2913
|
+
* Used in evaluation contexts to allow expressions and conditions
|
|
2914
|
+
* to reference field state (e.g., `fieldState.touched`, `formFieldState.email.dirty`).
|
|
2915
|
+
*
|
|
2916
|
+
* @public
|
|
1779
2917
|
*/
|
|
1780
|
-
interface
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
2918
|
+
interface FieldStateInfo {
|
|
2919
|
+
readonly touched: boolean;
|
|
2920
|
+
readonly dirty: boolean;
|
|
2921
|
+
/** Convenience property: equivalent to `!dirty` */
|
|
2922
|
+
readonly pristine: boolean;
|
|
2923
|
+
readonly valid: boolean;
|
|
2924
|
+
readonly invalid: boolean;
|
|
2925
|
+
readonly pending: boolean;
|
|
1786
2926
|
/**
|
|
1787
|
-
*
|
|
1788
|
-
*
|
|
2927
|
+
* Whether the field is currently hidden.
|
|
2928
|
+
*
|
|
2929
|
+
* Note: This is a library-managed property, not a native Angular Signal Forms
|
|
2930
|
+
* property. Returns `false` if the field instance does not expose a `hidden` signal.
|
|
1789
2931
|
*/
|
|
1790
|
-
|
|
2932
|
+
readonly hidden: boolean;
|
|
1791
2933
|
/**
|
|
1792
|
-
*
|
|
1793
|
-
*
|
|
2934
|
+
* Whether the field is currently readonly.
|
|
2935
|
+
*
|
|
2936
|
+
* Note: This is a library-managed property, not a native Angular Signal Forms
|
|
2937
|
+
* property. Returns `false` if the field instance does not expose a `readonly` signal.
|
|
1794
2938
|
*/
|
|
1795
|
-
|
|
2939
|
+
readonly readonly: boolean;
|
|
1796
2940
|
/**
|
|
1797
|
-
*
|
|
1798
|
-
*
|
|
2941
|
+
* Whether the field is currently disabled.
|
|
2942
|
+
*
|
|
2943
|
+
* Note: This is a library-managed property, not a native Angular Signal Forms
|
|
2944
|
+
* property. Returns `false` if the field instance does not expose a `disabled` signal.
|
|
1799
2945
|
*/
|
|
1800
|
-
|
|
2946
|
+
readonly disabled: boolean;
|
|
1801
2947
|
}
|
|
2948
|
+
/**
|
|
2949
|
+
* Map of field keys to their state information.
|
|
2950
|
+
*
|
|
2951
|
+
* Used as `formFieldState` in evaluation contexts to access
|
|
2952
|
+
* state of any field in the form by key.
|
|
2953
|
+
*
|
|
2954
|
+
* @public
|
|
2955
|
+
*/
|
|
2956
|
+
type FormFieldStateMap = Record<string, FieldStateInfo | undefined>;
|
|
2957
|
+
/**
|
|
2958
|
+
* Field state context for the current field being evaluated.
|
|
2959
|
+
*
|
|
2960
|
+
* Used as `fieldState` in evaluation contexts.
|
|
2961
|
+
*
|
|
2962
|
+
* @public
|
|
2963
|
+
*/
|
|
2964
|
+
type FieldStateContext = FieldStateInfo;
|
|
1802
2965
|
|
|
1803
2966
|
interface EvaluationContext<TValue = unknown> {
|
|
1804
2967
|
/** Current field value */
|
|
@@ -1871,6 +3034,43 @@ interface EvaluationContext<TValue = unknown> {
|
|
|
1871
3034
|
* ```
|
|
1872
3035
|
*/
|
|
1873
3036
|
externalData?: Record<string, unknown>;
|
|
3037
|
+
/**
|
|
3038
|
+
* DI-scoped tracker for deprecation warnings.
|
|
3039
|
+
* Used by the condition evaluator to deduplicate deprecation warnings.
|
|
3040
|
+
*/
|
|
3041
|
+
deprecationTracker?: DeprecationWarningTracker;
|
|
3042
|
+
/**
|
|
3043
|
+
* State of the current field being evaluated.
|
|
3044
|
+
*
|
|
3045
|
+
* Provides access to field state properties like `touched`, `dirty`, `valid`, etc.
|
|
3046
|
+
* Uses a Proxy for lazy evaluation — properties are only read when accessed.
|
|
3047
|
+
*
|
|
3048
|
+
* @example
|
|
3049
|
+
* ```typescript
|
|
3050
|
+
* // In a JavaScript expression:
|
|
3051
|
+
* condition: {
|
|
3052
|
+
* type: 'javascript',
|
|
3053
|
+
* expression: "fieldState.touched && fieldState.dirty"
|
|
3054
|
+
* }
|
|
3055
|
+
* ```
|
|
3056
|
+
*/
|
|
3057
|
+
fieldState?: FieldStateContext;
|
|
3058
|
+
/**
|
|
3059
|
+
* State of all fields in the form, keyed by field key.
|
|
3060
|
+
*
|
|
3061
|
+
* Provides access to any field's state properties by key.
|
|
3062
|
+
* Uses a Proxy for lazy evaluation — field state is only read when accessed.
|
|
3063
|
+
*
|
|
3064
|
+
* @example
|
|
3065
|
+
* ```typescript
|
|
3066
|
+
* // In a JavaScript expression:
|
|
3067
|
+
* condition: {
|
|
3068
|
+
* type: 'javascript',
|
|
3069
|
+
* expression: "formFieldState.email.dirty && formFieldState.email.valid"
|
|
3070
|
+
* }
|
|
3071
|
+
* ```
|
|
3072
|
+
*/
|
|
3073
|
+
formFieldState?: FormFieldStateMap;
|
|
1874
3074
|
/** Allow additional properties for flexible expression evaluation */
|
|
1875
3075
|
[key: string]: unknown;
|
|
1876
3076
|
}
|
|
@@ -1916,6 +3116,43 @@ interface EvaluationContext<TValue = unknown> {
|
|
|
1916
3116
|
*/
|
|
1917
3117
|
type CustomFunction = (context: EvaluationContext) => unknown;
|
|
1918
3118
|
|
|
3119
|
+
/**
|
|
3120
|
+
* Async function for derivations — returns any value.
|
|
3121
|
+
*
|
|
3122
|
+
* The function receives the evaluation context and may return a Promise or Observable.
|
|
3123
|
+
* Register functions in `customFnConfig.asyncDerivations`.
|
|
3124
|
+
*
|
|
3125
|
+
* @example
|
|
3126
|
+
* ```typescript
|
|
3127
|
+
* const fetchPrice: AsyncDerivationFunction = async (context) => {
|
|
3128
|
+
* const response = await fetch(`/api/price?product=${context.formValue.productId}`);
|
|
3129
|
+
* const data = await response.json();
|
|
3130
|
+
* return data.price;
|
|
3131
|
+
* };
|
|
3132
|
+
* ```
|
|
3133
|
+
*
|
|
3134
|
+
* @public
|
|
3135
|
+
*/
|
|
3136
|
+
type AsyncDerivationFunction = (context: EvaluationContext) => Promise<unknown> | Observable<unknown>;
|
|
3137
|
+
/**
|
|
3138
|
+
* Async function for conditions — returns boolean.
|
|
3139
|
+
*
|
|
3140
|
+
* The function receives the evaluation context and may return a Promise or Observable.
|
|
3141
|
+
* Register functions in `customFnConfig.asyncConditions`.
|
|
3142
|
+
*
|
|
3143
|
+
* @example
|
|
3144
|
+
* ```typescript
|
|
3145
|
+
* const checkPermission: AsyncConditionFunction = async (context) => {
|
|
3146
|
+
* const response = await fetch(`/api/permissions?user=${context.formValue.userId}`);
|
|
3147
|
+
* const data = await response.json();
|
|
3148
|
+
* return data.canEdit;
|
|
3149
|
+
* };
|
|
3150
|
+
* ```
|
|
3151
|
+
*
|
|
3152
|
+
* @public
|
|
3153
|
+
*/
|
|
3154
|
+
type AsyncConditionFunction = (context: EvaluationContext) => Promise<boolean> | Observable<boolean>;
|
|
3155
|
+
|
|
1919
3156
|
/**
|
|
1920
3157
|
* Re-export Angular's ValidationError for consistency
|
|
1921
3158
|
* This replaces our custom ValidationError interface
|
|
@@ -2421,6 +3658,103 @@ interface CustomFnConfig {
|
|
|
2421
3658
|
* ```
|
|
2422
3659
|
*/
|
|
2423
3660
|
derivations?: Record<string, CustomFunction>;
|
|
3661
|
+
/**
|
|
3662
|
+
* @deprecated Register functions under `derivations` instead. Use `type: 'derivation'`
|
|
3663
|
+
* with `targetProperty` in field logic configs. Will be removed in a future minor version.
|
|
3664
|
+
*
|
|
3665
|
+
* Custom property derivation functions for reactive property updates.
|
|
3666
|
+
*
|
|
3667
|
+
* These functions compute derived values for field properties (like `minDate`,
|
|
3668
|
+
* `options`, `label`, `placeholder`) and are called when a
|
|
3669
|
+
* `PropertyDerivationLogicConfig` references them by `functionName`.
|
|
3670
|
+
*
|
|
3671
|
+
* Property derivation functions:
|
|
3672
|
+
* - Receive an `EvaluationContext` with access to `formValue`
|
|
3673
|
+
* - Return the value to set on the target property
|
|
3674
|
+
* - Are called reactively when dependencies change
|
|
3675
|
+
*
|
|
3676
|
+
* @example
|
|
3677
|
+
* ```typescript
|
|
3678
|
+
* propertyDerivations: {
|
|
3679
|
+
* getCitiesForCountry: (context) => {
|
|
3680
|
+
* const countryCities: Record<string, Array<{ label: string; value: string }>> = {
|
|
3681
|
+
* 'USA': [{ label: 'New York', value: 'ny' }, { label: 'LA', value: 'la' }],
|
|
3682
|
+
* 'Germany': [{ label: 'Berlin', value: 'berlin' }, { label: 'Munich', value: 'munich' }],
|
|
3683
|
+
* };
|
|
3684
|
+
* return countryCities[context.formValue.country as string] ?? [];
|
|
3685
|
+
* },
|
|
3686
|
+
* }
|
|
3687
|
+
* ```
|
|
3688
|
+
*
|
|
3689
|
+
* Field configuration using a property derivation function:
|
|
3690
|
+
* ```typescript
|
|
3691
|
+
* {
|
|
3692
|
+
* key: 'city',
|
|
3693
|
+
* type: 'select',
|
|
3694
|
+
* logic: [{
|
|
3695
|
+
* type: 'propertyDerivation',
|
|
3696
|
+
* targetProperty: 'options',
|
|
3697
|
+
* functionName: 'getCitiesForCountry',
|
|
3698
|
+
* dependsOn: ['country']
|
|
3699
|
+
* }]
|
|
3700
|
+
* }
|
|
3701
|
+
* ```
|
|
3702
|
+
*/
|
|
3703
|
+
propertyDerivations?: Record<string, CustomFunction>;
|
|
3704
|
+
/**
|
|
3705
|
+
* Async derivation functions for asynchronous value derivation logic.
|
|
3706
|
+
*
|
|
3707
|
+
* These functions perform asynchronous operations (service calls, complex pipelines)
|
|
3708
|
+
* and return the derived value via a Promise or Observable. They are called when a
|
|
3709
|
+
* `DerivationLogicConfig` references them by `asyncFunctionName`.
|
|
3710
|
+
*
|
|
3711
|
+
* Async derivation functions:
|
|
3712
|
+
* - Receive an `EvaluationContext` with access to `formValue`
|
|
3713
|
+
* - Return a Promise or Observable of the value to set on the target field
|
|
3714
|
+
* - Handle their own I/O (no HttpClient provided — use injected services)
|
|
3715
|
+
* - Require explicit `dependsOn` to avoid triggering on every form change
|
|
3716
|
+
*
|
|
3717
|
+
* @example
|
|
3718
|
+
* ```typescript
|
|
3719
|
+
* asyncDerivations: {
|
|
3720
|
+
* fetchSuggestedPrice: async (context) => {
|
|
3721
|
+
* const response = await fetch(`/api/price?product=${context.formValue.productId}`);
|
|
3722
|
+
* const data = await response.json();
|
|
3723
|
+
* return data.suggestedPrice;
|
|
3724
|
+
* },
|
|
3725
|
+
* lookupAddress: (context) => {
|
|
3726
|
+
* return addressService.lookup(context.formValue.zipCode).pipe(
|
|
3727
|
+
* map(result => result.formattedAddress)
|
|
3728
|
+
* );
|
|
3729
|
+
* },
|
|
3730
|
+
* }
|
|
3731
|
+
* ```
|
|
3732
|
+
*/
|
|
3733
|
+
asyncDerivations?: Record<string, AsyncDerivationFunction>;
|
|
3734
|
+
/**
|
|
3735
|
+
* Async condition functions for asynchronous field state logic.
|
|
3736
|
+
*
|
|
3737
|
+
* These functions perform asynchronous operations and return a boolean
|
|
3738
|
+
* indicating whether the condition is met. They are referenced by
|
|
3739
|
+
* `asyncFunctionName` in `AsyncCondition` expressions.
|
|
3740
|
+
*
|
|
3741
|
+
* Async condition functions:
|
|
3742
|
+
* - Receive an `EvaluationContext` with access to `formValue`
|
|
3743
|
+
* - Return a Promise or Observable of boolean
|
|
3744
|
+
* - Handle their own I/O (use injected services)
|
|
3745
|
+
*
|
|
3746
|
+
* @example
|
|
3747
|
+
* ```typescript
|
|
3748
|
+
* asyncConditions: {
|
|
3749
|
+
* checkPermission: async (context) => {
|
|
3750
|
+
* const response = await fetch(`/api/permissions?role=${context.formValue.role}`);
|
|
3751
|
+
* const data = await response.json();
|
|
3752
|
+
* return data.canEdit;
|
|
3753
|
+
* },
|
|
3754
|
+
* }
|
|
3755
|
+
* ```
|
|
3756
|
+
*/
|
|
3757
|
+
asyncConditions?: Record<string, AsyncConditionFunction>;
|
|
2424
3758
|
/**
|
|
2425
3759
|
* Custom validators using Angular's public FieldContext API
|
|
2426
3760
|
*
|
|
@@ -2538,6 +3872,10 @@ interface CustomFnConfig {
|
|
|
2538
3872
|
*/
|
|
2539
3873
|
asyncValidators?: Record<string, AsyncCustomValidator>;
|
|
2540
3874
|
/**
|
|
3875
|
+
* @deprecated Prefer declarative HTTP validators using `type: 'http'` with `http` + `responseMapping`
|
|
3876
|
+
* in the validators array. Function-based HTTP validators now also use `type: 'http'` with `functionName`.
|
|
3877
|
+
* Will be removed in a future minor version.
|
|
3878
|
+
*
|
|
2541
3879
|
* HTTP validators using Angular's validateHttp() API
|
|
2542
3880
|
*
|
|
2543
3881
|
* Angular's validateHttp provides HTTP validation with automatic request
|
|
@@ -2680,233 +4018,128 @@ interface FormOptions {
|
|
|
2680
4018
|
*/
|
|
2681
4019
|
nextButton?: NextButtonOptions;
|
|
2682
4020
|
/**
|
|
2683
|
-
* Whether to
|
|
4021
|
+
* Whether to exclude values of hidden fields from submission output.
|
|
2684
4022
|
*
|
|
2685
|
-
*
|
|
2686
|
-
*
|
|
2687
|
-
* - `false` - Disable form value emission for this form (even if globally enabled)
|
|
2688
|
-
* - `undefined` - Use global setting (default)
|
|
2689
|
-
*
|
|
2690
|
-
* When enabled, events will include a `formValue` property with the current form state.
|
|
2691
|
-
* Use the `hasFormValue()` type guard to safely access this property.
|
|
2692
|
-
*
|
|
2693
|
-
* @example
|
|
2694
|
-
* ```typescript
|
|
2695
|
-
* // Enable for this form only (no global withEventFormValue() needed)
|
|
2696
|
-
* const config: FormConfig = {
|
|
2697
|
-
* fields: [...],
|
|
2698
|
-
* options: { emitFormValueOnEvents: true }
|
|
2699
|
-
* };
|
|
2700
|
-
*
|
|
2701
|
-
* // Disable for this form (when globally enabled)
|
|
2702
|
-
* const config: FormConfig = {
|
|
2703
|
-
* fields: [...],
|
|
2704
|
-
* options: { emitFormValueOnEvents: false }
|
|
2705
|
-
* };
|
|
2706
|
-
* ```
|
|
4023
|
+
* Overrides the global `withValueExclusionDefaults()` setting for this form.
|
|
4024
|
+
* Can be further overridden per-field on individual `FieldDef` entries.
|
|
2707
4025
|
*
|
|
2708
4026
|
* @default undefined (uses global setting)
|
|
2709
4027
|
*/
|
|
2710
|
-
|
|
2711
|
-
}
|
|
2712
|
-
/**
|
|
2713
|
-
* Options for controlling submit button disabled behavior.
|
|
2714
|
-
*
|
|
2715
|
-
* @public
|
|
2716
|
-
*/
|
|
2717
|
-
interface SubmitButtonOptions {
|
|
4028
|
+
excludeValueIfHidden?: boolean;
|
|
2718
4029
|
/**
|
|
2719
|
-
*
|
|
4030
|
+
* Whether to exclude values of disabled fields from submission output.
|
|
2720
4031
|
*
|
|
2721
|
-
*
|
|
4032
|
+
* Overrides the global `withValueExclusionDefaults()` setting for this form.
|
|
4033
|
+
* Can be further overridden per-field on individual `FieldDef` entries.
|
|
4034
|
+
*
|
|
4035
|
+
* @default undefined (uses global setting)
|
|
2722
4036
|
*/
|
|
2723
|
-
|
|
4037
|
+
excludeValueIfDisabled?: boolean;
|
|
2724
4038
|
/**
|
|
2725
|
-
*
|
|
4039
|
+
* Whether to exclude values of readonly fields from submission output.
|
|
2726
4040
|
*
|
|
2727
|
-
*
|
|
4041
|
+
* Overrides the global `withValueExclusionDefaults()` setting for this form.
|
|
4042
|
+
* Can be further overridden per-field on individual `FieldDef` entries.
|
|
2728
4043
|
*
|
|
2729
|
-
* @default
|
|
4044
|
+
* @default undefined (uses global setting)
|
|
2730
4045
|
*/
|
|
2731
|
-
|
|
2732
|
-
}
|
|
2733
|
-
/**
|
|
2734
|
-
* Options for controlling next page button disabled behavior.
|
|
2735
|
-
*
|
|
2736
|
-
* @public
|
|
2737
|
-
*/
|
|
2738
|
-
interface NextButtonOptions {
|
|
4046
|
+
excludeValueIfReadonly?: boolean;
|
|
2739
4047
|
/**
|
|
2740
|
-
*
|
|
4048
|
+
* Whether to attach the current form value to all events dispatched through the EventBus.
|
|
2741
4049
|
*
|
|
2742
|
-
*
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
* Disable next button while the form is submitting.
|
|
4050
|
+
* This per-form setting overrides the global `withEventFormValue()` feature:
|
|
4051
|
+
* - `true` - Enable form value emission for this form (even if globally disabled)
|
|
4052
|
+
* - `false` - Disable form value emission for this form (even if globally enabled)
|
|
4053
|
+
* - `undefined` - Use global setting (default)
|
|
2747
4054
|
*
|
|
2748
|
-
*
|
|
2749
|
-
|
|
2750
|
-
disableWhileSubmitting?: boolean;
|
|
2751
|
-
}
|
|
2752
|
-
|
|
2753
|
-
/**
|
|
2754
|
-
* Form mode enumeration distinguishing between paged and non-paged forms
|
|
2755
|
-
*/
|
|
2756
|
-
type FormMode = 'paged' | 'non-paged';
|
|
2757
|
-
/**
|
|
2758
|
-
* Result of form mode detection with validation details
|
|
2759
|
-
*/
|
|
2760
|
-
interface FormModeDetectionResult {
|
|
2761
|
-
/** The detected form mode */
|
|
2762
|
-
mode: FormMode;
|
|
2763
|
-
/** Whether the form configuration is valid for the detected mode */
|
|
2764
|
-
isValid: boolean;
|
|
2765
|
-
/** Array of validation errors if form is invalid */
|
|
2766
|
-
errors: string[];
|
|
2767
|
-
}
|
|
2768
|
-
|
|
2769
|
-
/**
|
|
2770
|
-
* Type guard to check if a field is a container field (page, row, group, or array)
|
|
2771
|
-
* Container fields have a 'fields' property and don't contribute values directly.
|
|
2772
|
-
* This overload works with RegisteredFieldTypes for full type narrowing.
|
|
2773
|
-
*/
|
|
2774
|
-
declare function isContainerField(field: RegisteredFieldTypes): field is ContainerFieldTypes;
|
|
2775
|
-
/**
|
|
2776
|
-
* Type guard to check if a field is a container field (page, row, group, or array).
|
|
2777
|
-
* This overload works with any FieldDef for looser type checking.
|
|
2778
|
-
*/
|
|
2779
|
-
declare function isContainerField(field: FieldDef<unknown>): boolean;
|
|
2780
|
-
/**
|
|
2781
|
-
* Type guard to check if a field is a leaf field (value or display field)
|
|
2782
|
-
* Leaf fields don't have children and either contribute values or display content
|
|
2783
|
-
*/
|
|
2784
|
-
declare function isLeafField(field: RegisteredFieldTypes): field is LeafFieldTypes;
|
|
2785
|
-
/**
|
|
2786
|
-
* Type guard to check if a field has a value property (value-bearing field)
|
|
2787
|
-
* These fields contribute to the form value output
|
|
2788
|
-
* Note: Using `unknown` in the Extract condition to match any value type
|
|
2789
|
-
*/
|
|
2790
|
-
declare function isValueBearingField(field: RegisteredFieldTypes): field is Extract<RegisteredFieldTypes, {
|
|
2791
|
-
value: unknown;
|
|
2792
|
-
}>;
|
|
2793
|
-
/**
|
|
2794
|
-
* Type guard to check if a field is excluded from form values (display-only field)
|
|
2795
|
-
* Currently this includes text fields and any other fields without a value property
|
|
2796
|
-
*/
|
|
2797
|
-
declare function isDisplayOnlyField(field: RegisteredFieldTypes): boolean;
|
|
2798
|
-
|
|
2799
|
-
/**
|
|
2800
|
-
* Helper types for creating type-safe field configurations with proper nesting constraints
|
|
2801
|
-
*/
|
|
2802
|
-
|
|
2803
|
-
/**
|
|
2804
|
-
* Type helper for accessing nested field paths safely
|
|
2805
|
-
* This allows accessing child paths while maintaining some type safety
|
|
2806
|
-
*/
|
|
2807
|
-
type FieldPathAccess<TValue> = {
|
|
2808
|
-
[K in keyof TValue]: SchemaPath<TValue[K]> | SchemaPathTree<TValue[K]>;
|
|
2809
|
-
};
|
|
2810
|
-
|
|
2811
|
-
/**
|
|
2812
|
-
* Result of processing all pending derivations.
|
|
2813
|
-
*
|
|
2814
|
-
* @public
|
|
2815
|
-
*/
|
|
2816
|
-
interface DerivationProcessingResult {
|
|
2817
|
-
/** Number of derivations successfully applied */
|
|
2818
|
-
appliedCount: number;
|
|
2819
|
-
/** Number of derivations skipped (condition not met or value unchanged) */
|
|
2820
|
-
skippedCount: number;
|
|
2821
|
-
/** Number of derivations that failed with errors */
|
|
2822
|
-
errorCount: number;
|
|
2823
|
-
/**
|
|
2824
|
-
* Number of derivations that encountered warnings (e.g., missing target field).
|
|
4055
|
+
* When enabled, events will include a `formValue` property with the current form state.
|
|
4056
|
+
* Use the `hasFormValue()` type guard to safely access this property.
|
|
2825
4057
|
*
|
|
2826
|
-
*
|
|
2827
|
-
*
|
|
4058
|
+
* @example
|
|
4059
|
+
* ```typescript
|
|
4060
|
+
* // Enable for this form only (no global withEventFormValue() needed)
|
|
4061
|
+
* const config: FormConfig = {
|
|
4062
|
+
* fields: [...],
|
|
4063
|
+
* options: { emitFormValueOnEvents: true }
|
|
4064
|
+
* };
|
|
4065
|
+
*
|
|
4066
|
+
* // Disable for this form (when globally enabled)
|
|
4067
|
+
* const config: FormConfig = {
|
|
4068
|
+
* fields: [...],
|
|
4069
|
+
* options: { emitFormValueOnEvents: false }
|
|
4070
|
+
* };
|
|
4071
|
+
* ```
|
|
4072
|
+
*
|
|
4073
|
+
* @default undefined (uses global setting)
|
|
2828
4074
|
*/
|
|
2829
|
-
|
|
2830
|
-
/** Total iterations performed */
|
|
2831
|
-
iterations: number;
|
|
2832
|
-
/** Whether max iterations was reached (potential loop) */
|
|
2833
|
-
maxIterationsReached: boolean;
|
|
4075
|
+
emitFormValueOnEvents?: boolean;
|
|
2834
4076
|
}
|
|
2835
|
-
|
|
2836
4077
|
/**
|
|
2837
|
-
*
|
|
4078
|
+
* Options for controlling submit button disabled behavior.
|
|
2838
4079
|
*
|
|
2839
4080
|
* @public
|
|
2840
4081
|
*/
|
|
2841
|
-
interface
|
|
2842
|
-
/**
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
/**
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
/** Duration in milliseconds (optional, for performance tracking) */
|
|
2857
|
-
durationMs?: number;
|
|
4082
|
+
interface SubmitButtonOptions {
|
|
4083
|
+
/**
|
|
4084
|
+
* Disable submit button when the form is invalid.
|
|
4085
|
+
*
|
|
4086
|
+
* @default true
|
|
4087
|
+
*/
|
|
4088
|
+
disableWhenInvalid?: boolean;
|
|
4089
|
+
/**
|
|
4090
|
+
* Disable submit button while the form is submitting.
|
|
4091
|
+
*
|
|
4092
|
+
* Requires `submission.action` to be configured for automatic detection.
|
|
4093
|
+
*
|
|
4094
|
+
* @default true
|
|
4095
|
+
*/
|
|
4096
|
+
disableWhileSubmitting?: boolean;
|
|
2858
4097
|
}
|
|
2859
|
-
|
|
2860
4098
|
/**
|
|
2861
|
-
*
|
|
2862
|
-
*
|
|
2863
|
-
* Provides methods to log derivation processing events at different verbosity levels.
|
|
2864
|
-
* Use `DERIVATION_LOGGER` injection token to get an instance.
|
|
4099
|
+
* Options for controlling next page button disabled behavior.
|
|
2865
4100
|
*
|
|
2866
4101
|
* @public
|
|
2867
4102
|
*/
|
|
2868
|
-
interface
|
|
2869
|
-
/**
|
|
2870
|
-
* Logs the start of a derivation processing cycle.
|
|
2871
|
-
* Only logs when config level is 'verbose'.
|
|
2872
|
-
*/
|
|
2873
|
-
cycleStart(trigger: 'onChange' | 'debounced', entryCount: number): void;
|
|
2874
|
-
/**
|
|
2875
|
-
* Logs the start of a derivation iteration within a cycle.
|
|
2876
|
-
* Only logs when config level is 'verbose'.
|
|
2877
|
-
*/
|
|
2878
|
-
iteration(iterationNumber: number): void;
|
|
2879
|
-
/**
|
|
2880
|
-
* Logs a single derivation evaluation result.
|
|
2881
|
-
* Only logs when config level is 'verbose'.
|
|
2882
|
-
*/
|
|
2883
|
-
evaluation(entry: DerivationLogEntry): void;
|
|
4103
|
+
interface NextButtonOptions {
|
|
2884
4104
|
/**
|
|
2885
|
-
*
|
|
2886
|
-
*
|
|
4105
|
+
* Disable next button when the current page has invalid fields.
|
|
4106
|
+
*
|
|
4107
|
+
* @default true
|
|
2887
4108
|
*/
|
|
2888
|
-
|
|
4109
|
+
disableWhenPageInvalid?: boolean;
|
|
2889
4110
|
/**
|
|
2890
|
-
*
|
|
4111
|
+
* Disable next button while the form is submitting.
|
|
4112
|
+
*
|
|
4113
|
+
* @default true
|
|
2891
4114
|
*/
|
|
2892
|
-
|
|
4115
|
+
disableWhileSubmitting?: boolean;
|
|
4116
|
+
}
|
|
4117
|
+
|
|
4118
|
+
/**
|
|
4119
|
+
* Form mode enumeration distinguishing between paged and non-paged forms
|
|
4120
|
+
*/
|
|
4121
|
+
type FormMode = 'paged' | 'non-paged';
|
|
4122
|
+
/**
|
|
4123
|
+
* Result of form mode detection with validation details
|
|
4124
|
+
*/
|
|
4125
|
+
interface FormModeDetectionResult {
|
|
4126
|
+
/** The detected form mode */
|
|
4127
|
+
mode: FormMode;
|
|
4128
|
+
/** Whether the form configuration is valid for the detected mode */
|
|
4129
|
+
isValid: boolean;
|
|
4130
|
+
/** Array of validation errors if form is invalid */
|
|
4131
|
+
errors: string[];
|
|
2893
4132
|
}
|
|
2894
4133
|
|
|
2895
4134
|
/**
|
|
2896
|
-
*
|
|
2897
|
-
* Only includes form-specific signals that cannot be injected.
|
|
4135
|
+
* Error from async field component loading.
|
|
2898
4136
|
*
|
|
2899
|
-
* @
|
|
4137
|
+
* @public
|
|
2900
4138
|
*/
|
|
2901
|
-
interface
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
formValue: Signal<Record<string, unknown>>;
|
|
2906
|
-
/** Signal containing the form accessor */
|
|
2907
|
-
form: Signal<FieldTree<unknown>>;
|
|
2908
|
-
/** Signal containing the derivation logger */
|
|
2909
|
-
derivationLogger: Signal<DerivationLogger>;
|
|
4139
|
+
interface FieldLoadingError {
|
|
4140
|
+
readonly fieldType: string;
|
|
4141
|
+
readonly fieldKey: string;
|
|
4142
|
+
readonly error: Error;
|
|
2910
4143
|
}
|
|
2911
4144
|
|
|
2912
4145
|
/**
|
|
@@ -3072,108 +4305,88 @@ declare class PageNavigationStateChangeEvent implements FormEvent {
|
|
|
3072
4305
|
}
|
|
3073
4306
|
|
|
3074
4307
|
/**
|
|
3075
|
-
* Dynamic form component
|
|
3076
|
-
*
|
|
3077
|
-
* This is the main entry point for the dynamic form system. It handles form state management,
|
|
3078
|
-
* validation, field rendering, and event coordination using Angular's signal-based reactive forms.
|
|
3079
|
-
*
|
|
3080
|
-
* @typeParam TFields - Array of registered field types available for this form
|
|
3081
|
-
* @typeParam TModel - The strongly-typed interface for form values
|
|
4308
|
+
* Dynamic form component — renders a form based on configuration.
|
|
4309
|
+
* Delegates state management to `FormStateManager`.
|
|
3082
4310
|
*
|
|
3083
4311
|
* @example
|
|
3084
4312
|
*```html
|
|
3085
|
-
* <form
|
|
3086
|
-
* [dynamic-form]="formConfig"
|
|
3087
|
-
* [(value)]="formData"
|
|
3088
|
-
* (submitted)="handleSubmit($event)"
|
|
3089
|
-
* (validityChange)="handleValidityChange($event)">
|
|
3090
|
-
* </form>
|
|
4313
|
+
* <form [dynamic-form]="formConfig" [(value)]="formData" (submitted)="handleSubmit($event)"></form>
|
|
3091
4314
|
* ```
|
|
3092
4315
|
*/
|
|
3093
|
-
declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFieldTypes[], TModel extends Record<string, unknown> = InferFormValue$1<TFields> & Record<string, unknown>>
|
|
3094
|
-
private readonly destroyRef;
|
|
3095
|
-
private readonly fieldRegistry;
|
|
3096
|
-
private readonly injector;
|
|
3097
|
-
private readonly eventBus;
|
|
3098
|
-
private readonly rootFormRegistry;
|
|
3099
|
-
private readonly functionRegistry;
|
|
3100
|
-
private readonly schemaRegistry;
|
|
3101
|
-
private readonly logger;
|
|
3102
|
-
private readonly derivationLogConfig;
|
|
4316
|
+
declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFieldTypes[], TModel extends Record<string, unknown> = InferFormValue$1<TFields> & Record<string, unknown>> {
|
|
3103
4317
|
/** Form configuration defining the structure, validation, and behavior. */
|
|
3104
|
-
config: InputSignal<FormConfig<TFields>>;
|
|
4318
|
+
config: _angular_core.InputSignal<FormConfig<TFields, InferFormValue$1<TFields extends readonly RegisteredFieldTypes[] ? TFields : RegisteredFieldTypes[]>, Record<string, unknown>, unknown>>;
|
|
3105
4319
|
/** Runtime form options that override config options when provided. */
|
|
3106
|
-
formOptions: InputSignal<FormOptions | undefined>;
|
|
4320
|
+
formOptions: _angular_core.InputSignal<FormOptions | undefined>;
|
|
3107
4321
|
/** Form values for two-way data binding. */
|
|
3108
4322
|
value: _angular_core.ModelSignal<Partial<TModel> | undefined>;
|
|
3109
|
-
private
|
|
3110
|
-
private
|
|
3111
|
-
private
|
|
3112
|
-
private
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
4323
|
+
private destroyRef;
|
|
4324
|
+
private injector;
|
|
4325
|
+
private eventBus;
|
|
4326
|
+
private logger;
|
|
4327
|
+
/**
|
|
4328
|
+
* Connect input signals to the shared deps holder BEFORE injecting FormStateManager.
|
|
4329
|
+
* Field initializers run in declaration order, so this runs after inputs are created
|
|
4330
|
+
* but before stateManager is injected. FormStateManager reads from these signals
|
|
4331
|
+
* in computeds/effects, which are all lazy and evaluated after construction.
|
|
4332
|
+
*/
|
|
4333
|
+
private readonly _connectStateDeps;
|
|
4334
|
+
/** State manager that owns all form state. */
|
|
4335
|
+
private stateManager;
|
|
4336
|
+
private componentId;
|
|
4337
|
+
/** The currently active config used for form rendering */
|
|
4338
|
+
activeConfig: Signal<FormConfig<TFields, InferFormValue$1<TFields extends readonly RegisteredFieldTypes[] ? TFields : RegisteredFieldTypes[]>, Record<string, unknown>, unknown> | undefined>;
|
|
4339
|
+
/** Current render phase: 'render' = showing form, 'teardown' = hiding old components */
|
|
4340
|
+
renderPhase: Signal<"teardown" | "render">;
|
|
4341
|
+
/** Computed form mode detection with validation */
|
|
4342
|
+
formModeDetection: Signal<_ng_forge_dynamic_forms.FormModeDetectionResult>;
|
|
4343
|
+
/** Page field definitions for paged forms */
|
|
4344
|
+
pageFieldDefinitions: Signal<_ng_forge_dynamic_forms.PageField<_ng_forge_dynamic_forms.PageAllowedChildren[]>[]>;
|
|
4345
|
+
/** Effective form options (merged from config and input) */
|
|
4346
|
+
effectiveFormOptions: Signal<{
|
|
3118
4347
|
disabled?: boolean;
|
|
3119
4348
|
maxDerivationIterations?: number;
|
|
3120
4349
|
submitButton?: _ng_forge_dynamic_forms.SubmitButtonOptions;
|
|
3121
4350
|
nextButton?: _ng_forge_dynamic_forms.NextButtonOptions;
|
|
4351
|
+
excludeValueIfHidden?: boolean;
|
|
4352
|
+
excludeValueIfDisabled?: boolean;
|
|
4353
|
+
excludeValueIfReadonly?: boolean;
|
|
3122
4354
|
emitFormValueOnEvents?: boolean;
|
|
3123
4355
|
}>;
|
|
3124
|
-
/**
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
/**
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
readonly invalid: Signal<boolean>;
|
|
3155
|
-
/** Whether any form field has been modified. */
|
|
3156
|
-
readonly dirty: Signal<boolean>;
|
|
3157
|
-
/** Whether any form field has been touched (blurred). */
|
|
3158
|
-
readonly touched: Signal<boolean>;
|
|
3159
|
-
/** Current validation errors from all fields. */
|
|
3160
|
-
readonly errors: Signal<_angular_forms_signals.ValidationError.WithField[]>;
|
|
3161
|
-
/** Whether the form is disabled (from options or form state). */
|
|
3162
|
-
readonly disabled: Signal<boolean>;
|
|
3163
|
-
/** Whether the form is currently submitting. */
|
|
3164
|
-
readonly submitting: Signal<boolean>;
|
|
3165
|
-
/**
|
|
3166
|
-
* Configuration for the derivation orchestrator.
|
|
3167
|
-
* Bundles the form-specific signals needed by the orchestrator.
|
|
3168
|
-
*
|
|
3169
|
-
* @internal Used by DERIVATION_ORCHESTRATOR provider.
|
|
3170
|
-
*/
|
|
3171
|
-
readonly derivationOrchestratorConfig: DerivationOrchestratorConfig;
|
|
3172
|
-
readonly initialized$: rxjs.Observable<boolean>;
|
|
4356
|
+
/** Field signal context for injection into child components */
|
|
4357
|
+
fieldSignalContext: Signal<_ng_forge_dynamic_forms.FieldSignalContext<TModel>>;
|
|
4358
|
+
/** Default values computed from field definitions */
|
|
4359
|
+
defaultValues: WritableSignal<TModel>;
|
|
4360
|
+
/** The Angular Signal Form instance */
|
|
4361
|
+
form: Signal<FieldTree<TModel, string | number>>;
|
|
4362
|
+
/** Current form values (reactive) */
|
|
4363
|
+
formValue: Signal<TModel | (TModel extends infer T ? T extends TModel ? T extends _angular_forms.AbstractControl<unknown, infer TValue extends unknown, any> ? TValue : never : never : never)>;
|
|
4364
|
+
/** Whether the form is currently valid */
|
|
4365
|
+
valid: Signal<boolean>;
|
|
4366
|
+
/** Whether the form is currently invalid */
|
|
4367
|
+
invalid: Signal<boolean>;
|
|
4368
|
+
/** Whether any form field has been modified */
|
|
4369
|
+
dirty: Signal<boolean>;
|
|
4370
|
+
/** Whether any form field has been touched (blurred) */
|
|
4371
|
+
touched: Signal<boolean>;
|
|
4372
|
+
/** Current validation errors from all fields */
|
|
4373
|
+
errors: Signal<_angular_forms_signals.ValidationError.WithField[]>;
|
|
4374
|
+
/** Whether the form is disabled (from options or form state) */
|
|
4375
|
+
disabled: Signal<boolean>;
|
|
4376
|
+
/** Whether the form is currently submitting */
|
|
4377
|
+
submitting: Signal<boolean>;
|
|
4378
|
+
/** Collects errors from async field component loading for error boundary patterns */
|
|
4379
|
+
fieldLoadingErrors: WritableSignal<FieldLoadingError[]>;
|
|
4380
|
+
/** Whether to render the form template */
|
|
4381
|
+
shouldRender: Signal<boolean>;
|
|
4382
|
+
/** Resolved fields ready for rendering */
|
|
4383
|
+
protected resolvedFields: Signal<ResolvedField[]>;
|
|
4384
|
+
private totalComponentsCount;
|
|
4385
|
+
initialized$: rxjs.Observable<boolean>;
|
|
3173
4386
|
/** Emits when form validity changes. */
|
|
3174
|
-
|
|
4387
|
+
validityChange: _angular_core.OutputRef<boolean>;
|
|
3175
4388
|
/** Emits when form dirty state changes. */
|
|
3176
|
-
|
|
4389
|
+
dirtyChange: _angular_core.OutputRef<boolean>;
|
|
3177
4390
|
/**
|
|
3178
4391
|
* Emits form values when submitted (via SubmitEvent) and form is valid.
|
|
3179
4392
|
*
|
|
@@ -3183,23 +4396,22 @@ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFie
|
|
|
3183
4396
|
*
|
|
3184
4397
|
* Note: Does not emit when `submission.action` is configured - use one or the other.
|
|
3185
4398
|
*/
|
|
3186
|
-
|
|
4399
|
+
submitted: _angular_core.OutputRef<Partial<TModel>>;
|
|
3187
4400
|
/** Emits when form is reset to default values. */
|
|
3188
|
-
|
|
4401
|
+
reset: _angular_core.OutputRef<FormResetEvent>;
|
|
3189
4402
|
/** Emits when form is cleared to empty state. */
|
|
3190
|
-
|
|
4403
|
+
cleared: _angular_core.OutputRef<FormClearEvent>;
|
|
3191
4404
|
/** Emits all form events for custom event handling. */
|
|
3192
|
-
|
|
4405
|
+
events: _angular_core.OutputRef<_ng_forge_dynamic_forms.FormEvent>;
|
|
3193
4406
|
/**
|
|
3194
4407
|
* Emits when all form components are initialized and ready for interaction.
|
|
3195
4408
|
* Useful for E2E testing to ensure the form is fully rendered before interaction.
|
|
3196
4409
|
*/
|
|
3197
|
-
|
|
4410
|
+
initialized: _angular_core.OutputRef<boolean>;
|
|
3198
4411
|
/** Emits when the current page changes in paged forms. */
|
|
3199
|
-
|
|
4412
|
+
onPageChange: _angular_core.OutputRef<PageChangeEvent>;
|
|
3200
4413
|
/** Emits when page navigation state changes (canGoNext, canGoPrevious, etc.). */
|
|
3201
|
-
|
|
3202
|
-
protected readonly resolvedFields: Signal<ResolvedField[]>;
|
|
4414
|
+
onPageNavigationStateChange: _angular_core.OutputRef<PageNavigationStateChangeEvent>;
|
|
3203
4415
|
constructor();
|
|
3204
4416
|
/**
|
|
3205
4417
|
* Handles native form submission triggered by:
|
|
@@ -3215,12 +4427,6 @@ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFie
|
|
|
3215
4427
|
protected onNativeSubmit(event: Event): void;
|
|
3216
4428
|
private setupEffects;
|
|
3217
4429
|
private setupEventHandlers;
|
|
3218
|
-
private registerValidatorsFromConfig;
|
|
3219
|
-
private createFormSetupFromConfig;
|
|
3220
|
-
private createEmptyFormSetup;
|
|
3221
|
-
private onFormReset;
|
|
3222
|
-
private onFormClear;
|
|
3223
|
-
ngOnDestroy(): void;
|
|
3224
4430
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DynamicForm<any, any>, never>;
|
|
3225
4431
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DynamicForm<any, any>, "form[dynamic-form]", never, { "config": { "alias": "dynamic-form"; "required": true; "isSignal": true; }; "formOptions": { "alias": "formOptions"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "validityChange": "validityChange"; "dirtyChange": "dirtyChange"; "submitted": "submitted"; "reset": "reset"; "cleared": "cleared"; "events": "events"; "initialized": "initialized"; "onPageChange": "onPageChange"; "onPageNavigationStateChange": "onPageNavigationStateChange"; }, never, never, true, never>;
|
|
3226
4432
|
}
|
|
@@ -3239,6 +4445,48 @@ interface DynamicFormFeature<TKind extends string = string> {
|
|
|
3239
4445
|
ɵproviders: Provider[];
|
|
3240
4446
|
}
|
|
3241
4447
|
|
|
4448
|
+
/**
|
|
4449
|
+
* Type guard to check if a field is a container field (page, row, group, or array)
|
|
4450
|
+
* Container fields have a 'fields' property and don't contribute values directly.
|
|
4451
|
+
* This overload works with RegisteredFieldTypes for full type narrowing.
|
|
4452
|
+
*/
|
|
4453
|
+
declare function isContainerField(field: RegisteredFieldTypes): field is ContainerFieldTypes;
|
|
4454
|
+
/**
|
|
4455
|
+
* Type guard to check if a field is a container field (page, row, group, or array).
|
|
4456
|
+
* This overload works with any FieldDef for looser type checking.
|
|
4457
|
+
*/
|
|
4458
|
+
declare function isContainerField(field: FieldDef<unknown>): boolean;
|
|
4459
|
+
/**
|
|
4460
|
+
* Type guard to check if a field is a leaf field (value or display field)
|
|
4461
|
+
* Leaf fields don't have children and either contribute values or display content
|
|
4462
|
+
*/
|
|
4463
|
+
declare function isLeafField(field: RegisteredFieldTypes): field is LeafFieldTypes;
|
|
4464
|
+
/**
|
|
4465
|
+
* Type guard to check if a field has a value property (value-bearing field)
|
|
4466
|
+
* These fields contribute to the form value output
|
|
4467
|
+
* Note: Using `unknown` in the Extract condition to match any value type
|
|
4468
|
+
*/
|
|
4469
|
+
declare function isValueBearingField(field: RegisteredFieldTypes): field is Extract<RegisteredFieldTypes, {
|
|
4470
|
+
value: unknown;
|
|
4471
|
+
}>;
|
|
4472
|
+
/**
|
|
4473
|
+
* Type guard to check if a field is excluded from form values (display-only field)
|
|
4474
|
+
* Currently this includes text fields and any other fields without a value property
|
|
4475
|
+
*/
|
|
4476
|
+
declare function isDisplayOnlyField(field: RegisteredFieldTypes): boolean;
|
|
4477
|
+
|
|
4478
|
+
/**
|
|
4479
|
+
* Helper types for creating type-safe field configurations with proper nesting constraints
|
|
4480
|
+
*/
|
|
4481
|
+
|
|
4482
|
+
/**
|
|
4483
|
+
* Type helper for accessing nested field paths safely
|
|
4484
|
+
* This allows accessing child paths while maintaining some type safety
|
|
4485
|
+
*/
|
|
4486
|
+
type FieldPathAccess<TValue> = {
|
|
4487
|
+
[K in keyof TValue]: SchemaPath<TValue[K]> | SchemaPathTree<TValue[K]>;
|
|
4488
|
+
};
|
|
4489
|
+
|
|
3242
4490
|
/**
|
|
3243
4491
|
* Extract FieldDef type from FieldTypeDefinition
|
|
3244
4492
|
*/
|
|
@@ -3509,6 +4757,100 @@ declare class NoopLogger implements Logger {
|
|
|
3509
4757
|
*/
|
|
3510
4758
|
declare function withEventFormValue(): DynamicFormFeature<'event-form-value'>;
|
|
3511
4759
|
|
|
4760
|
+
/**
|
|
4761
|
+
* Configuration for excluding field values from form submission output
|
|
4762
|
+
* based on their reactive state (hidden, disabled, readonly).
|
|
4763
|
+
*
|
|
4764
|
+
* Supports a 3-tier configuration hierarchy: Field > Form > Global.
|
|
4765
|
+
* The most specific level wins for each property.
|
|
4766
|
+
*
|
|
4767
|
+
* @public
|
|
4768
|
+
*/
|
|
4769
|
+
interface ValueExclusionConfig {
|
|
4770
|
+
/**
|
|
4771
|
+
* Whether to exclude the value of hidden fields from submission output.
|
|
4772
|
+
*
|
|
4773
|
+
* When `true`, fields whose `hidden()` state is `true` will have their
|
|
4774
|
+
* values omitted from the submitted form value.
|
|
4775
|
+
*
|
|
4776
|
+
* @remarks
|
|
4777
|
+
* This does NOT affect HiddenField (`type: 'hidden'`) — those fields
|
|
4778
|
+
* store values without UI and their `hidden()` state is always `false`.
|
|
4779
|
+
*/
|
|
4780
|
+
readonly excludeValueIfHidden?: boolean;
|
|
4781
|
+
/**
|
|
4782
|
+
* Whether to exclude the value of disabled fields from submission output.
|
|
4783
|
+
*
|
|
4784
|
+
* When `true`, fields whose `disabled()` state is `true` will have their
|
|
4785
|
+
* values omitted from the submitted form value.
|
|
4786
|
+
*/
|
|
4787
|
+
readonly excludeValueIfDisabled?: boolean;
|
|
4788
|
+
/**
|
|
4789
|
+
* Whether to exclude the value of readonly fields from submission output.
|
|
4790
|
+
*
|
|
4791
|
+
* When `true`, fields whose `readonly()` state is `true` will have their
|
|
4792
|
+
* values omitted from the submitted form value.
|
|
4793
|
+
*/
|
|
4794
|
+
readonly excludeValueIfReadonly?: boolean;
|
|
4795
|
+
}
|
|
4796
|
+
/**
|
|
4797
|
+
* Fully resolved exclusion config with all properties required.
|
|
4798
|
+
* Produced by the 3-tier resolution: `field ?? form ?? global`.
|
|
4799
|
+
*
|
|
4800
|
+
* @public
|
|
4801
|
+
*/
|
|
4802
|
+
type ResolvedValueExclusionConfig = Required<ValueExclusionConfig>;
|
|
4803
|
+
|
|
4804
|
+
/**
|
|
4805
|
+
* Configures global value exclusion defaults for form submission output.
|
|
4806
|
+
*
|
|
4807
|
+
* Value exclusion is **enabled by default** — field values are excluded from the
|
|
4808
|
+
* `(submitted)` output based on their reactive state. Use this feature to
|
|
4809
|
+
* override those defaults. This does NOT affect two-way binding (`value` model /
|
|
4810
|
+
* `entity`) — fields retain their values internally.
|
|
4811
|
+
*
|
|
4812
|
+
* @example Default behavior (all exclusions enabled)
|
|
4813
|
+
* ```typescript
|
|
4814
|
+
* provideDynamicForm(
|
|
4815
|
+
* ...withMaterialFields(),
|
|
4816
|
+
* withValueExclusionDefaults()
|
|
4817
|
+
* )
|
|
4818
|
+
* ```
|
|
4819
|
+
*
|
|
4820
|
+
* @example Disable specific exclusions
|
|
4821
|
+
* ```typescript
|
|
4822
|
+
* provideDynamicForm(
|
|
4823
|
+
* ...withMaterialFields(),
|
|
4824
|
+
* withValueExclusionDefaults({ excludeValueIfReadonly: false })
|
|
4825
|
+
* )
|
|
4826
|
+
* ```
|
|
4827
|
+
*
|
|
4828
|
+
* @example Disable all exclusions (restore pre-v1 behavior)
|
|
4829
|
+
* ```typescript
|
|
4830
|
+
* provideDynamicForm(
|
|
4831
|
+
* ...withMaterialFields(),
|
|
4832
|
+
* withValueExclusionDefaults({
|
|
4833
|
+
* excludeValueIfHidden: false,
|
|
4834
|
+
* excludeValueIfDisabled: false,
|
|
4835
|
+
* excludeValueIfReadonly: false,
|
|
4836
|
+
* })
|
|
4837
|
+
* )
|
|
4838
|
+
* ```
|
|
4839
|
+
*
|
|
4840
|
+
* @remarks
|
|
4841
|
+
* **Precedence rules:**
|
|
4842
|
+
* 1. Per-field `excludeValueIf*` on `FieldDef` — wins for that field
|
|
4843
|
+
* 2. Per-form `excludeValueIf*` on `FormOptions` — wins for all fields in that form
|
|
4844
|
+
* 3. Global `withValueExclusionDefaults()` — baseline default
|
|
4845
|
+
* 4. No global feature — uses token default (all enabled)
|
|
4846
|
+
*
|
|
4847
|
+
* @param config - Partial override of exclusion rules. Unspecified properties default to `true`.
|
|
4848
|
+
* @returns A DynamicFormFeature that configures value exclusion defaults
|
|
4849
|
+
*
|
|
4850
|
+
* @public
|
|
4851
|
+
*/
|
|
4852
|
+
declare function withValueExclusionDefaults(config?: Partial<ValueExclusionConfig>): DynamicFormFeature<'value-exclusion'>;
|
|
4853
|
+
|
|
3512
4854
|
interface FieldOption<T = unknown> {
|
|
3513
4855
|
label: DynamicText;
|
|
3514
4856
|
value: T;
|
|
@@ -3595,127 +4937,53 @@ declare const DEFAULT_PROPS: InjectionToken<Signal<Record<string, unknown> | und
|
|
|
3595
4937
|
/**
|
|
3596
4938
|
* Injection token for form-level default validation messages.
|
|
3597
4939
|
*
|
|
3598
|
-
* Default validation messages act as fallbacks when fields have validation
|
|
3599
|
-
* errors but no field-level `validationMessages` defined.
|
|
3600
|
-
*
|
|
3601
|
-
* Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
|
|
3602
|
-
* and inherited by all children via Angular's hierarchical injector.
|
|
3603
|
-
*
|
|
3604
|
-
* The token provides a Signal to enable reactivity - when config changes, mappers
|
|
3605
|
-
* that read the signal inside their computed() will automatically update.
|
|
3606
|
-
*
|
|
3607
|
-
* @example
|
|
3608
|
-
* ```typescript
|
|
3609
|
-
* // In a mapper or component
|
|
3610
|
-
* const defaultMessagesSignal = inject(DEFAULT_VALIDATION_MESSAGES);
|
|
3611
|
-
*
|
|
3612
|
-
* return computed(() => {
|
|
3613
|
-
* const defaultMessages = defaultMessagesSignal?.(); // Read inside computed for reactivity
|
|
3614
|
-
* const message = defaultMessages?.required ?? 'Field is required';
|
|
3615
|
-
* // ...
|
|
3616
|
-
* });
|
|
3617
|
-
* ```
|
|
3618
|
-
*/
|
|
3619
|
-
declare const DEFAULT_VALIDATION_MESSAGES: InjectionToken<Signal<ValidationMessages | undefined>>;
|
|
3620
|
-
/**
|
|
3621
|
-
* Injection token for form-level options.
|
|
3622
|
-
*
|
|
3623
|
-
* Form options control form-wide behavior including button disabled states.
|
|
3624
|
-
* Used by button mappers to determine default disabled behavior.
|
|
3625
|
-
*
|
|
3626
|
-
* Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
|
|
3627
|
-
* and inherited by all children via Angular's hierarchical injector.
|
|
3628
|
-
*
|
|
3629
|
-
* The token provides a Signal to enable reactivity - when config changes, mappers
|
|
3630
|
-
* that read the signal inside their computed() will automatically update.
|
|
3631
|
-
*
|
|
3632
|
-
* @example
|
|
3633
|
-
* ```typescript
|
|
3634
|
-
* // In a button mapper
|
|
3635
|
-
* const formOptionsSignal = inject(FORM_OPTIONS);
|
|
3636
|
-
*
|
|
3637
|
-
* return computed(() => {
|
|
3638
|
-
* const formOptions = formOptionsSignal?.(); // Read inside computed for reactivity
|
|
3639
|
-
* const disableWhenInvalid = formOptions?.submitButton?.disableWhenInvalid ?? true;
|
|
3640
|
-
* // ...
|
|
3641
|
-
* });
|
|
3642
|
-
* ```
|
|
3643
|
-
*/
|
|
3644
|
-
declare const FORM_OPTIONS: InjectionToken<Signal<FormOptions | undefined>>;
|
|
3645
|
-
|
|
3646
|
-
/**
|
|
3647
|
-
* Context for resolving button disabled state.
|
|
3648
|
-
*
|
|
3649
|
-
* @public
|
|
3650
|
-
*/
|
|
3651
|
-
interface ButtonLogicContext {
|
|
3652
|
-
/** The form's FieldTree instance (supports both string and number keys for array indices) */
|
|
3653
|
-
form: FieldTree<unknown, string | number>;
|
|
3654
|
-
/** Form-level options */
|
|
3655
|
-
formOptions?: FormOptions;
|
|
3656
|
-
/** Field-level logic array (if provided) */
|
|
3657
|
-
fieldLogic?: LogicConfig[];
|
|
3658
|
-
/** Explicit disabled state from field definition */
|
|
3659
|
-
explicitlyDisabled?: boolean;
|
|
3660
|
-
/** Current page validity signal (for paged forms) */
|
|
3661
|
-
currentPageValid?: Signal<boolean>;
|
|
3662
|
-
/** Current form value for evaluating conditional expressions */
|
|
3663
|
-
formValue?: unknown;
|
|
3664
|
-
/** Optional logger for diagnostic output. Falls back to no-op logger if not provided. */
|
|
3665
|
-
logger?: Logger;
|
|
3666
|
-
}
|
|
3667
|
-
/**
|
|
3668
|
-
* Resolves the disabled state for a submit button.
|
|
3669
|
-
*
|
|
3670
|
-
* The disabled state is determined by (in order of precedence):
|
|
3671
|
-
* 1. Explicit `disabled: true` on the field definition
|
|
3672
|
-
* 2. Field-level `logic` array (if present, overrides form-level defaults)
|
|
3673
|
-
* 3. Form-level `options.submitButton` defaults
|
|
4940
|
+
* Default validation messages act as fallbacks when fields have validation
|
|
4941
|
+
* errors but no field-level `validationMessages` defined.
|
|
3674
4942
|
*
|
|
3675
|
-
*
|
|
3676
|
-
*
|
|
4943
|
+
* Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
|
|
4944
|
+
* and inherited by all children via Angular's hierarchical injector.
|
|
4945
|
+
*
|
|
4946
|
+
* The token provides a Signal to enable reactivity - when config changes, mappers
|
|
4947
|
+
* that read the signal inside their computed() will automatically update.
|
|
3677
4948
|
*
|
|
3678
4949
|
* @example
|
|
3679
4950
|
* ```typescript
|
|
3680
|
-
*
|
|
3681
|
-
*
|
|
3682
|
-
* formOptions: config.options,
|
|
3683
|
-
* fieldLogic: buttonField.logic,
|
|
3684
|
-
* explicitlyDisabled: buttonField.disabled,
|
|
3685
|
-
* });
|
|
4951
|
+
* // In a mapper or component
|
|
4952
|
+
* const defaultMessagesSignal = inject(DEFAULT_VALIDATION_MESSAGES);
|
|
3686
4953
|
*
|
|
3687
|
-
*
|
|
3688
|
-
*
|
|
4954
|
+
* return computed(() => {
|
|
4955
|
+
* const defaultMessages = defaultMessagesSignal?.(); // Read inside computed for reactivity
|
|
4956
|
+
* const message = defaultMessages?.required ?? 'Field is required';
|
|
4957
|
+
* // ...
|
|
4958
|
+
* });
|
|
3689
4959
|
* ```
|
|
3690
|
-
*
|
|
3691
|
-
* @public
|
|
3692
4960
|
*/
|
|
3693
|
-
declare
|
|
4961
|
+
declare const DEFAULT_VALIDATION_MESSAGES: InjectionToken<Signal<ValidationMessages | undefined>>;
|
|
3694
4962
|
/**
|
|
3695
|
-
*
|
|
4963
|
+
* Injection token for form-level options.
|
|
3696
4964
|
*
|
|
3697
|
-
*
|
|
3698
|
-
*
|
|
3699
|
-
* 2. Field-level `logic` array (if present, overrides form-level defaults)
|
|
3700
|
-
* 3. Form-level `options.nextButton` defaults
|
|
4965
|
+
* Form options control form-wide behavior including button disabled states.
|
|
4966
|
+
* Used by button mappers to determine default disabled behavior.
|
|
3701
4967
|
*
|
|
3702
|
-
*
|
|
3703
|
-
*
|
|
4968
|
+
* Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
|
|
4969
|
+
* and inherited by all children via Angular's hierarchical injector.
|
|
4970
|
+
*
|
|
4971
|
+
* The token provides a Signal to enable reactivity - when config changes, mappers
|
|
4972
|
+
* that read the signal inside their computed() will automatically update.
|
|
3704
4973
|
*
|
|
3705
4974
|
* @example
|
|
3706
4975
|
* ```typescript
|
|
3707
|
-
*
|
|
3708
|
-
*
|
|
3709
|
-
*
|
|
3710
|
-
*
|
|
3711
|
-
*
|
|
3712
|
-
*
|
|
4976
|
+
* // In a button mapper
|
|
4977
|
+
* const formOptionsSignal = inject(FORM_OPTIONS);
|
|
4978
|
+
*
|
|
4979
|
+
* return computed(() => {
|
|
4980
|
+
* const formOptions = formOptionsSignal?.(); // Read inside computed for reactivity
|
|
4981
|
+
* const disableWhenInvalid = formOptions?.submitButton?.disableWhenInvalid ?? true;
|
|
4982
|
+
* // ...
|
|
3713
4983
|
* });
|
|
3714
4984
|
* ```
|
|
3715
|
-
*
|
|
3716
|
-
* @public
|
|
3717
4985
|
*/
|
|
3718
|
-
declare
|
|
4986
|
+
declare const FORM_OPTIONS: InjectionToken<Signal<FormOptions | undefined>>;
|
|
3719
4987
|
|
|
3720
4988
|
interface FieldWithValidation {
|
|
3721
4989
|
readonly required?: boolean;
|
|
@@ -3783,7 +5051,7 @@ interface BaseValueField<TProps, TValue, TMeta extends FieldMeta = FieldMeta> ex
|
|
|
3783
5051
|
required?: boolean;
|
|
3784
5052
|
}
|
|
3785
5053
|
declare function isValueField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseValueField<TProps, ValueType, TMeta>;
|
|
3786
|
-
type ExcludedKeys$1 = 'type' | 'conditionals' | 'value' | 'valueType' | 'disabled' | 'readonly' | 'hidden' | 'col' | 'minValue' | 'maxValue' | 'step' | 'required' | 'email' | 'min' | 'max' | 'minLength' | 'maxLength' | 'pattern' | 'validators' | 'logic' | 'derivation' | 'schemas';
|
|
5054
|
+
type ExcludedKeys$1 = 'type' | 'conditionals' | 'value' | 'valueType' | 'disabled' | 'readonly' | 'hidden' | 'col' | 'minValue' | 'maxValue' | 'step' | 'required' | 'email' | 'min' | 'max' | 'minLength' | 'maxLength' | 'pattern' | 'validators' | 'logic' | 'derivation' | 'schemas' | 'excludeValueIfHidden' | 'excludeValueIfDisabled' | 'excludeValueIfReadonly';
|
|
3787
5055
|
type ValueFieldComponent<T extends BaseValueField<Record<string, unknown> | unknown, unknown>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys$1>>>;
|
|
3788
5056
|
|
|
3789
5057
|
interface BaseCheckedField<TProps, TMeta extends FieldMeta = FieldMeta> extends FieldDef<TProps, TMeta>, FieldWithValidation {
|
|
@@ -3796,7 +5064,7 @@ interface BaseCheckedField<TProps, TMeta extends FieldMeta = FieldMeta> extends
|
|
|
3796
5064
|
required?: boolean;
|
|
3797
5065
|
}
|
|
3798
5066
|
declare function isCheckedField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseCheckedField<TProps, TMeta>;
|
|
3799
|
-
type ExcludedKeys = 'type' | 'conditionals' | 'value' | 'disabled' | 'readonly' | 'hidden' | 'col' | keyof FieldWithValidation;
|
|
5067
|
+
type ExcludedKeys = 'type' | 'conditionals' | 'value' | 'disabled' | 'readonly' | 'hidden' | 'col' | keyof FieldWithValidation | 'excludeValueIfHidden' | 'excludeValueIfDisabled' | 'excludeValueIfReadonly';
|
|
3800
5068
|
type CheckedFieldComponent<T extends BaseCheckedField<Record<string, unknown> | unknown>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys>>>;
|
|
3801
5069
|
|
|
3802
5070
|
declare function applyValidator(config: ValidatorConfig, fieldPath: SchemaPath<any> | SchemaPathTree<any>): void;
|
|
@@ -3815,77 +5083,367 @@ declare class PreviousPageEvent implements FormEvent {
|
|
|
3815
5083
|
}
|
|
3816
5084
|
|
|
3817
5085
|
/**
|
|
3818
|
-
*
|
|
5086
|
+
* Template type for array items used in events.
|
|
5087
|
+
* Canonical definition lives in {@link ArrayItemDefinition} — this alias
|
|
5088
|
+
* preserves the events-specific naming convention.
|
|
5089
|
+
*/
|
|
5090
|
+
type ArrayItemDefinitionTemplate = ArrayItemDefinition;
|
|
5091
|
+
|
|
5092
|
+
/**
|
|
5093
|
+
* Event dispatched to append a new item at the END of an array field.
|
|
5094
|
+
*
|
|
5095
|
+
* This is the most common array operation - adding items to the end.
|
|
5096
|
+
* For other positions, use {@link PrependArrayItemEvent} or {@link InsertArrayItemEvent}.
|
|
5097
|
+
*
|
|
5098
|
+
* @example
|
|
5099
|
+
* ```typescript
|
|
5100
|
+
* // Object item: append { name, email } object
|
|
5101
|
+
* eventBus.dispatch(arrayEvent('contacts').append([
|
|
5102
|
+
* { key: 'name', type: 'input', label: 'Name' },
|
|
5103
|
+
* { key: 'email', type: 'input', label: 'Email' }
|
|
5104
|
+
* ]));
|
|
5105
|
+
*
|
|
5106
|
+
* // Primitive item: append single value
|
|
5107
|
+
* eventBus.dispatch(arrayEvent('tags').append(
|
|
5108
|
+
* { key: 'tag', type: 'input', label: 'Tag' }
|
|
5109
|
+
* ));
|
|
5110
|
+
* ```
|
|
5111
|
+
*
|
|
5112
|
+
* @typeParam TTemplate - The type of the template (single field or array of fields)
|
|
5113
|
+
*/
|
|
5114
|
+
declare class AppendArrayItemEvent<TTemplate extends ArrayItemDefinitionTemplate = ArrayItemDefinitionTemplate> implements FormEvent {
|
|
5115
|
+
/** The key of the array field to append an item to */
|
|
5116
|
+
readonly arrayKey: string;
|
|
5117
|
+
/**
|
|
5118
|
+
* Template for the new array item. REQUIRED.
|
|
5119
|
+
* - Single field (FieldDef): Creates a primitive item (field's value is extracted directly)
|
|
5120
|
+
* - Array of fields (FieldDef[]): Creates an object item (fields merged into object)
|
|
5121
|
+
*/
|
|
5122
|
+
readonly template: TTemplate;
|
|
5123
|
+
readonly type: "append-array-item";
|
|
5124
|
+
constructor(
|
|
5125
|
+
/** The key of the array field to append an item to */
|
|
5126
|
+
arrayKey: string,
|
|
5127
|
+
/**
|
|
5128
|
+
* Template for the new array item. REQUIRED.
|
|
5129
|
+
* - Single field (FieldDef): Creates a primitive item (field's value is extracted directly)
|
|
5130
|
+
* - Array of fields (FieldDef[]): Creates an object item (fields merged into object)
|
|
5131
|
+
*/
|
|
5132
|
+
template: TTemplate);
|
|
5133
|
+
}
|
|
5134
|
+
|
|
5135
|
+
/**
|
|
5136
|
+
* Event dispatched to prepend a new item at the BEGINNING of an array field.
|
|
3819
5137
|
*
|
|
3820
|
-
*
|
|
3821
|
-
*
|
|
3822
|
-
* based on the provided field template, or uses the array's own template if not provided.
|
|
5138
|
+
* Use this when new items should appear at the start of the list.
|
|
5139
|
+
* For appending to the end, use {@link AppendArrayItemEvent}.
|
|
3823
5140
|
*
|
|
3824
5141
|
* @example
|
|
3825
5142
|
* ```typescript
|
|
3826
|
-
* //
|
|
3827
|
-
* eventBus.dispatch(
|
|
5143
|
+
* // Object item: prepend { name } object
|
|
5144
|
+
* eventBus.dispatch(arrayEvent('contacts').prepend([
|
|
5145
|
+
* { key: 'name', type: 'input', label: 'Name' }
|
|
5146
|
+
* ]));
|
|
5147
|
+
*
|
|
5148
|
+
* // Primitive item: prepend single value
|
|
5149
|
+
* eventBus.dispatch(arrayEvent('tags').prepend(
|
|
5150
|
+
* { key: 'tag', type: 'input', label: 'Tag' }
|
|
5151
|
+
* ));
|
|
5152
|
+
* ```
|
|
5153
|
+
*
|
|
5154
|
+
* @typeParam TTemplate - The type of the template (single field or array of fields)
|
|
5155
|
+
*/
|
|
5156
|
+
declare class PrependArrayItemEvent<TTemplate extends ArrayItemDefinitionTemplate = ArrayItemDefinitionTemplate> implements FormEvent {
|
|
5157
|
+
/** The key of the array field to prepend an item to */
|
|
5158
|
+
readonly arrayKey: string;
|
|
5159
|
+
/**
|
|
5160
|
+
* Template for the new array item. REQUIRED.
|
|
5161
|
+
* - Single field (FieldDef): Creates a primitive item (field's value is extracted directly)
|
|
5162
|
+
* - Array of fields (FieldDef[]): Creates an object item (fields merged into object)
|
|
5163
|
+
*/
|
|
5164
|
+
readonly template: TTemplate;
|
|
5165
|
+
readonly type: "prepend-array-item";
|
|
5166
|
+
constructor(
|
|
5167
|
+
/** The key of the array field to prepend an item to */
|
|
5168
|
+
arrayKey: string,
|
|
5169
|
+
/**
|
|
5170
|
+
* Template for the new array item. REQUIRED.
|
|
5171
|
+
* - Single field (FieldDef): Creates a primitive item (field's value is extracted directly)
|
|
5172
|
+
* - Array of fields (FieldDef[]): Creates an object item (fields merged into object)
|
|
5173
|
+
*/
|
|
5174
|
+
template: TTemplate);
|
|
5175
|
+
}
|
|
5176
|
+
|
|
5177
|
+
/**
|
|
5178
|
+
* Event dispatched to insert a new item at a SPECIFIC INDEX in an array field.
|
|
3828
5179
|
*
|
|
3829
|
-
*
|
|
3830
|
-
*
|
|
3831
|
-
* eventBus.dispatch(AddArrayItemEvent, 'contacts', fieldTemplate);
|
|
5180
|
+
* Use this when you need precise control over where the new item appears.
|
|
5181
|
+
* For simpler operations, use {@link AppendArrayItemEvent} or {@link PrependArrayItemEvent}.
|
|
3832
5182
|
*
|
|
3833
|
-
*
|
|
3834
|
-
*
|
|
5183
|
+
* @example
|
|
5184
|
+
* ```typescript
|
|
5185
|
+
* // Object item: insert { name } object at index 2
|
|
5186
|
+
* eventBus.dispatch(arrayEvent('contacts').insertAt(2, [
|
|
5187
|
+
* { key: 'name', type: 'input', label: 'Name' }
|
|
5188
|
+
* ]));
|
|
5189
|
+
*
|
|
5190
|
+
* // Primitive item: insert single value at index 2
|
|
5191
|
+
* eventBus.dispatch(arrayEvent('tags').insertAt(2,
|
|
5192
|
+
* { key: 'tag', type: 'input', label: 'Tag' }
|
|
5193
|
+
* ));
|
|
3835
5194
|
* ```
|
|
5195
|
+
*
|
|
5196
|
+
* @typeParam TTemplate - The type of the template (single field or array of fields)
|
|
3836
5197
|
*/
|
|
3837
|
-
declare class
|
|
3838
|
-
/** The key of the array field to
|
|
5198
|
+
declare class InsertArrayItemEvent<TTemplate extends ArrayItemDefinitionTemplate = ArrayItemDefinitionTemplate> implements FormEvent {
|
|
5199
|
+
/** The key of the array field to insert an item into */
|
|
3839
5200
|
readonly arrayKey: string;
|
|
5201
|
+
/** The index at which to insert the new item */
|
|
5202
|
+
readonly index: number;
|
|
3840
5203
|
/**
|
|
3841
|
-
*
|
|
3842
|
-
*
|
|
5204
|
+
* Template for the new array item. REQUIRED.
|
|
5205
|
+
* - Single field (FieldDef): Creates a primitive item (field's value is extracted directly)
|
|
5206
|
+
* - Array of fields (FieldDef[]): Creates an object item (fields merged into object)
|
|
3843
5207
|
*/
|
|
3844
|
-
readonly
|
|
3845
|
-
|
|
3846
|
-
readonly index?: number | undefined;
|
|
3847
|
-
readonly type: "add-array-item";
|
|
5208
|
+
readonly template: TTemplate;
|
|
5209
|
+
readonly type: "insert-array-item";
|
|
3848
5210
|
constructor(
|
|
3849
|
-
/** The key of the array field to
|
|
5211
|
+
/** The key of the array field to insert an item into */
|
|
3850
5212
|
arrayKey: string,
|
|
5213
|
+
/** The index at which to insert the new item */
|
|
5214
|
+
index: number,
|
|
3851
5215
|
/**
|
|
3852
|
-
*
|
|
3853
|
-
*
|
|
5216
|
+
* Template for the new array item. REQUIRED.
|
|
5217
|
+
* - Single field (FieldDef): Creates a primitive item (field's value is extracted directly)
|
|
5218
|
+
* - Array of fields (FieldDef[]): Creates an object item (fields merged into object)
|
|
3854
5219
|
*/
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
5220
|
+
template: TTemplate);
|
|
5221
|
+
}
|
|
5222
|
+
|
|
5223
|
+
/**
|
|
5224
|
+
* Event dispatched to remove the LAST item from an array field.
|
|
5225
|
+
*
|
|
5226
|
+
* Equivalent to JavaScript's `Array.pop()` - removes from the end.
|
|
5227
|
+
* For removing from the beginning, use {@link ShiftArrayItemEvent}.
|
|
5228
|
+
* For removing at a specific index, use {@link RemoveAtIndexEvent}.
|
|
5229
|
+
*
|
|
5230
|
+
* @example
|
|
5231
|
+
* ```typescript
|
|
5232
|
+
* // Use the builder API (recommended)
|
|
5233
|
+
* eventBus.dispatch(arrayEvent('contacts').pop());
|
|
5234
|
+
*
|
|
5235
|
+
* // Or instantiate directly
|
|
5236
|
+
* eventBus.dispatch(new PopArrayItemEvent('contacts'));
|
|
5237
|
+
* ```
|
|
5238
|
+
*/
|
|
5239
|
+
declare class PopArrayItemEvent implements FormEvent {
|
|
5240
|
+
/** The key of the array field to remove the last item from */
|
|
5241
|
+
readonly arrayKey: string;
|
|
5242
|
+
readonly type: "pop-array-item";
|
|
5243
|
+
constructor(
|
|
5244
|
+
/** The key of the array field to remove the last item from */
|
|
5245
|
+
arrayKey: string);
|
|
3858
5246
|
}
|
|
3859
5247
|
|
|
3860
5248
|
/**
|
|
3861
|
-
* Event dispatched to remove
|
|
5249
|
+
* Event dispatched to remove the FIRST item from an array field.
|
|
3862
5250
|
*
|
|
3863
|
-
*
|
|
3864
|
-
*
|
|
3865
|
-
* at
|
|
5251
|
+
* Equivalent to JavaScript's `Array.shift()` - removes from the beginning.
|
|
5252
|
+
* For removing from the end, use {@link PopArrayItemEvent}.
|
|
5253
|
+
* For removing at a specific index, use {@link RemoveAtIndexEvent}.
|
|
3866
5254
|
*
|
|
3867
5255
|
* @example
|
|
3868
5256
|
* ```typescript
|
|
3869
|
-
* //
|
|
3870
|
-
* eventBus.dispatch(
|
|
5257
|
+
* // Use the builder API (recommended)
|
|
5258
|
+
* eventBus.dispatch(arrayEvent('contacts').shift());
|
|
3871
5259
|
*
|
|
3872
|
-
* //
|
|
3873
|
-
* eventBus.dispatch(
|
|
5260
|
+
* // Or instantiate directly
|
|
5261
|
+
* eventBus.dispatch(new ShiftArrayItemEvent('contacts'));
|
|
3874
5262
|
* ```
|
|
3875
5263
|
*/
|
|
3876
|
-
declare class
|
|
5264
|
+
declare class ShiftArrayItemEvent implements FormEvent {
|
|
5265
|
+
/** The key of the array field to remove the first item from */
|
|
5266
|
+
readonly arrayKey: string;
|
|
5267
|
+
readonly type: "shift-array-item";
|
|
5268
|
+
constructor(
|
|
5269
|
+
/** The key of the array field to remove the first item from */
|
|
5270
|
+
arrayKey: string);
|
|
5271
|
+
}
|
|
5272
|
+
|
|
5273
|
+
/**
|
|
5274
|
+
* Event dispatched to remove an item at a SPECIFIC INDEX from an array field.
|
|
5275
|
+
*
|
|
5276
|
+
* Use this when you need precise control over which item to remove.
|
|
5277
|
+
* For simpler operations, use {@link PopArrayItemEvent} or {@link ShiftArrayItemEvent}.
|
|
5278
|
+
*
|
|
5279
|
+
* @example
|
|
5280
|
+
* ```typescript
|
|
5281
|
+
* // Use the builder API (recommended)
|
|
5282
|
+
* eventBus.dispatch(arrayEvent('contacts').removeAt(2));
|
|
5283
|
+
*
|
|
5284
|
+
* // Or instantiate directly
|
|
5285
|
+
* eventBus.dispatch(new RemoveAtIndexEvent('contacts', 2));
|
|
5286
|
+
* ```
|
|
5287
|
+
*/
|
|
5288
|
+
declare class RemoveAtIndexEvent implements FormEvent {
|
|
3877
5289
|
/** The key of the array field to remove an item from */
|
|
3878
5290
|
readonly arrayKey: string;
|
|
3879
|
-
/**
|
|
3880
|
-
readonly index
|
|
3881
|
-
readonly type: "remove-
|
|
5291
|
+
/** The index of the item to remove */
|
|
5292
|
+
readonly index: number;
|
|
5293
|
+
readonly type: "remove-at-index";
|
|
3882
5294
|
constructor(
|
|
3883
5295
|
/** The key of the array field to remove an item from */
|
|
3884
5296
|
arrayKey: string,
|
|
3885
|
-
/**
|
|
3886
|
-
index
|
|
5297
|
+
/** The index of the item to remove */
|
|
5298
|
+
index: number);
|
|
3887
5299
|
}
|
|
3888
5300
|
|
|
5301
|
+
/**
|
|
5302
|
+
* Builder for array manipulation events.
|
|
5303
|
+
*
|
|
5304
|
+
* Provides a fluent, discoverable API for array operations.
|
|
5305
|
+
* Type `arrayEvent('key').` in your IDE to see all available operations.
|
|
5306
|
+
*
|
|
5307
|
+
* **BREAKING CHANGE**: Template is now required for add operations.
|
|
5308
|
+
*
|
|
5309
|
+
* Supports both primitive and object array items:
|
|
5310
|
+
* - Primitive: Pass a single field definition → extracts field value directly
|
|
5311
|
+
* - Object: Pass an array of field definitions → merges fields into object
|
|
5312
|
+
*
|
|
5313
|
+
* @example
|
|
5314
|
+
* ```typescript
|
|
5315
|
+
* import { arrayEvent } from '@ng-forge/dynamic-forms';
|
|
5316
|
+
*
|
|
5317
|
+
* // Object item: append { name, email } object
|
|
5318
|
+
* eventBus.dispatch(arrayEvent('contacts').append([
|
|
5319
|
+
* { key: 'name', type: 'input', label: 'Name' },
|
|
5320
|
+
* { key: 'email', type: 'input', label: 'Email' }
|
|
5321
|
+
* ]));
|
|
5322
|
+
*
|
|
5323
|
+
* // Primitive item: append single value
|
|
5324
|
+
* eventBus.dispatch(arrayEvent('tags').append(
|
|
5325
|
+
* { key: 'tag', type: 'input', label: 'Tag' }
|
|
5326
|
+
* ));
|
|
5327
|
+
*
|
|
5328
|
+
* // Removing items (no template needed)
|
|
5329
|
+
* eventBus.dispatch(arrayEvent('contacts').pop()); // Remove last
|
|
5330
|
+
* eventBus.dispatch(arrayEvent('contacts').shift()); // Remove first
|
|
5331
|
+
* eventBus.dispatch(arrayEvent('contacts').removeAt(2)); // Remove at index
|
|
5332
|
+
* ```
|
|
5333
|
+
*
|
|
5334
|
+
* @param arrayKey - The key of the array field to operate on
|
|
5335
|
+
* @returns An object with methods for all 6 array operations
|
|
5336
|
+
*/
|
|
5337
|
+
declare function arrayEvent(arrayKey: string): {
|
|
5338
|
+
/**
|
|
5339
|
+
* Append a new item at the END of the array.
|
|
5340
|
+
* This is the most common operation for adding items.
|
|
5341
|
+
*
|
|
5342
|
+
* @param template - Template for the new item (REQUIRED)
|
|
5343
|
+
* - Single field: Creates a primitive item (field's value is extracted directly)
|
|
5344
|
+
* - Array of fields: Creates an object item (fields merged into object)
|
|
5345
|
+
* @returns An AppendArrayItemEvent to dispatch
|
|
5346
|
+
*
|
|
5347
|
+
* @example
|
|
5348
|
+
* ```typescript
|
|
5349
|
+
* // Object item
|
|
5350
|
+
* eventBus.dispatch(arrayEvent('contacts').append([
|
|
5351
|
+
* { key: 'name', type: 'input', label: 'Name' }
|
|
5352
|
+
* ]));
|
|
5353
|
+
*
|
|
5354
|
+
* // Primitive item
|
|
5355
|
+
* eventBus.dispatch(arrayEvent('tags').append(
|
|
5356
|
+
* { key: 'tag', type: 'input', label: 'Tag' }
|
|
5357
|
+
* ));
|
|
5358
|
+
* ```
|
|
5359
|
+
*/
|
|
5360
|
+
append: <T extends ArrayItemDefinitionTemplate>(template: T) => AppendArrayItemEvent<T>;
|
|
5361
|
+
/**
|
|
5362
|
+
* Prepend a new item at the BEGINNING of the array.
|
|
5363
|
+
* Use when new items should appear at the start.
|
|
5364
|
+
*
|
|
5365
|
+
* @param template - Template for the new item (REQUIRED)
|
|
5366
|
+
* - Single field: Creates a primitive item (field's value is extracted directly)
|
|
5367
|
+
* - Array of fields: Creates an object item (fields merged into object)
|
|
5368
|
+
* @returns A PrependArrayItemEvent to dispatch
|
|
5369
|
+
*
|
|
5370
|
+
* @example
|
|
5371
|
+
* ```typescript
|
|
5372
|
+
* // Object item
|
|
5373
|
+
* eventBus.dispatch(arrayEvent('contacts').prepend([
|
|
5374
|
+
* { key: 'name', type: 'input', label: 'Name' }
|
|
5375
|
+
* ]));
|
|
5376
|
+
*
|
|
5377
|
+
* // Primitive item
|
|
5378
|
+
* eventBus.dispatch(arrayEvent('tags').prepend(
|
|
5379
|
+
* { key: 'tag', type: 'input', label: 'Tag' }
|
|
5380
|
+
* ));
|
|
5381
|
+
* ```
|
|
5382
|
+
*/
|
|
5383
|
+
prepend: <T extends ArrayItemDefinitionTemplate>(template: T) => PrependArrayItemEvent<T>;
|
|
5384
|
+
/**
|
|
5385
|
+
* Insert a new item at a SPECIFIC INDEX in the array.
|
|
5386
|
+
* Use when you need precise control over item placement.
|
|
5387
|
+
*
|
|
5388
|
+
* @param index - The position at which to insert the new item
|
|
5389
|
+
* @param template - Template for the new item (REQUIRED)
|
|
5390
|
+
* - Single field: Creates a primitive item (field's value is extracted directly)
|
|
5391
|
+
* - Array of fields: Creates an object item (fields merged into object)
|
|
5392
|
+
* @returns An InsertArrayItemEvent to dispatch
|
|
5393
|
+
*
|
|
5394
|
+
* @example
|
|
5395
|
+
* ```typescript
|
|
5396
|
+
* // Object item at index 2
|
|
5397
|
+
* eventBus.dispatch(arrayEvent('contacts').insertAt(2, [
|
|
5398
|
+
* { key: 'name', type: 'input', label: 'Name' }
|
|
5399
|
+
* ]));
|
|
5400
|
+
*
|
|
5401
|
+
* // Primitive item at index 2
|
|
5402
|
+
* eventBus.dispatch(arrayEvent('tags').insertAt(2,
|
|
5403
|
+
* { key: 'tag', type: 'input', label: 'Tag' }
|
|
5404
|
+
* ));
|
|
5405
|
+
* ```
|
|
5406
|
+
*/
|
|
5407
|
+
insertAt: <T extends ArrayItemDefinitionTemplate>(index: number, template: T) => InsertArrayItemEvent<T>;
|
|
5408
|
+
/**
|
|
5409
|
+
* Remove the LAST item from the array.
|
|
5410
|
+
* Equivalent to JavaScript's `Array.pop()`.
|
|
5411
|
+
*
|
|
5412
|
+
* @returns A PopArrayItemEvent to dispatch
|
|
5413
|
+
*
|
|
5414
|
+
* @example
|
|
5415
|
+
* ```typescript
|
|
5416
|
+
* eventBus.dispatch(arrayEvent('contacts').pop());
|
|
5417
|
+
* ```
|
|
5418
|
+
*/
|
|
5419
|
+
pop: () => PopArrayItemEvent;
|
|
5420
|
+
/**
|
|
5421
|
+
* Remove the FIRST item from the array.
|
|
5422
|
+
* Equivalent to JavaScript's `Array.shift()`.
|
|
5423
|
+
*
|
|
5424
|
+
* @returns A ShiftArrayItemEvent to dispatch
|
|
5425
|
+
*
|
|
5426
|
+
* @example
|
|
5427
|
+
* ```typescript
|
|
5428
|
+
* eventBus.dispatch(arrayEvent('contacts').shift());
|
|
5429
|
+
* ```
|
|
5430
|
+
*/
|
|
5431
|
+
shift: () => ShiftArrayItemEvent;
|
|
5432
|
+
/**
|
|
5433
|
+
* Remove an item at a SPECIFIC INDEX from the array.
|
|
5434
|
+
* Use when you need to remove a specific item by position.
|
|
5435
|
+
*
|
|
5436
|
+
* @param index - The position of the item to remove
|
|
5437
|
+
* @returns A RemoveAtIndexEvent to dispatch
|
|
5438
|
+
*
|
|
5439
|
+
* @example
|
|
5440
|
+
* ```typescript
|
|
5441
|
+
* eventBus.dispatch(arrayEvent('contacts').removeAt(2));
|
|
5442
|
+
* ```
|
|
5443
|
+
*/
|
|
5444
|
+
removeAt: (index: number) => RemoveAtIndexEvent;
|
|
5445
|
+
};
|
|
5446
|
+
|
|
3889
5447
|
/**
|
|
3890
5448
|
* Event bus service for form-wide event communication.
|
|
3891
5449
|
*
|
|
@@ -3984,6 +5542,8 @@ interface TokenContext {
|
|
|
3984
5542
|
arrayKey?: string;
|
|
3985
5543
|
/** Current form value for complex indexing */
|
|
3986
5544
|
formValue?: unknown;
|
|
5545
|
+
/** Template for array item creation */
|
|
5546
|
+
template?: unknown;
|
|
3987
5547
|
}
|
|
3988
5548
|
/**
|
|
3989
5549
|
* Resolves special tokens in event arguments to their actual values
|
|
@@ -3992,6 +5552,7 @@ interface TokenContext {
|
|
|
3992
5552
|
* - $key: The current field key
|
|
3993
5553
|
* - $index: The array index (if inside an array field)
|
|
3994
5554
|
* - $arrayKey: The parent array field key (if inside an array field)
|
|
5555
|
+
* - $template: The template for array item creation
|
|
3995
5556
|
* - formValue: Reference to the current form value for indexing
|
|
3996
5557
|
*
|
|
3997
5558
|
* @param args - Array of arguments that may contain tokens
|
|
@@ -4005,6 +5566,10 @@ interface TokenContext {
|
|
|
4005
5566
|
* @example
|
|
4006
5567
|
* resolveTokens(['$key', 'static'], { key: 'myField' })
|
|
4007
5568
|
* // Returns: ['myField', 'static']
|
|
5569
|
+
*
|
|
5570
|
+
* @example
|
|
5571
|
+
* resolveTokens(['$arrayKey', '$template'], { arrayKey: 'contacts', template: [{ key: 'name', type: 'input' }] })
|
|
5572
|
+
* // Returns: ['contacts', [{ key: 'name', type: 'input' }]]
|
|
4008
5573
|
*/
|
|
4009
5574
|
declare function resolveTokens(args: readonly (string | number | boolean | null | undefined)[], context: TokenContext): (string | number | boolean | null | undefined | unknown)[];
|
|
4010
5575
|
|
|
@@ -4177,6 +5742,8 @@ declare function baseFieldMapper(fieldDef: FieldDef<unknown>): Signal<Record<str
|
|
|
4177
5742
|
* Row components are layout containers that don't change the form shape.
|
|
4178
5743
|
* The row component will inject FIELD_SIGNAL_CONTEXT directly.
|
|
4179
5744
|
*
|
|
5745
|
+
* Supports hidden state resolution via `logic` array or static `hidden` property.
|
|
5746
|
+
*
|
|
4180
5747
|
* @param fieldDef The row field definition
|
|
4181
5748
|
* @returns Signal containing Record of input names to values for ngComponentOutlet
|
|
4182
5749
|
*/
|
|
@@ -4189,6 +5756,8 @@ declare function rowFieldMapper(fieldDef: RowField): Signal<Record<string, unkno
|
|
|
4189
5756
|
* The group component will inject the parent FIELD_SIGNAL_CONTEXT and create
|
|
4190
5757
|
* a scoped child injector for its nested fields.
|
|
4191
5758
|
*
|
|
5759
|
+
* Supports hidden state resolution via `logic` array or static `hidden` property.
|
|
5760
|
+
*
|
|
4192
5761
|
* @param fieldDef The group field definition
|
|
4193
5762
|
* @returns Signal containing Record of input names to values for ngComponentOutlet
|
|
4194
5763
|
*/
|
|
@@ -4201,6 +5770,8 @@ declare function groupFieldMapper(fieldDef: GroupField): Signal<Record<string, u
|
|
|
4201
5770
|
* The array component will inject the parent FIELD_SIGNAL_CONTEXT and create
|
|
4202
5771
|
* a scoped child injector for its array item fields.
|
|
4203
5772
|
*
|
|
5773
|
+
* Supports hidden state resolution via `logic` array or static `hidden` property.
|
|
5774
|
+
*
|
|
4204
5775
|
* @param fieldDef The array field definition
|
|
4205
5776
|
* @returns Signal containing Record of input names to values for ngComponentOutlet
|
|
4206
5777
|
*/
|
|
@@ -4212,6 +5783,10 @@ declare function arrayFieldMapper(fieldDef: ArrayField): Signal<Record<string, u
|
|
|
4212
5783
|
* Page fields are layout containers that don't modify the form context.
|
|
4213
5784
|
* The page component will inject FIELD_SIGNAL_CONTEXT directly.
|
|
4214
5785
|
*
|
|
5786
|
+
* Note: Unlike other container mappers (group, row, array), the page mapper does NOT
|
|
5787
|
+
* resolve hidden logic here. Page visibility is managed by the {@link PageOrchestratorComponent}
|
|
5788
|
+
* which evaluates `ContainerLogicConfig` conditions and controls the `isVisible` input.
|
|
5789
|
+
*
|
|
4215
5790
|
* @param fieldDef The page field definition
|
|
4216
5791
|
* @returns Signal containing Record of input names to values for ngComponentOutlet
|
|
4217
5792
|
*/
|
|
@@ -4221,8 +5796,14 @@ declare function pageFieldMapper(fieldDef: PageField): Signal<Record<string, unk
|
|
|
4221
5796
|
* Maps a text field definition to component inputs.
|
|
4222
5797
|
*
|
|
4223
5798
|
* Text fields are display-only fields that don't participate in the form schema.
|
|
4224
|
-
* This mapper handles the `logic` configuration by
|
|
4225
|
-
*
|
|
5799
|
+
* This mapper handles the `logic` configuration by using the non-field-hidden resolver
|
|
5800
|
+
* to evaluate conditions against the form value from RootFormRegistryService.
|
|
5801
|
+
*
|
|
5802
|
+
* Hidden state is resolved using the non-field-hidden resolver which considers:
|
|
5803
|
+
* 1. Explicit `hidden: true` on the field definition
|
|
5804
|
+
* 2. Field-level `logic` array with `type: 'hidden'` conditions
|
|
5805
|
+
*
|
|
5806
|
+
* Note: Text fields don't support disabled logic since they are display-only.
|
|
4226
5807
|
*
|
|
4227
5808
|
* @param fieldDef The text field definition
|
|
4228
5809
|
* @returns Signal containing Record of input names to values for ngComponentOutlet
|
|
@@ -4318,6 +5899,7 @@ declare class RowFieldComponent {
|
|
|
4318
5899
|
field: _angular_core.InputSignal<RowField<readonly _ng_forge_dynamic_forms.RowAllowedChildren[]>>;
|
|
4319
5900
|
key: _angular_core.InputSignal<string>;
|
|
4320
5901
|
className: _angular_core.InputSignal<string | undefined>;
|
|
5902
|
+
hidden: _angular_core.InputSignal<boolean>;
|
|
4321
5903
|
readonly hostClasses: _angular_core.Signal<string>;
|
|
4322
5904
|
readonly disabled: _angular_core.Signal<boolean>;
|
|
4323
5905
|
private readonly rawFieldRegistry;
|
|
@@ -4326,7 +5908,7 @@ declare class RowFieldComponent {
|
|
|
4326
5908
|
constructor();
|
|
4327
5909
|
private setupEffects;
|
|
4328
5910
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<RowFieldComponent, never>;
|
|
4329
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<RowFieldComponent, "div[row-field]", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
5911
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<RowFieldComponent, "div[row-field]", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "hidden": { "alias": "hidden"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
4330
5912
|
}
|
|
4331
5913
|
|
|
4332
5914
|
/**
|
|
@@ -4343,16 +5925,20 @@ declare class GroupFieldComponent<TModel extends Record<string, unknown> = Recor
|
|
|
4343
5925
|
private readonly injector;
|
|
4344
5926
|
private readonly eventBus;
|
|
4345
5927
|
private readonly logger;
|
|
4346
|
-
private readonly
|
|
4347
|
-
private readonly memoizedKeyBy;
|
|
4348
|
-
private readonly memoizedDefaultValues;
|
|
5928
|
+
private readonly fieldProcessors;
|
|
4349
5929
|
field: _angular_core.InputSignal<GroupField<readonly _ng_forge_dynamic_forms.GroupAllowedChildren[]>>;
|
|
4350
5930
|
key: _angular_core.InputSignal<string>;
|
|
4351
5931
|
className: _angular_core.InputSignal<string | undefined>;
|
|
5932
|
+
hidden: _angular_core.InputSignal<boolean>;
|
|
4352
5933
|
readonly hostClasses: _angular_core.Signal<string>;
|
|
4353
5934
|
private readonly rawFieldRegistry;
|
|
4354
5935
|
private readonly formSetup;
|
|
4355
5936
|
readonly defaultValues: _angular_core.WritableSignal<Record<string, unknown>>;
|
|
5937
|
+
/**
|
|
5938
|
+
* Entity computed from parent value, group key, and defaults.
|
|
5939
|
+
* Uses deep equality check to prevent unnecessary updates when
|
|
5940
|
+
* object spread creates new references with identical values.
|
|
5941
|
+
*/
|
|
4356
5942
|
private readonly entity;
|
|
4357
5943
|
private readonly form;
|
|
4358
5944
|
readonly formValue: _angular_core.Signal<{}>;
|
|
@@ -4363,6 +5949,7 @@ declare class GroupFieldComponent<TModel extends Record<string, unknown> = Recor
|
|
|
4363
5949
|
readonly errors: _angular_core.Signal<_angular_forms_signals.ValidationError.WithField[]>;
|
|
4364
5950
|
readonly disabled: _angular_core.Signal<boolean>;
|
|
4365
5951
|
private readonly nestedFieldTree;
|
|
5952
|
+
private readonly groupFieldSignalContext;
|
|
4366
5953
|
private readonly groupInjector;
|
|
4367
5954
|
readonly validityChange: _angular_core.OutputRef<boolean>;
|
|
4368
5955
|
readonly dirtyChange: _angular_core.OutputRef<boolean>;
|
|
@@ -4372,17 +5959,13 @@ declare class GroupFieldComponent<TModel extends Record<string, unknown> = Recor
|
|
|
4372
5959
|
constructor();
|
|
4373
5960
|
private setupEffects;
|
|
4374
5961
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<GroupFieldComponent<any>, never>;
|
|
4375
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<GroupFieldComponent<any>, "fieldset[group-field]", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; }, { "validityChange": "validityChange"; "dirtyChange": "dirtyChange"; "submitted": "submitted"; }, never, never, true, never>;
|
|
5962
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<GroupFieldComponent<any>, "fieldset[group-field]", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "hidden": { "alias": "hidden"; "required": false; "isSignal": true; }; }, { "validityChange": "validityChange"; "dirtyChange": "dirtyChange"; "submitted": "submitted"; }, never, never, true, never>;
|
|
4376
5963
|
}
|
|
4377
5964
|
|
|
4378
5965
|
/**
|
|
4379
|
-
*
|
|
4380
|
-
* Each item has a stable unique ID and a linkedSignal-based index that
|
|
4381
|
-
* automatically updates when items are added/removed.
|
|
5966
|
+
* A single field within a resolved array item.
|
|
4382
5967
|
*/
|
|
4383
|
-
interface
|
|
4384
|
-
/** Unique identifier for this item (stable across position changes). */
|
|
4385
|
-
id: string;
|
|
5968
|
+
interface ResolvedArrayItemField {
|
|
4386
5969
|
/** The loaded component type. */
|
|
4387
5970
|
component: Type<unknown>;
|
|
4388
5971
|
/** Injector providing ARRAY_CONTEXT and FIELD_SIGNAL_CONTEXT. */
|
|
@@ -4390,13 +5973,26 @@ interface ResolvedArrayItem {
|
|
|
4390
5973
|
/** Inputs signal for ngComponentOutlet - evaluated in template for reactivity. */
|
|
4391
5974
|
inputs: Signal<Record<string, unknown>>;
|
|
4392
5975
|
}
|
|
5976
|
+
/**
|
|
5977
|
+
* Resolved array item ready for declarative rendering with ngComponentOutlet.
|
|
5978
|
+
* Each item has a stable unique ID and a linkedSignal-based index that
|
|
5979
|
+
* automatically updates when items are added/removed.
|
|
5980
|
+
* Supports multiple fields per array item (e.g., name + email without a wrapper).
|
|
5981
|
+
*/
|
|
5982
|
+
interface ResolvedArrayItem {
|
|
5983
|
+
/** Unique identifier for this item (stable across position changes). */
|
|
5984
|
+
id: string;
|
|
5985
|
+
/** All fields to render for this array item. */
|
|
5986
|
+
fields: ResolvedArrayItemField[];
|
|
5987
|
+
}
|
|
4393
5988
|
|
|
4394
5989
|
/**
|
|
4395
5990
|
* Container component for rendering dynamic arrays of fields.
|
|
4396
5991
|
*
|
|
4397
|
-
* Supports add/remove operations via
|
|
5992
|
+
* Supports add/remove operations via the arrayEvent() builder API.
|
|
4398
5993
|
* Uses differential updates to optimize rendering - only recreates items when necessary.
|
|
4399
5994
|
* Each item gets a scoped injector with ARRAY_CONTEXT for position-aware operations.
|
|
5995
|
+
* Supports multiple sibling fields per array item (e.g., name + email without a wrapper).
|
|
4400
5996
|
*/
|
|
4401
5997
|
declare class ArrayFieldComponent<TModel extends Record<string, unknown> = Record<string, unknown>> {
|
|
4402
5998
|
private readonly destroyRef;
|
|
@@ -4405,28 +6001,111 @@ declare class ArrayFieldComponent<TModel extends Record<string, unknown> = Recor
|
|
|
4405
6001
|
private readonly parentInjector;
|
|
4406
6002
|
private readonly eventBus;
|
|
4407
6003
|
private readonly logger;
|
|
4408
|
-
|
|
6004
|
+
private readonly templateRegistry;
|
|
6005
|
+
private readonly generateItemId;
|
|
6006
|
+
field: _angular_core.InputSignal<ArrayField<readonly ArrayItemDefinition[]>>;
|
|
4409
6007
|
key: _angular_core.InputSignal<string>;
|
|
4410
6008
|
className: _angular_core.InputSignal<string | undefined>;
|
|
6009
|
+
hidden: _angular_core.InputSignal<boolean>;
|
|
4411
6010
|
readonly hostClasses: _angular_core.Signal<string>;
|
|
4412
6011
|
private readonly rawFieldRegistry;
|
|
4413
|
-
|
|
6012
|
+
/**
|
|
6013
|
+
* Gets the auto-remove button FieldDef from normalization metadata.
|
|
6014
|
+
* Set by simplified array normalization for primitive arrays with remove buttons.
|
|
6015
|
+
* The button is rendered alongside each item without wrapping in a row,
|
|
6016
|
+
* preserving flat primitive form values.
|
|
6017
|
+
*/
|
|
6018
|
+
private readonly autoRemoveButton;
|
|
6019
|
+
/**
|
|
6020
|
+
* For primitive array items, the key of the value field template (e.g., 'value').
|
|
6021
|
+
* Used to wrap the FormControl in the item context so getFieldTree(key) works.
|
|
6022
|
+
* Returns undefined for object arrays (FormGroup items have natural child navigation).
|
|
6023
|
+
*
|
|
6024
|
+
* Detection order:
|
|
6025
|
+
* 1. Normalization metadata (for simplified arrays — always available at normalization time)
|
|
6026
|
+
* 2. Existing item definitions (non-empty full-API arrays)
|
|
6027
|
+
* 3. Dynamically discovered key from handleAddFromEvent (empty full-API arrays)
|
|
6028
|
+
*
|
|
6029
|
+
* Uses linkedSignal: recomputes when field() changes, supports manual set()
|
|
6030
|
+
* for the dynamic discovery case.
|
|
6031
|
+
*/
|
|
6032
|
+
private readonly primitiveFieldKey;
|
|
6033
|
+
/**
|
|
6034
|
+
* Gets the item templates (field definitions) for the array.
|
|
6035
|
+
* Each element can be either:
|
|
6036
|
+
* - A single FieldDef (primitive item) - normalized to [FieldDef]
|
|
6037
|
+
* - An array of FieldDefs (object item) - used as-is
|
|
6038
|
+
*
|
|
6039
|
+
* When auto-remove is configured, the remove button is appended to each item's
|
|
6040
|
+
* template list for rendering. This is purely visual — the form schema uses the
|
|
6041
|
+
* original primitive item definition (single FieldDef → FormControl → flat value).
|
|
6042
|
+
*
|
|
6043
|
+
* Returns normalized templates where all items are arrays for consistent handling.
|
|
6044
|
+
*/
|
|
6045
|
+
private readonly itemTemplates;
|
|
4414
6046
|
private readonly arrayFieldTrees;
|
|
4415
6047
|
private readonly resolvedItemsSignal;
|
|
4416
6048
|
private readonly updateVersion;
|
|
4417
|
-
|
|
4418
|
-
|
|
6049
|
+
/**
|
|
6050
|
+
* Map of item IDs to their current positions. O(1) lookup vs O(n) indexOf().
|
|
6051
|
+
* Used by child linkedSignals to reactively track their position in the array.
|
|
6052
|
+
*/
|
|
6053
|
+
private readonly itemPositionMap;
|
|
6054
|
+
/** Read-only view of resolved items for template consumption. */
|
|
6055
|
+
readonly resolvedItems: _angular_core.Signal<ResolvedArrayItem[]>;
|
|
6056
|
+
/**
|
|
6057
|
+
* Caches the result of appending the auto-remove button to template arrays.
|
|
6058
|
+
* Keyed by template array reference — cache hits occur during recreate/resolution
|
|
6059
|
+
* operations where stored template references are reused. Add operations via
|
|
6060
|
+
* handleAddFromEvent always create a fresh `[...template]` copy (line ~277),
|
|
6061
|
+
* so each add is a cache miss by design (the spread is needed for mutability).
|
|
6062
|
+
*/
|
|
6063
|
+
private readonly autoRemoveCache;
|
|
4419
6064
|
constructor();
|
|
4420
6065
|
private setupEffects;
|
|
4421
6066
|
private setupEventHandlers;
|
|
6067
|
+
/**
|
|
6068
|
+
* Handles add operations from events (append, prepend, insert).
|
|
6069
|
+
* Creates resolved items FIRST, then updates form value.
|
|
6070
|
+
* This ensures prepend/insert work correctly - differential update sees "none"
|
|
6071
|
+
* because resolved items count already matches the new array length.
|
|
6072
|
+
*
|
|
6073
|
+
* Supports both primitive (single FieldDef) and object (FieldDef[]) templates.
|
|
6074
|
+
*/
|
|
6075
|
+
private handleAddFromEvent;
|
|
4422
6076
|
private performDifferentialUpdate;
|
|
4423
6077
|
private resolveAllItems;
|
|
4424
6078
|
private appendItems;
|
|
6079
|
+
/**
|
|
6080
|
+
* Creates an observable that resolves a single array item.
|
|
6081
|
+
*
|
|
6082
|
+
* Template resolution order:
|
|
6083
|
+
* 1. Use overrideTemplate if provided (from recreate with stored templates)
|
|
6084
|
+
* 2. Use itemTemplates[index] if within defined templates range
|
|
6085
|
+
* 3. Return undefined (item cannot be resolved without a template)
|
|
6086
|
+
*/
|
|
4425
6087
|
private createResolveItemObservable;
|
|
4426
|
-
|
|
6088
|
+
/**
|
|
6089
|
+
* Appends the auto-remove button to a templates array for rendering.
|
|
6090
|
+
* The button is added for visual rendering only — it doesn't affect the form schema.
|
|
6091
|
+
* Original templates are stored in templateRegistry WITHOUT the remove button,
|
|
6092
|
+
* so this method is called during resolution to add it dynamically.
|
|
6093
|
+
*
|
|
6094
|
+
* Uses a WeakMap cache keyed by template array reference. Cache hits occur
|
|
6095
|
+
* during recreate/resolution paths where stored templates are reused.
|
|
6096
|
+
* Add operations always pass a fresh `[...template]` copy, so they miss
|
|
6097
|
+
* the cache intentionally (the copy is needed for mutable item construction).
|
|
6098
|
+
*/
|
|
6099
|
+
private withAutoRemove;
|
|
6100
|
+
/**
|
|
6101
|
+
* Handles remove operations from events (pop, shift, removeAt).
|
|
6102
|
+
* Updates resolvedItems FIRST, then form value - this ensures differential
|
|
6103
|
+
* update sees "none" (lengths match) and avoids unnecessary recreates.
|
|
6104
|
+
* Remaining items' linkedSignal indices auto-update via itemPositionMap.
|
|
6105
|
+
*/
|
|
4427
6106
|
private removeItem;
|
|
4428
6107
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ArrayFieldComponent<any>, never>;
|
|
4429
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ArrayFieldComponent<any>, "array-field", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
6108
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ArrayFieldComponent<any>, "array-field", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "hidden": { "alias": "hidden"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
4430
6109
|
}
|
|
4431
6110
|
|
|
4432
6111
|
/**
|
|
@@ -4501,97 +6180,16 @@ declare function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): O
|
|
|
4501
6180
|
declare function interpolateParams(message: string, error: ValidationError): string;
|
|
4502
6181
|
|
|
4503
6182
|
/**
|
|
4504
|
-
* Registry service that
|
|
4505
|
-
* This provides a type-safe way to access root form values from child fields
|
|
4506
|
-
* without relying on internal Angular Forms API navigation.
|
|
4507
|
-
*
|
|
4508
|
-
* This service should be provided at the component level to ensure proper
|
|
4509
|
-
* isolation between different form instances.
|
|
6183
|
+
* Registry service that provides access to the root form and its values.
|
|
4510
6184
|
*
|
|
4511
|
-
*
|
|
4512
|
-
*
|
|
4513
|
-
* This allows logic functions to read form values during schema evaluation.
|
|
4514
|
-
* 2. registerRootForm() - Register the FieldTree AFTER form creation
|
|
4515
|
-
* This provides access to the full form structure.
|
|
4516
|
-
*
|
|
4517
|
-
* For cross-field logic to work correctly, registerFormValueSignal() should be
|
|
4518
|
-
* called BEFORE the form() call.
|
|
6185
|
+
* Constructed via factory in `provideDynamicFormDI` with signals from
|
|
6186
|
+
* `FormStateManager` — no DI injection, no Maps, no lifecycle.
|
|
4519
6187
|
*/
|
|
4520
6188
|
declare class RootFormRegistryService {
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
/**
|
|
4525
|
-
* Registers the form value signal BEFORE form creation.
|
|
4526
|
-
*
|
|
4527
|
-
* This is the recommended pattern for cross-field logic:
|
|
4528
|
-
* 1. Create the formValue signal
|
|
4529
|
-
* 2. Register it with registerFormValueSignal()
|
|
4530
|
-
* 3. Create the form with form(formValue, schema(...))
|
|
4531
|
-
*
|
|
4532
|
-
* This allows logic functions to access form values during schema evaluation.
|
|
4533
|
-
*/
|
|
4534
|
-
registerFormValueSignal(valueSignal: Signal<Record<string, unknown>>, formId?: string): void;
|
|
4535
|
-
/**
|
|
4536
|
-
* Gets the registered form value signal by id.
|
|
4537
|
-
*
|
|
4538
|
-
* This allows direct access to the source signal, which can be read
|
|
4539
|
-
* reactively to get form values.
|
|
4540
|
-
*/
|
|
4541
|
-
getFormValueSignal(formId?: string): Signal<Record<string, unknown>> | undefined;
|
|
4542
|
-
/**
|
|
4543
|
-
* Registers a root form field with an optional identifier.
|
|
4544
|
-
* If no id is provided, uses 'default' as the key.
|
|
4545
|
-
*
|
|
4546
|
-
* This triggers reactive updates for any computed signals that
|
|
4547
|
-
* depend on getRootForm().
|
|
4548
|
-
*/
|
|
4549
|
-
registerRootForm(rootField: FieldTree<unknown>, formId?: string): void;
|
|
4550
|
-
/**
|
|
4551
|
-
* Gets a registered root form field by id.
|
|
4552
|
-
*
|
|
4553
|
-
* IMPORTANT: This method reads from a signal, which means any computed
|
|
4554
|
-
* signal that calls this method will automatically re-evaluate when
|
|
4555
|
-
* the form is registered or updated.
|
|
4556
|
-
*/
|
|
4557
|
-
getRootForm(formId?: string): FieldTree<unknown> | undefined;
|
|
4558
|
-
/**
|
|
4559
|
-
* Gets the form value, preferring the direct signal if available.
|
|
4560
|
-
*
|
|
4561
|
-
* This method tries in order:
|
|
4562
|
-
* 1. Form value signal (if registered) - reads directly from source signal
|
|
4563
|
-
* 2. Root form's .value() (if registered) - reads from FieldTree
|
|
4564
|
-
* 3. Returns empty object if nothing is registered
|
|
4565
|
-
*
|
|
4566
|
-
* Reading from signals creates reactive dependencies, so computeds will
|
|
4567
|
-
* re-evaluate when form values change.
|
|
4568
|
-
*/
|
|
4569
|
-
getFormValue(formId?: string): Record<string, unknown>;
|
|
4570
|
-
/**
|
|
4571
|
-
* Registers form context (field paths and metadata) for a specific form.
|
|
4572
|
-
* This can be used to store additional context information.
|
|
4573
|
-
*/
|
|
4574
|
-
registerFormContext(context: Record<string, unknown>, formId?: string): void;
|
|
4575
|
-
/**
|
|
4576
|
-
* Gets form context by id.
|
|
4577
|
-
*/
|
|
4578
|
-
getFormContext(formId?: string): Record<string, unknown>;
|
|
4579
|
-
/**
|
|
4580
|
-
* Unregisters a form and its context.
|
|
4581
|
-
* This should be called when a form component is destroyed to prevent memory leaks.
|
|
4582
|
-
*/
|
|
4583
|
-
unregisterForm(formId?: string): void;
|
|
4584
|
-
/**
|
|
4585
|
-
* Gets all registered form IDs.
|
|
4586
|
-
*/
|
|
4587
|
-
getRegisteredFormIds(): string[];
|
|
4588
|
-
/**
|
|
4589
|
-
* Clears all registered forms. Useful for testing.
|
|
4590
|
-
*/
|
|
4591
|
-
clear(): void;
|
|
4592
|
-
static ɵfac: _angular_core.ɵɵFactoryDeclaration<RootFormRegistryService, never>;
|
|
4593
|
-
static ɵprov: _angular_core.ɵɵInjectableDeclaration<RootFormRegistryService>;
|
|
6189
|
+
readonly formValue: Signal<Record<string, unknown>>;
|
|
6190
|
+
readonly rootForm: Signal<FieldTree<Record<string, unknown>> | undefined>;
|
|
6191
|
+
constructor(formValue: Signal<Record<string, unknown>>, rootForm: Signal<FieldTree<Record<string, unknown>> | undefined>);
|
|
4594
6192
|
}
|
|
4595
6193
|
|
|
4596
|
-
export { ARRAY_CONTEXT,
|
|
4597
|
-
export type { ArrayAllowedChildren, ArrayContext, ArrayField, ArrayFieldTree, ArrayItemContext, AsyncCustomValidator, AsyncValidatorConfig, AvailableFieldTypes, BaseCheckedField, BaseValidatorConfig, BaseValueField, BuiltInValidatorConfig, ButtonLogicContext, CheckedFieldComponent, ConditionalExpression, ContainerFieldTypes, CustomFnConfig, CustomValidator, CustomValidatorConfig, DynamicFormFieldRegistry, DynamicText, EvaluationContext, ExtractField, ExtractFieldDefs, ExtractFormValue, FieldComponent, FieldDef, FieldMeta, FieldOption, FieldPathAccess, FieldRegistryContainers, FieldRegistryLeaves, FieldSignalContext, FieldTypeDefinition, FieldWithValidation, FormConfig, FormEvent, FormEventConstructor, FormMode, FormModeDetectionResult, FormOptions, FormStateCondition, GroupAllowedChildren, GroupField, HttpCustomValidator, HttpResourceRequest, HttpValidatorConfig, InferFormValue$1 as InferFormValue, LeafFieldTypes, Logger, LogicConfig, MapperFn, NarrowField, NarrowFields, NextButtonOptions, PageAllowedChildren, PageField, Prettify, RegisteredFieldTypes, RowAllowedChildren, RowField, SchemaApplicationConfig, SchemaDefinition, SubmissionActionResult, SubmissionConfig, SubmitButtonOptions, TextElementType, TextField, TextProps, TokenContext, ValidationError, ValidationMessages, ValidatorConfig, ValueFieldComponent, ValueHandlingMode, ValueType, WithInputSignals };
|
|
6194
|
+
export { ARRAY_CONTEXT, AppendArrayItemEvent, ArrayFieldComponent, BUILT_IN_FIELDS, ConsoleLogger, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, DynamicForm, DynamicFormError, DynamicFormLogger, DynamicTextPipe, EventBus, FIELD_REGISTRY, FIELD_SIGNAL_CONTEXT, FORM_OPTIONS, FormClearEvent, FormResetEvent, GroupFieldComponent, InsertArrayItemEvent, NextPageEvent, NoopLogger, PageChangeEvent, PopArrayItemEvent, PrependArrayItemEvent, PreviousPageEvent, RemoveAtIndexEvent, RootFormRegistryService, RowFieldComponent, ShiftArrayItemEvent, SubmitEvent, applyMetaToElement, applyValidator, applyValidators, arrayEvent, arrayFieldMapper, baseFieldMapper, buildBaseInputs, createField, dynamicTextToObservable, evaluateNonFieldDisabled, evaluateNonFieldHidden, field, formConfig, getArrayLength, groupFieldMapper, hasFormValue, interpolateParams, isArrayField, isCheckedField, isContainerField, isDisplayOnlyField, isEqual, isFormStateCondition, isGroupField, isLeafField, isPageField, isPropertyDerivationLogicConfig, isRowField, isSimplifiedArrayField, isValueBearingField, isValueField, omit, pageFieldMapper, provideDynamicForm, resolveNextButtonDisabled, resolveNonFieldDisabled, resolveNonFieldHidden, resolveSubmitButtonDisabled, resolveTokens, rowFieldMapper, textFieldMapper, withEventFormValue, withLoggerConfig, withValueExclusionDefaults };
|
|
6195
|
+
export type { ArrayAllowedChildren, ArrayButtonConfig, ArrayContext, ArrayField, ArrayFieldTree, ArrayItemContext, ArrayItemDefinitionTemplate, ArrayItemTemplate, AsyncCondition, AsyncConditionFunction, AsyncCustomValidator, AsyncDerivationFunction, AsyncValidatorConfig, AvailableFieldTypes, BaseCheckedField, BaseValidatorConfig, BaseValueField, BuiltInValidatorConfig, ButtonLogicContext, CheckedFieldComponent, ConditionalExpression, ContainerFieldTypes, CustomFnConfig, CustomValidator, CustomValidatorConfig, DeclarativeHttpValidatorConfig, DynamicFormFieldRegistry, DynamicText, EvaluationContext, ExtractField, ExtractFieldDefs, ExtractFormValue, FieldComponent, FieldDef, FieldMeta, FieldOption, FieldPathAccess, FieldRegistryContainers, FieldRegistryLeaves, FieldSignalContext, FieldStateContext, FieldStateInfo, FieldTypeDefinition, FieldWithValidation, FormConfig, FormEvent, FormEventConstructor, FormFieldStateMap, FormMode, FormModeDetectionResult, FormOptions, FormStateCondition, GroupAllowedChildren, GroupField, HttpCondition, HttpCustomValidator, HttpRequestConfig, HttpResourceRequest, HttpValidationResponseMapping, HttpValidatorConfig, InferFormValue$1 as InferFormValue, LeafFieldTypes, Logger, LogicConfig, MapperFn, NarrowField, NarrowFields, NextButtonOptions, NonFieldLogicConfig, NonFieldLogicContext, NonFieldLogicType, PageAllowedChildren, PageField, Prettify, PropertyDerivationLogicConfig, RegisteredFieldTypes, ResolvedValueExclusionConfig, RowAllowedChildren, RowField, SchemaApplicationConfig, SchemaDefinition, SimplifiedArrayField, StateLogicConfig, SubmissionActionResult, SubmissionConfig, SubmitButtonOptions, TextElementType, TextField, TextProps, TokenContext, ValidationError, ValidationMessages, ValidatorConfig, ValueExclusionConfig, ValueFieldComponent, ValueHandlingMode, ValueType, WithInputSignals };
|