@angular/forms 21.2.0-next.0 → 21.2.0-next.2

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.
@@ -1,112 +1,15 @@
1
1
  /**
2
- * @license Angular v21.2.0-next.0
2
+ * @license Angular v21.2.0-next.2
3
3
  * (c) 2010-2026 Google LLC. https://angular.dev/
4
4
  * License: MIT
5
5
  */
6
6
 
7
7
  import * as i0 from '@angular/core';
8
- import { ɵFormFieldBindingOptions as _FormFieldBindingOptions, InjectionToken, Injector, ɵCONTROL as _CONTROL, ɵɵcontrolCreate as __controlCreate, ɵcontrolUpdate as _controlUpdate, Signal, ɵFieldState as _FieldState, Provider, WritableSignal, DestroyableInjector } from '@angular/core';
8
+ import { Signal, WritableSignal, InjectionToken, Injector, Provider } from '@angular/core';
9
9
  import * as _angular_forms from '@angular/forms';
10
- import { NgControl, AbstractControl, ValidationErrors, FormControlStatus, ControlValueAccessor, ValidatorFn } from '@angular/forms';
10
+ import { AbstractControl, ValidationErrors, FormControlStatus, ControlValueAccessor, ValidatorFn } from '@angular/forms';
11
11
  import { StandardSchemaV1 } from '@standard-schema/spec';
12
12
 
13
- /**
14
- * Properties of both NgControl & AbstractControl that are supported by the InteropNgControl.
15
- */
16
- type InteropSharedKeys = 'value' | 'valid' | 'invalid' | 'touched' | 'untouched' | 'disabled' | 'enabled' | 'errors' | 'pristine' | 'dirty' | 'status';
17
- /**
18
- * A fake version of `NgControl` provided by the `Field` directive. This allows interoperability
19
- * with a wider range of components designed to work with reactive forms, in particular ones that
20
- * inject the `NgControl`. The interop control does not implement *all* properties and methods of
21
- * the real `NgControl`, but does implement some of the most commonly used ones that have a clear
22
- * equivalent in signal forms.
23
- */
24
- declare class InteropNgControl implements Pick<NgControl, InteropSharedKeys | 'control' | 'valueAccessor'>, Pick<AbstractControl<unknown>, InteropSharedKeys | 'hasValidator'> {
25
- protected field: () => FieldState<unknown>;
26
- constructor(field: () => FieldState<unknown>);
27
- readonly control: AbstractControl<any, any>;
28
- get value(): any;
29
- get valid(): boolean;
30
- get invalid(): boolean;
31
- get pending(): boolean | null;
32
- get disabled(): boolean;
33
- get enabled(): boolean;
34
- get errors(): ValidationErrors | null;
35
- get pristine(): boolean;
36
- get dirty(): boolean;
37
- get touched(): boolean;
38
- get untouched(): boolean;
39
- get status(): FormControlStatus;
40
- valueAccessor: ControlValueAccessor | null;
41
- hasValidator(validator: ValidatorFn): boolean;
42
- updateValueAndValidity(): void;
43
- }
44
-
45
- interface FormFieldBindingOptions extends _FormFieldBindingOptions {
46
- /**
47
- * Focuses the binding.
48
- *
49
- * If not specified, Signal Forms will attempt to focus the host element of the `FormField` when
50
- * asked to focus this binding.
51
- */
52
- focus?(options?: FocusOptions): void;
53
- }
54
- /**
55
- * Lightweight DI token provided by the {@link FormField} directive.
56
- *
57
- * @category control
58
- * @experimental 21.0.0
59
- */
60
- declare const FORM_FIELD: InjectionToken<FormField<unknown>>;
61
- /**
62
- * Binds a form `FieldTree` to a UI control that edits it. A UI control can be one of several things:
63
- * 1. A native HTML input or textarea
64
- * 2. A signal forms custom control that implements `FormValueControl` or `FormCheckboxControl`
65
- * 3. A component that provides a `ControlValueAccessor`. This should only be used for backwards
66
- * compatibility with reactive forms. Prefer options (1) and (2).
67
- *
68
- * This directive has several responsibilities:
69
- * 1. Two-way binds the field state's value with the UI control's value
70
- * 2. Binds additional forms related state on the field state to the UI control (disabled, required, etc.)
71
- * 3. Relays relevant events on the control to the field state (e.g. marks touched on blur)
72
- * 4. Provides a fake `NgControl` that implements a subset of the features available on the
73
- * reactive forms `NgControl`. This is provided to improve interoperability with controls
74
- * designed to work with reactive forms. It should not be used by controls written for signal
75
- * forms.
76
- *
77
- * @category control
78
- * @experimental 21.0.0
79
- */
80
- declare class FormField<T> {
81
- readonly element: HTMLElement;
82
- readonly injector: Injector;
83
- readonly formField: i0.InputSignal<FieldTree<T>>;
84
- readonly state: i0.Signal<[T] extends [_angular_forms.AbstractControl<any, any, any>] ? CompatFieldState<T, string | number> : FieldState<T, string | number>>;
85
- private readonly bindingOptions;
86
- readonly [_CONTROL]: {
87
- readonly create: typeof __controlCreate;
88
- readonly update: typeof _controlUpdate;
89
- };
90
- private config;
91
- /** Any `ControlValueAccessor` instances provided on the host element. */
92
- private readonly controlValueAccessors;
93
- /** A lazily instantiated fake `NgControl`. */
94
- private interopNgControl;
95
- /** Lazily instantiates a fake `NgControl` for this form field. */
96
- protected getOrCreateNgControl(): InteropNgControl;
97
- /**
98
- * Registers this `FormField` as a binding on its associated `FieldState`.
99
- *
100
- * This method should be called at most once for a given `FormField`. A `FormField` placed on a
101
- * custom control (`FormUiControl`) automatically registers that custom control as a binding.
102
- */
103
- registerAsBinding(bindingOptions?: FormFieldBindingOptions): void;
104
- /** Focuses this UI control. */
105
- focus(options?: FocusOptions): void;
106
- static ɵfac: i0.ɵɵFactoryDeclaration<FormField<any>, never>;
107
- static ɵdir: i0.ɵɵDirectiveDeclaration<FormField<any>, "[formField]", never, { "formField": { "alias": "formField"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
108
- }
109
-
110
13
  /**
111
14
  * Sets a value for the {@link MetadataKey} for this field.
112
15
  *
@@ -273,6 +176,70 @@ declare const MAX_LENGTH: MetadataKey<Signal<number | undefined>, number | undef
273
176
  */
274
177
  declare const PATTERN: MetadataKey<Signal<RegExp[]>, RegExp | undefined, RegExp[]>;
275
178
 
179
+ /**
180
+ * Utility type that removes a string index key when its value is `unknown`,
181
+ * i.e. `{[key: string]: unknown}`. It allows specific string keys to pass through, even if their
182
+ * value is `unknown`, e.g. `{key: unknown}`.
183
+ *
184
+ * @experimental 21.0.0
185
+ */
186
+ type RemoveStringIndexUnknownKey<K, V> = string extends K ? unknown extends V ? never : K : K;
187
+ /**
188
+ * Utility type that recursively ignores unknown string index properties on the given object.
189
+ * We use this on the `TSchema` type in `validateStandardSchema` in order to accommodate Zod's
190
+ * `looseObject` which includes `{[key: string]: unknown}` as part of the type.
191
+ *
192
+ * @experimental 21.0.0
193
+ */
194
+ type IgnoreUnknownProperties<T> = T extends Record<PropertyKey, unknown> ? {
195
+ [K in keyof T as RemoveStringIndexUnknownKey<K, T[K]>]: IgnoreUnknownProperties<T[K]>;
196
+ } : T;
197
+ /**
198
+ * Validates a field using a `StandardSchemaV1` compatible validator (e.g. a Zod validator).
199
+ *
200
+ * See https://github.com/standard-schema/standard-schema for more about standard schema.
201
+ *
202
+ * @param path The `FieldPath` to the field to validate.
203
+ * @param schema The standard schema compatible validator to use for validation.
204
+ * @template TSchema The type validated by the schema. This may be either the full `TValue` type,
205
+ * or a partial of it.
206
+ * @template TValue The type of value stored in the field being validated.
207
+ *
208
+ * @see [Signal Form Schema Validation](guide/forms/signals/validation#integration-with-schema-validation-libraries)
209
+ * @category validation
210
+ * @experimental 21.0.0
211
+ */
212
+ declare function validateStandardSchema<TSchema, TModel extends IgnoreUnknownProperties<TSchema>>(path: SchemaPath<TModel> & SchemaPathTree<TModel>, schema: StandardSchemaV1<TSchema>): void;
213
+ /**
214
+ * Create a standard schema issue error associated with the target field
215
+ * @param issue The standard schema issue
216
+ * @param options The validation error options
217
+ *
218
+ * @category validation
219
+ * @experimental 21.0.0
220
+ */
221
+ declare function standardSchemaError(issue: StandardSchemaV1.Issue, options: WithFieldTree<ValidationErrorOptions>): StandardSchemaValidationError;
222
+ /**
223
+ * Create a standard schema issue error
224
+ * @param issue The standard schema issue
225
+ * @param options The optional validation error options
226
+ *
227
+ * @category validation
228
+ * @experimental 21.0.0
229
+ */
230
+ declare function standardSchemaError(issue: StandardSchemaV1.Issue, options?: ValidationErrorOptions): WithoutFieldTree<StandardSchemaValidationError>;
231
+ /**
232
+ * An error used to indicate an issue validating against a standard schema.
233
+ *
234
+ * @category validation
235
+ * @experimental 21.0.0
236
+ */
237
+ declare class StandardSchemaValidationError extends BaseNgValidationError {
238
+ readonly issue: StandardSchemaV1.Issue;
239
+ readonly kind = "standardSchema";
240
+ constructor(issue: StandardSchemaV1.Issue, options?: ValidationErrorOptions);
241
+ }
242
+
276
243
  /**
277
244
  * Symbol used to retain generic type information when it would otherwise be lost.
278
245
  */
@@ -348,7 +315,7 @@ type ValidationSuccess = null | undefined | void;
348
315
  * @category types
349
316
  * @experimental 21.0.0
350
317
  */
351
- type TreeValidationResult<E extends ValidationError.WithOptionalField = ValidationError.WithOptionalField> = ValidationSuccess | OneOrMany<E>;
318
+ type TreeValidationResult<E extends ValidationError.WithOptionalFieldTree = ValidationError.WithOptionalFieldTree> = ValidationSuccess | OneOrMany<E>;
352
319
  /**
353
320
  * A validation result where all errors explicitly define their target field.
354
321
  *
@@ -430,7 +397,64 @@ type MaybeFieldTree<TModel, TKey extends string | number = string | number> = (T
430
397
  * @category structure
431
398
  * @experimental 21.0.0
432
399
  */
433
- interface FieldState<TValue, TKey extends string | number = string | number> extends _FieldState<TValue> {
400
+ interface FieldState<TValue, TKey extends string | number = string | number> {
401
+ /**
402
+ * A writable signal containing the value for this field.
403
+ *
404
+ * Updating this signal will update the data model that the field is bound to.
405
+ *
406
+ * While updates from the UI control are eventually reflected here, they may be delayed if
407
+ * debounced.
408
+ */
409
+ readonly value: WritableSignal<TValue>;
410
+ /**
411
+ * A signal indicating whether the field is currently disabled.
412
+ */
413
+ readonly disabled: Signal<boolean>;
414
+ /**
415
+ * A signal indicating the field's maximum value, if applicable.
416
+ *
417
+ * Applies to `<input>` with a numeric or date `type` attribute and custom controls.
418
+ */
419
+ readonly max?: Signal<number | undefined>;
420
+ /**
421
+ * A signal indicating the field's maximum string length, if applicable.
422
+ *
423
+ * Applies to `<input>`, `<textarea>`, and custom controls.
424
+ */
425
+ readonly maxLength?: Signal<number | undefined>;
426
+ /**
427
+ * A signal indicating the field's minimum value, if applicable.
428
+ *
429
+ * Applies to `<input>` with a numeric or date `type` attribute and custom controls.
430
+ */
431
+ readonly min?: Signal<number | undefined>;
432
+ /**
433
+ * A signal indicating the field's minimum string length, if applicable.
434
+ *
435
+ * Applies to `<input>`, `<textarea>`, and custom controls.
436
+ */
437
+ readonly minLength?: Signal<number | undefined>;
438
+ /**
439
+ * A signal of a unique name for the field, by default based on the name of its parent field.
440
+ */
441
+ readonly name: Signal<string>;
442
+ /**
443
+ * A signal indicating the patterns the field must match.
444
+ */
445
+ readonly pattern: Signal<readonly RegExp[]>;
446
+ /**
447
+ * A signal indicating whether the field is currently readonly.
448
+ */
449
+ readonly readonly: Signal<boolean>;
450
+ /**
451
+ * A signal indicating whether the field is required.
452
+ */
453
+ readonly required: Signal<boolean>;
454
+ /**
455
+ * A signal indicating whether the field has been touched by the user.
456
+ */
457
+ readonly touched: Signal<boolean>;
434
458
  /**
435
459
  * A signal indicating whether field value has been changed by user.
436
460
  */
@@ -449,11 +473,11 @@ interface FieldState<TValue, TKey extends string | number = string | number> ext
449
473
  */
450
474
  readonly hidden: Signal<boolean>;
451
475
  readonly disabledReasons: Signal<readonly DisabledReason[]>;
452
- readonly errors: Signal<ValidationError.WithField[]>;
476
+ readonly errors: Signal<ValidationError.WithFieldTree[]>;
453
477
  /**
454
478
  * A signal containing the {@link errors} of the field and its descendants.
455
479
  */
456
- readonly errorSummary: Signal<ValidationError.WithField[]>;
480
+ readonly errorSummary: Signal<ValidationError.WithFieldTree[]>;
457
481
  /**
458
482
  * A signal indicating whether the field's value is currently valid.
459
483
  *
@@ -495,6 +519,22 @@ interface FieldState<TValue, TKey extends string | number = string | number> ext
495
519
  * The {@link FormField} directives that bind this field to a UI control.
496
520
  */
497
521
  readonly formFieldBindings: Signal<readonly FormField<unknown>[]>;
522
+ /**
523
+ * A signal containing the value of the control to which this field is bound.
524
+ *
525
+ * This differs from {@link value} in that it's not subject to debouncing, and thus is used to
526
+ * buffer debounced updates from the control to the field. This will also not take into account
527
+ * the {@link controlValue} of children.
528
+ */
529
+ readonly controlValue: WritableSignal<TValue>;
530
+ /**
531
+ * Sets the dirty status of the field to `true`.
532
+ */
533
+ markAsDirty(): void;
534
+ /**
535
+ * Sets the touched status of the field to `true`.
536
+ */
537
+ markAsTouched(): void;
498
538
  /**
499
539
  * Reads a metadata value from the field.
500
540
  * @param key The metadata key to read.
@@ -691,7 +731,7 @@ type LogicFn<TValue, TReturn, TPathKind extends PathKind = PathKind.Root> = (ctx
691
731
  * @category validation
692
732
  * @experimental 21.0.0
693
733
  */
694
- type FieldValidator<TValue, TPathKind extends PathKind = PathKind.Root> = LogicFn<TValue, ValidationResult<ValidationError.WithoutField>, TPathKind>;
734
+ type FieldValidator<TValue, TPathKind extends PathKind = PathKind.Root> = LogicFn<TValue, ValidationResult<ValidationError.WithoutFieldTree>, TPathKind>;
695
735
  /**
696
736
  * A function that takes the `FieldContext` for the field being validated and returns a
697
737
  * `TreeValidationResult` indicating errors for the field and its sub-fields.
@@ -784,63 +824,224 @@ type ItemType<T extends Object> = T extends ReadonlyArray<any> ? T[number] : T[k
784
824
  type Debouncer<TValue, TPathKind extends PathKind = PathKind.Root> = (context: FieldContext<TValue, TPathKind>, abortSignal: AbortSignal) => Promise<void> | void;
785
825
 
786
826
  /**
787
- * Options used to create a `ValidationError`.
788
- */
789
- interface ValidationErrorOptions {
790
- /** Human readable error message. */
791
- message?: string;
827
+ * Represents a combination of `NgControl` and `AbstractControl`.
828
+ *
829
+ * Note: We have this separate interface, rather than implementing the relevant parts of the two
830
+ * controls with something like `InteropNgControl implements Pick<NgControl, ...>, Pick<AbstractControl, ...>`
831
+ * because it confuses the internal JS minifier which can cause collisions in field names.
832
+ */
833
+ interface CombinedControl {
834
+ value: any;
835
+ valid: boolean;
836
+ invalid: boolean;
837
+ touched: boolean;
838
+ untouched: boolean;
839
+ disabled: boolean;
840
+ enabled: boolean;
841
+ errors: ValidationErrors | null;
842
+ pristine: boolean;
843
+ dirty: boolean;
844
+ status: FormControlStatus;
845
+ control: AbstractControl<any, any>;
846
+ valueAccessor: ControlValueAccessor | null;
847
+ hasValidator(validator: ValidatorFn): boolean;
848
+ updateValueAndValidity(): void;
792
849
  }
793
850
  /**
794
- * A type that requires the given type `T` to have a `field` property.
795
- * @template T The type to add a `field` to.
796
- *
797
- * @experimental 21.0.0
798
- */
799
- type WithField<T> = T & {
800
- fieldTree: FieldTree<unknown>;
801
- };
802
- /**
803
- * A type that allows the given type `T` to optionally have a `field` property.
804
- * @template T The type to optionally add a `field` to.
805
- *
806
- * @experimental 21.0.0
851
+ * A fake version of `NgControl` provided by the `Field` directive. This allows interoperability
852
+ * with a wider range of components designed to work with reactive forms, in particular ones that
853
+ * inject the `NgControl`. The interop control does not implement *all* properties and methods of
854
+ * the real `NgControl`, but does implement some of the most commonly used ones that have a clear
855
+ * equivalent in signal forms.
807
856
  */
808
- type WithOptionalField<T> = Omit<T, 'fieldTree'> & {
809
- fieldTree?: FieldTree<unknown>;
810
- };
857
+ declare class InteropNgControl implements CombinedControl {
858
+ protected field: () => FieldState<unknown>;
859
+ constructor(field: () => FieldState<unknown>);
860
+ readonly control: AbstractControl<any, any>;
861
+ get value(): any;
862
+ get valid(): boolean;
863
+ get invalid(): boolean;
864
+ get pending(): boolean | null;
865
+ get disabled(): boolean;
866
+ get enabled(): boolean;
867
+ get errors(): ValidationErrors | null;
868
+ get pristine(): boolean;
869
+ get dirty(): boolean;
870
+ get touched(): boolean;
871
+ get untouched(): boolean;
872
+ get status(): FormControlStatus;
873
+ valueAccessor: ControlValueAccessor | null;
874
+ hasValidator(validator: ValidatorFn): boolean;
875
+ updateValueAndValidity(): void;
876
+ }
877
+
878
+ declare const ɵNgFieldDirective: unique symbol;
879
+ interface FormFieldBindingOptions {
880
+ /**
881
+ * Focuses the binding.
882
+ *
883
+ * If not specified, Signal Forms will attempt to focus the host element of the `FormField` when
884
+ * asked to focus this binding.
885
+ */
886
+ readonly focus?: (focusOptions?: FocusOptions) => void;
887
+ /**
888
+ * Source of parse errors for this binding.
889
+ */
890
+ readonly parseErrors?: Signal<ValidationError.WithoutFieldTree[]>;
891
+ }
811
892
  /**
812
- * A type that ensures the given type `T` does not have a `field` property.
813
- * @template T The type to remove the `field` from.
893
+ * Lightweight DI token provided by the {@link FormField} directive.
814
894
  *
895
+ * @category control
815
896
  * @experimental 21.0.0
816
897
  */
817
- type WithoutField<T> = T & {
818
- fieldTree: never;
819
- };
898
+ declare const FORM_FIELD: InjectionToken<FormField<unknown>>;
820
899
  /**
821
- * Create a required error associated with the target field
822
- * @param options The validation error options
900
+ * Binds a form `FieldTree` to a UI control that edits it. A UI control can be one of several things:
901
+ * 1. A native HTML input or textarea
902
+ * 2. A signal forms custom control that implements `FormValueControl` or `FormCheckboxControl`
903
+ * 3. A component that provides a `ControlValueAccessor`. This should only be used for backwards
904
+ * compatibility with reactive forms. Prefer options (1) and (2).
823
905
  *
824
- * @experimental 21.0.0
825
- */
826
- declare function requiredError(options: WithField<ValidationErrorOptions>): RequiredValidationError;
827
- /**
828
- * Create a required error
829
- * @param options The optional validation error options
906
+ * This directive has several responsibilities:
907
+ * 1. Two-way binds the field state's value with the UI control's value
908
+ * 2. Binds additional forms related state on the field state to the UI control (disabled, required, etc.)
909
+ * 3. Relays relevant events on the control to the field state (e.g. marks touched on blur)
910
+ * 4. Provides a fake `NgControl` that implements a subset of the features available on the
911
+ * reactive forms `NgControl`. This is provided to improve interoperability with controls
912
+ * designed to work with reactive forms. It should not be used by controls written for signal
913
+ * forms.
830
914
  *
831
- * @category validation
915
+ * @category control
832
916
  * @experimental 21.0.0
833
917
  */
834
- declare function requiredError(options?: ValidationErrorOptions): WithoutField<RequiredValidationError>;
835
- /**
836
- * Create a min value error associated with the target field
837
- * @param min The min value constraint
838
- * @param options The validation error options
839
- *
840
- * @category validation
841
- * @experimental 21.0.0
918
+ declare class FormField<T> {
919
+ readonly fieldTree: i0.InputSignal<FieldTree<T>>;
920
+ /**
921
+ * `FieldState` for the currently bound field.
922
+ */
923
+ readonly state: Signal<[T] extends [_angular_forms.AbstractControl<any, any, any>] ? CompatFieldState<T, string | number> : FieldState<T, string | number>>;
924
+ /**
925
+ * The node injector for the element this field binding.
926
+ */
927
+ readonly injector: Injector;
928
+ /**
929
+ * The DOM element hosting this field binding.
930
+ */
931
+ readonly element: HTMLElement;
932
+ private readonly elementIsNativeFormElement;
933
+ private readonly elementAcceptsNumericValues;
934
+ private readonly elementAcceptsTextualValues;
935
+ /**
936
+ * Current focus implementation, set by `registerAsBinding`.
937
+ */
938
+ private focuser;
939
+ /** Any `ControlValueAccessor` instances provided on the host element. */
940
+ private readonly controlValueAccessors;
941
+ private readonly config;
942
+ private readonly parseErrorsSource;
943
+ /** A lazily instantiated fake `NgControl`. */
944
+ private _interopNgControl;
945
+ /** Lazily instantiates a fake `NgControl` for this form field. */
946
+ protected get interopNgControl(): InteropNgControl;
947
+ /** Errors associated with this form field. */
948
+ readonly errors: Signal<ValidationError.WithFieldTree[]>;
949
+ /** Whether this `FormField` has been registered as a binding on its associated `FieldState`. */
950
+ private isFieldBinding;
951
+ /**
952
+ * Creates an `afterRenderEffect` that applies the configured class bindings to the host element
953
+ * if needed.
954
+ */
955
+ private installClassBindingEffect;
956
+ /**
957
+ * Focuses this field binding.
958
+ *
959
+ * By default, this will focus the host DOM element. However, custom `FormUiControl`s can
960
+ * implement custom focusing behavior.
961
+ */
962
+ focus(options?: FocusOptions): void;
963
+ /**
964
+ * Registers this `FormField` as a binding on its associated `FieldState`.
965
+ *
966
+ * This method should be called at most once for a given `FormField`. A `FormField` placed on a
967
+ * custom control (`FormUiControl`) automatically registers that custom control as a binding.
968
+ */
969
+ registerAsBinding(bindingOptions?: FormFieldBindingOptions): void;
970
+ /**
971
+ * The presence of this symbol tells the template type-checker that this directive is a control
972
+ * directive and should be type-checked as such. We don't use the `ɵngControlCreate` method below
973
+ * as it's marked internal and removed from the public API. A symbol is used instead to avoid
974
+ * polluting the public API with the marker.
975
+ */
976
+ readonly [ɵNgFieldDirective]: true;
977
+ static ɵfac: i0.ɵɵFactoryDeclaration<FormField<any>, never>;
978
+ static ɵdir: i0.ɵɵDirectiveDeclaration<FormField<any>, "[formField]", ["formField"], { "fieldTree": { "alias": "formField"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
979
+ }
980
+
981
+ /**
982
+ * Options used to create a `ValidationError`.
983
+ */
984
+ interface ValidationErrorOptions {
985
+ /** Human readable error message. */
986
+ message?: string;
987
+ }
988
+ /**
989
+ * A type that requires the given type `T` to have a `field` property.
990
+ * @template T The type to add a `field` to.
991
+ *
992
+ * @experimental 21.0.0
993
+ */
994
+ type WithFieldTree<T> = T & {
995
+ fieldTree: FieldTree<unknown>;
996
+ };
997
+ /** @deprecated Use `WithFieldTree` instead */
998
+ type WithField<T> = WithFieldTree<T>;
999
+ /**
1000
+ * A type that allows the given type `T` to optionally have a `field` property.
1001
+ * @template T The type to optionally add a `field` to.
1002
+ *
1003
+ * @experimental 21.0.0
1004
+ */
1005
+ type WithOptionalFieldTree<T> = Omit<T, 'fieldTree'> & {
1006
+ fieldTree?: FieldTree<unknown>;
1007
+ };
1008
+ /** @deprecated Use `WithOptionalFieldTree` instead */
1009
+ type WithOptionalField<T> = WithOptionalFieldTree<T>;
1010
+ /**
1011
+ * A type that ensures the given type `T` does not have a `field` property.
1012
+ * @template T The type to remove the `field` from.
1013
+ *
1014
+ * @experimental 21.0.0
1015
+ */
1016
+ type WithoutFieldTree<T> = T & {
1017
+ fieldTree: never;
1018
+ };
1019
+ /** @deprecated Use `WithoutFieldTree` instead */
1020
+ type WithoutField<T> = WithoutFieldTree<T>;
1021
+ /**
1022
+ * Create a required error associated with the target field
1023
+ * @param options The validation error options
1024
+ *
1025
+ * @experimental 21.0.0
1026
+ */
1027
+ declare function requiredError(options: WithFieldTree<ValidationErrorOptions>): RequiredValidationError;
1028
+ /**
1029
+ * Create a required error
1030
+ * @param options The optional validation error options
1031
+ *
1032
+ * @category validation
1033
+ * @experimental 21.0.0
1034
+ */
1035
+ declare function requiredError(options?: ValidationErrorOptions): WithoutFieldTree<RequiredValidationError>;
1036
+ /**
1037
+ * Create a min value error associated with the target field
1038
+ * @param min The min value constraint
1039
+ * @param options The validation error options
1040
+ *
1041
+ * @category validation
1042
+ * @experimental 21.0.0
842
1043
  */
843
- declare function minError(min: number, options: WithField<ValidationErrorOptions>): MinValidationError;
1044
+ declare function minError(min: number, options: WithFieldTree<ValidationErrorOptions>): MinValidationError;
844
1045
  /**
845
1046
  * Create a min value error
846
1047
  * @param min The min value constraint
@@ -849,7 +1050,7 @@ declare function minError(min: number, options: WithField<ValidationErrorOptions
849
1050
  * @category validation
850
1051
  * @experimental 21.0.0
851
1052
  */
852
- declare function minError(min: number, options?: ValidationErrorOptions): WithoutField<MinValidationError>;
1053
+ declare function minError(min: number, options?: ValidationErrorOptions): WithoutFieldTree<MinValidationError>;
853
1054
  /**
854
1055
  * Create a max value error associated with the target field
855
1056
  * @param max The max value constraint
@@ -858,7 +1059,7 @@ declare function minError(min: number, options?: ValidationErrorOptions): Withou
858
1059
  * @category validation
859
1060
  * @experimental 21.0.0
860
1061
  */
861
- declare function maxError(max: number, options: WithField<ValidationErrorOptions>): MaxValidationError;
1062
+ declare function maxError(max: number, options: WithFieldTree<ValidationErrorOptions>): MaxValidationError;
862
1063
  /**
863
1064
  * Create a max value error
864
1065
  * @param max The max value constraint
@@ -867,7 +1068,7 @@ declare function maxError(max: number, options: WithField<ValidationErrorOptions
867
1068
  * @category validation
868
1069
  * @experimental 21.0.0
869
1070
  */
870
- declare function maxError(max: number, options?: ValidationErrorOptions): WithoutField<MaxValidationError>;
1071
+ declare function maxError(max: number, options?: ValidationErrorOptions): WithoutFieldTree<MaxValidationError>;
871
1072
  /**
872
1073
  * Create a minLength error associated with the target field
873
1074
  * @param minLength The minLength constraint
@@ -876,7 +1077,7 @@ declare function maxError(max: number, options?: ValidationErrorOptions): Withou
876
1077
  * @category validation
877
1078
  * @experimental 21.0.0
878
1079
  */
879
- declare function minLengthError(minLength: number, options: WithField<ValidationErrorOptions>): MinLengthValidationError;
1080
+ declare function minLengthError(minLength: number, options: WithFieldTree<ValidationErrorOptions>): MinLengthValidationError;
880
1081
  /**
881
1082
  * Create a minLength error
882
1083
  * @param minLength The minLength constraint
@@ -885,7 +1086,7 @@ declare function minLengthError(minLength: number, options: WithField<Validation
885
1086
  * @category validation
886
1087
  * @experimental 21.0.0
887
1088
  */
888
- declare function minLengthError(minLength: number, options?: ValidationErrorOptions): WithoutField<MinLengthValidationError>;
1089
+ declare function minLengthError(minLength: number, options?: ValidationErrorOptions): WithoutFieldTree<MinLengthValidationError>;
889
1090
  /**
890
1091
  * Create a maxLength error associated with the target field
891
1092
  * @param maxLength The maxLength constraint
@@ -894,7 +1095,7 @@ declare function minLengthError(minLength: number, options?: ValidationErrorOpti
894
1095
  * @category validation
895
1096
  * @experimental 21.0.0
896
1097
  */
897
- declare function maxLengthError(maxLength: number, options: WithField<ValidationErrorOptions>): MaxLengthValidationError;
1098
+ declare function maxLengthError(maxLength: number, options: WithFieldTree<ValidationErrorOptions>): MaxLengthValidationError;
898
1099
  /**
899
1100
  * Create a maxLength error
900
1101
  * @param maxLength The maxLength constraint
@@ -903,7 +1104,7 @@ declare function maxLengthError(maxLength: number, options: WithField<Validation
903
1104
  * @category validation
904
1105
  * @experimental 21.0.0
905
1106
  */
906
- declare function maxLengthError(maxLength: number, options?: ValidationErrorOptions): WithoutField<MaxLengthValidationError>;
1107
+ declare function maxLengthError(maxLength: number, options?: ValidationErrorOptions): WithoutFieldTree<MaxLengthValidationError>;
907
1108
  /**
908
1109
  * Create a pattern matching error associated with the target field
909
1110
  * @param pattern The violated pattern
@@ -912,7 +1113,7 @@ declare function maxLengthError(maxLength: number, options?: ValidationErrorOpti
912
1113
  * @category validation
913
1114
  * @experimental 21.0.0
914
1115
  */
915
- declare function patternError(pattern: RegExp, options: WithField<ValidationErrorOptions>): PatternValidationError;
1116
+ declare function patternError(pattern: RegExp, options: WithFieldTree<ValidationErrorOptions>): PatternValidationError;
916
1117
  /**
917
1118
  * Create a pattern matching error
918
1119
  * @param pattern The violated pattern
@@ -921,7 +1122,7 @@ declare function patternError(pattern: RegExp, options: WithField<ValidationErro
921
1122
  * @category validation
922
1123
  * @experimental 21.0.0
923
1124
  */
924
- declare function patternError(pattern: RegExp, options?: ValidationErrorOptions): WithoutField<PatternValidationError>;
1125
+ declare function patternError(pattern: RegExp, options?: ValidationErrorOptions): WithoutFieldTree<PatternValidationError>;
925
1126
  /**
926
1127
  * Create an email format error associated with the target field
927
1128
  * @param options The validation error options
@@ -929,7 +1130,7 @@ declare function patternError(pattern: RegExp, options?: ValidationErrorOptions)
929
1130
  * @category validation
930
1131
  * @experimental 21.0.0
931
1132
  */
932
- declare function emailError(options: WithField<ValidationErrorOptions>): EmailValidationError;
1133
+ declare function emailError(options: WithFieldTree<ValidationErrorOptions>): EmailValidationError;
933
1134
  /**
934
1135
  * Create an email format error
935
1136
  * @param options The optional validation error options
@@ -937,25 +1138,7 @@ declare function emailError(options: WithField<ValidationErrorOptions>): EmailVa
937
1138
  * @category validation
938
1139
  * @experimental 21.0.0
939
1140
  */
940
- declare function emailError(options?: ValidationErrorOptions): WithoutField<EmailValidationError>;
941
- /**
942
- * Create a standard schema issue error associated with the target field
943
- * @param issue The standard schema issue
944
- * @param options The validation error options
945
- *
946
- * @category validation
947
- * @experimental 21.0.0
948
- */
949
- declare function standardSchemaError(issue: StandardSchemaV1.Issue, options: WithField<ValidationErrorOptions>): StandardSchemaValidationError;
950
- /**
951
- * Create a standard schema issue error
952
- * @param issue The standard schema issue
953
- * @param options The optional validation error options
954
- *
955
- * @category validation
956
- * @experimental 21.0.0
957
- */
958
- declare function standardSchemaError(issue: StandardSchemaV1.Issue, options?: ValidationErrorOptions): WithoutField<StandardSchemaValidationError>;
1141
+ declare function emailError(options?: ValidationErrorOptions): WithoutFieldTree<EmailValidationError>;
959
1142
  /**
960
1143
  * Common interface for all validation errors.
961
1144
  *
@@ -977,14 +1160,23 @@ interface ValidationError {
977
1160
  }
978
1161
  declare namespace ValidationError {
979
1162
  /**
980
- * Validation error with a field.
1163
+ * Validation error with an associated field tree.
981
1164
  *
982
1165
  * This is returned from field state, e.g., catField.errors() would be of a list of errors with
983
1166
  * `field: catField` bound to state.
984
1167
  */
985
- interface WithField extends ValidationError {
1168
+ interface WithFieldTree extends ValidationError {
986
1169
  /** The field associated with this error. */
987
1170
  readonly fieldTree: FieldTree<unknown>;
1171
+ readonly formField?: FormField<unknown>;
1172
+ }
1173
+ /** @deprecated Use `ValidationError.WithFieldTree` instead */
1174
+ type WithField = WithFieldTree;
1175
+ /**
1176
+ * Validation error with an associated field tree and specific form field binding.
1177
+ */
1178
+ interface WithFormField extends WithFieldTree {
1179
+ readonly formField: FormField<unknown>;
988
1180
  }
989
1181
  /**
990
1182
  * Validation error with optional field.
@@ -992,19 +1184,24 @@ declare namespace ValidationError {
992
1184
  * This is generally used in places where the result might have a field.
993
1185
  * e.g., as a result of a `validateTree`, or when handling form submission.
994
1186
  */
995
- interface WithOptionalField extends ValidationError {
1187
+ interface WithOptionalFieldTree extends ValidationError {
996
1188
  /** The field associated with this error. */
997
1189
  readonly fieldTree?: FieldTree<unknown>;
998
1190
  }
1191
+ /** @deprecated Use `ValidationError.WithOptionalFieldTree` instead */
1192
+ type WithOptionalField = WithOptionalFieldTree;
999
1193
  /**
1000
1194
  * Validation error with no field.
1001
1195
  *
1002
1196
  * This is used to strongly enforce that fields are not allowed in validation result.
1003
1197
  */
1004
- interface WithoutField extends ValidationError {
1198
+ interface WithoutFieldTree extends ValidationError {
1005
1199
  /** The field associated with this error. */
1006
1200
  readonly fieldTree?: never;
1201
+ readonly formField?: never;
1007
1202
  }
1203
+ /** @deprecated Use `ValidationError.WithoutFieldTree` instead */
1204
+ type WithoutField = WithoutFieldTree;
1008
1205
  }
1009
1206
  /**
1010
1207
  * Internal version of `NgValidationError`, we create this separately so we can change its type on
@@ -1012,7 +1209,7 @@ declare namespace ValidationError {
1012
1209
  *
1013
1210
  * @experimental 21.0.0
1014
1211
  */
1015
- declare abstract class _NgValidationError implements ValidationError {
1212
+ declare abstract class BaseNgValidationError implements ValidationError {
1016
1213
  /** Brand the class to avoid Typescript structural matching */
1017
1214
  private __brand;
1018
1215
  /** Identifies the kind of error. */
@@ -1029,7 +1226,7 @@ declare abstract class _NgValidationError implements ValidationError {
1029
1226
  * @category validation
1030
1227
  * @experimental 21.0.0
1031
1228
  */
1032
- declare class RequiredValidationError extends _NgValidationError {
1229
+ declare class RequiredValidationError extends BaseNgValidationError {
1033
1230
  readonly kind = "required";
1034
1231
  }
1035
1232
  /**
@@ -1038,7 +1235,7 @@ declare class RequiredValidationError extends _NgValidationError {
1038
1235
  * @category validation
1039
1236
  * @experimental 21.0.0
1040
1237
  */
1041
- declare class MinValidationError extends _NgValidationError {
1238
+ declare class MinValidationError extends BaseNgValidationError {
1042
1239
  readonly min: number;
1043
1240
  readonly kind = "min";
1044
1241
  constructor(min: number, options?: ValidationErrorOptions);
@@ -1049,7 +1246,7 @@ declare class MinValidationError extends _NgValidationError {
1049
1246
  * @category validation
1050
1247
  * @experimental 21.0.0
1051
1248
  */
1052
- declare class MaxValidationError extends _NgValidationError {
1249
+ declare class MaxValidationError extends BaseNgValidationError {
1053
1250
  readonly max: number;
1054
1251
  readonly kind = "max";
1055
1252
  constructor(max: number, options?: ValidationErrorOptions);
@@ -1060,7 +1257,7 @@ declare class MaxValidationError extends _NgValidationError {
1060
1257
  * @category validation
1061
1258
  * @experimental 21.0.0
1062
1259
  */
1063
- declare class MinLengthValidationError extends _NgValidationError {
1260
+ declare class MinLengthValidationError extends BaseNgValidationError {
1064
1261
  readonly minLength: number;
1065
1262
  readonly kind = "minLength";
1066
1263
  constructor(minLength: number, options?: ValidationErrorOptions);
@@ -1071,7 +1268,7 @@ declare class MinLengthValidationError extends _NgValidationError {
1071
1268
  * @category validation
1072
1269
  * @experimental 21.0.0
1073
1270
  */
1074
- declare class MaxLengthValidationError extends _NgValidationError {
1271
+ declare class MaxLengthValidationError extends BaseNgValidationError {
1075
1272
  readonly maxLength: number;
1076
1273
  readonly kind = "maxLength";
1077
1274
  constructor(maxLength: number, options?: ValidationErrorOptions);
@@ -1082,7 +1279,7 @@ declare class MaxLengthValidationError extends _NgValidationError {
1082
1279
  * @category validation
1083
1280
  * @experimental 21.0.0
1084
1281
  */
1085
- declare class PatternValidationError extends _NgValidationError {
1282
+ declare class PatternValidationError extends BaseNgValidationError {
1086
1283
  readonly pattern: RegExp;
1087
1284
  readonly kind = "pattern";
1088
1285
  constructor(pattern: RegExp, options?: ValidationErrorOptions);
@@ -1093,20 +1290,9 @@ declare class PatternValidationError extends _NgValidationError {
1093
1290
  * @category validation
1094
1291
  * @experimental 21.0.0
1095
1292
  */
1096
- declare class EmailValidationError extends _NgValidationError {
1293
+ declare class EmailValidationError extends BaseNgValidationError {
1097
1294
  readonly kind = "email";
1098
1295
  }
1099
- /**
1100
- * An error used to indicate an issue validating against a standard schema.
1101
- *
1102
- * @category validation
1103
- * @experimental 21.0.0
1104
- */
1105
- declare class StandardSchemaValidationError extends _NgValidationError {
1106
- readonly issue: StandardSchemaV1.Issue;
1107
- readonly kind = "standardSchema";
1108
- constructor(issue: StandardSchemaV1.Issue, options?: ValidationErrorOptions);
1109
- }
1110
1296
  /**
1111
1297
  * The base class for all built-in, non-custom errors. This class can be used to check if an error
1112
1298
  * is one of the standard kinds, allowing you to switch on the kind to further narrow the type.
@@ -1153,990 +1339,40 @@ interface SignalFormsConfig {
1153
1339
  */
1154
1340
  declare function provideSignalFormsConfig(config: SignalFormsConfig): Provider[];
1155
1341
 
1156
- /** Represents a result that should be ignored because its predicate indicates it is not active. */
1157
- declare const IGNORED: unique symbol;
1158
- /**
1159
- * A predicate that indicates whether an `AbstractLogic` instance is currently active, or should be
1160
- * ignored.
1161
- */
1162
- interface Predicate {
1163
- /** A boolean logic function that returns true if the logic is considered active. */
1164
- readonly fn: LogicFn<any, boolean>;
1165
- /**
1166
- * The path which this predicate was created for. This is used to determine the correct
1167
- * `FieldContext` to pass to the predicate function.
1168
- */
1169
- readonly path: SchemaPath<any>;
1170
- }
1171
1342
  /**
1172
- * Represents a predicate that is bound to a particular depth in the field tree. This is needed for
1173
- * recursively applied logic to ensure that the predicate is evaluated against the correct
1174
- * application of that logic.
1175
- *
1176
- * Consider the following example:
1343
+ * Options that can be specified when submitting a form.
1177
1344
  *
1178
- * ```ts
1179
- * const s = schema(p => {
1180
- * disabled(p.data);
1181
- * applyWhen(p.next, ({valueOf}) => valueOf(p.data) === 1, s);
1182
- * });
1183
- *
1184
- * const f = form(signal({data: 0, next: {data: 1, next: {data: 2, next: undefined}}}), s);
1185
- *
1186
- * const isDisabled = f.next.next.data().disabled();
1187
- * ```
1188
- *
1189
- * In order to determine `isDisabled` we need to evaluate the predicate from `applyWhen` *twice*.
1190
- * Once to see if the schema should be applied to `f.next` and again to see if it should be applied
1191
- * to `f.next.next`. The `depth` tells us which field we should be evaluating against each time.
1192
- */
1193
- interface BoundPredicate extends Predicate {
1194
- /** The depth in the field tree at which this predicate is bound. */
1195
- readonly depth: number;
1196
- }
1197
- /**
1198
- * Base class for all logic. It is responsible for combining the results from multiple individual
1199
- * logic functions registered in the schema, and using them to derive the value for some associated
1200
- * piece of field state.
1201
- */
1202
- declare abstract class AbstractLogic<TReturn, TValue = TReturn> {
1203
- /**
1204
- * A list of predicates that conditionally enable all logic in this logic instance.
1205
- * The logic is only enabled when *all* of the predicates evaluate to true.
1206
- */
1207
- private predicates;
1208
- /** The set of logic functions that contribute to the value of the associated state. */
1209
- protected readonly fns: Array<LogicFn<any, TValue | typeof IGNORED>>;
1210
- constructor(
1211
- /**
1212
- * A list of predicates that conditionally enable all logic in this logic instance.
1213
- * The logic is only enabled when *all* of the predicates evaluate to true.
1214
- */
1215
- predicates: ReadonlyArray<BoundPredicate>);
1216
- /**
1217
- * Computes the value of the associated field state based on the logic functions and predicates
1218
- * registered with this logic instance.
1219
- */
1220
- abstract compute(arg: FieldContext<any>): TReturn;
1221
- /**
1222
- * The default value that the associated field state should assume if there are no logic functions
1223
- * registered by the schema (or if the logic is disabled by a predicate).
1224
- */
1225
- abstract get defaultValue(): TReturn;
1226
- /** Registers a logic function with this logic instance. */
1227
- push(logicFn: LogicFn<any, TValue>): void;
1228
- /**
1229
- * Merges in the logic from another logic instance, subject to the predicates of both the other
1230
- * instance and this instance.
1231
- */
1232
- mergeIn(other: AbstractLogic<TReturn, TValue>): void;
1233
- }
1234
- /** Logic that combines its individual logic function results with logical OR. */
1235
- declare class BooleanOrLogic extends AbstractLogic<boolean> {
1236
- get defaultValue(): boolean;
1237
- compute(arg: FieldContext<any>): boolean;
1238
- }
1239
- /**
1240
- * Logic that combines its individual logic function results by aggregating them in an array.
1241
- * Depending on its `ignore` function it may ignore certain values, omitting them from the array.
1242
- */
1243
- declare class ArrayMergeIgnoreLogic<TElement, TIgnore = never> extends AbstractLogic<readonly TElement[], TElement | readonly (TElement | TIgnore)[] | TIgnore | undefined | void> {
1244
- private ignore;
1245
- /** Creates an instance of this class that ignores `null` values. */
1246
- static ignoreNull<TElement>(predicates: ReadonlyArray<BoundPredicate>): ArrayMergeIgnoreLogic<TElement, null>;
1247
- constructor(predicates: ReadonlyArray<BoundPredicate>, ignore: undefined | ((e: TElement | undefined | TIgnore) => e is TIgnore));
1248
- get defaultValue(): never[];
1249
- compute(arg: FieldContext<any>): readonly TElement[];
1250
- }
1251
- /** Logic that combines its individual logic function results by aggregating them in an array. */
1252
- declare class ArrayMergeLogic<TElement> extends ArrayMergeIgnoreLogic<TElement, never> {
1253
- constructor(predicates: ReadonlyArray<BoundPredicate>);
1254
- }
1255
- /**
1256
- * Container for all the different types of logic that can be applied to a field
1257
- * (disabled, hidden, errors, etc.)
1258
- */
1259
- declare class LogicContainer {
1260
- private predicates;
1261
- /** Logic that determines if the field is hidden. */
1262
- readonly hidden: BooleanOrLogic;
1263
- /** Logic that determines reasons for the field being disabled. */
1264
- readonly disabledReasons: ArrayMergeLogic<DisabledReason>;
1265
- /** Logic that determines if the field is read-only. */
1266
- readonly readonly: BooleanOrLogic;
1267
- /** Logic that produces synchronous validation errors for the field. */
1268
- readonly syncErrors: ArrayMergeIgnoreLogic<ValidationError.WithField, null>;
1269
- /** Logic that produces synchronous validation errors for the field's subtree. */
1270
- readonly syncTreeErrors: ArrayMergeIgnoreLogic<ValidationError.WithField, null>;
1271
- /** Logic that produces asynchronous validation results (errors or 'pending'). */
1272
- readonly asyncErrors: ArrayMergeIgnoreLogic<ValidationError.WithField | 'pending', null>;
1273
- /** A map of metadata keys to the `AbstractLogic` instances that compute their values. */
1274
- private readonly metadata;
1275
- /**
1276
- * Constructs a new `Logic` container.
1277
- * @param predicates An array of predicates that must all be true for the logic
1278
- * functions within this container to be active.
1279
- */
1280
- constructor(predicates: ReadonlyArray<BoundPredicate>);
1281
- /** Checks whether there is logic for the given metadata key. */
1282
- hasMetadata(key: MetadataKey<any, any, any>): boolean;
1283
- /**
1284
- * Gets an iterable of [metadata key, logic function] pairs.
1285
- * @returns An iterable of metadata keys.
1286
- */
1287
- getMetadataKeys(): MapIterator<MetadataKey<unknown, unknown, unknown>>;
1288
- /**
1289
- * Retrieves or creates the `AbstractLogic` for a given metadata key.
1290
- * @param key The `MetadataKey` for which to get the logic.
1291
- * @returns The `AbstractLogic` associated with the key.
1292
- */
1293
- getMetadata<T>(key: MetadataKey<any, T, any>): AbstractLogic<T>;
1294
- /**
1295
- * Merges logic from another `Logic` instance into this one.
1296
- * @param other The `Logic` instance to merge from.
1297
- */
1298
- mergeIn(other: LogicContainer): void;
1299
- }
1300
-
1301
- /**
1302
- * Abstract base class for building a `LogicNode`.
1303
- * This class defines the interface for adding various logic rules (e.g., hidden, disabled)
1304
- * and data factories to a node in the logic tree.
1305
- * LogicNodeBuilders are 1:1 with nodes in the Schema tree.
1306
- */
1307
- declare abstract class AbstractLogicNodeBuilder {
1308
- /** The depth of this node in the schema tree. */
1309
- protected readonly depth: number;
1310
- constructor(
1311
- /** The depth of this node in the schema tree. */
1312
- depth: number);
1313
- /** Adds a rule to determine if a field should be hidden. */
1314
- abstract addHiddenRule(logic: LogicFn<any, boolean>): void;
1315
- /** Adds a rule to determine if a field should be disabled, and for what reason. */
1316
- abstract addDisabledReasonRule(logic: LogicFn<any, DisabledReason | undefined>): void;
1317
- /** Adds a rule to determine if a field should be read-only. */
1318
- abstract addReadonlyRule(logic: LogicFn<any, boolean>): void;
1319
- /** Adds a rule for synchronous validation errors for a field. */
1320
- abstract addSyncErrorRule(logic: LogicFn<any, ValidationResult>): void;
1321
- /** Adds a rule for synchronous validation errors that apply to a subtree. */
1322
- abstract addSyncTreeErrorRule(logic: LogicFn<any, ValidationResult>): void;
1323
- /** Adds a rule for asynchronous validation errors for a field. */
1324
- abstract addAsyncErrorRule(logic: LogicFn<any, AsyncValidationResult>): void;
1325
- /** Adds a rule to compute metadata for a field. */
1326
- abstract addMetadataRule<M>(key: MetadataKey<unknown, M, unknown>, logic: LogicFn<any, M>): void;
1327
- /**
1328
- * Gets a builder for a child node associated with the given property key.
1329
- * @param key The property key of the child.
1330
- * @returns A `LogicNodeBuilder` for the child.
1331
- */
1332
- abstract getChild(key: PropertyKey): LogicNodeBuilder;
1333
- /**
1334
- * Checks whether a particular `AbstractLogicNodeBuilder` has been merged into this one.
1335
- * @param builder The builder to check for.
1336
- * @returns True if the builder has been merged, false otherwise.
1337
- */
1338
- abstract hasLogic(builder: AbstractLogicNodeBuilder): boolean;
1339
- /**
1340
- * Builds the `LogicNode` from the accumulated rules and child builders.
1341
- * @returns The constructed `LogicNode`.
1342
- */
1343
- build(): LogicNode;
1344
- }
1345
- /**
1346
- * A builder for `LogicNode`. Used to add logic to the final `LogicNode` tree.
1347
- * This builder supports merging multiple sources of logic, potentially with predicates,
1348
- * preserving the order of rule application.
1349
- */
1350
- declare class LogicNodeBuilder extends AbstractLogicNodeBuilder {
1351
- constructor(depth: number);
1352
- /**
1353
- * The current `NonMergeableLogicNodeBuilder` being used to add rules directly to this
1354
- * `LogicNodeBuilder`. Do not use this directly, call `getCurrent()` which will create a current
1355
- * builder if there is none.
1356
- */
1357
- private current;
1358
- /**
1359
- * Stores all builders that contribute to this node, along with any predicates
1360
- * that gate their application.
1361
- */
1362
- readonly all: {
1363
- builder: AbstractLogicNodeBuilder;
1364
- predicate?: Predicate;
1365
- }[];
1366
- addHiddenRule(logic: LogicFn<any, boolean>): void;
1367
- addDisabledReasonRule(logic: LogicFn<any, DisabledReason | undefined>): void;
1368
- addReadonlyRule(logic: LogicFn<any, boolean>): void;
1369
- addSyncErrorRule(logic: LogicFn<any, ValidationResult<ValidationError.WithField>>): void;
1370
- addSyncTreeErrorRule(logic: LogicFn<any, ValidationResult<ValidationError.WithField>>): void;
1371
- addAsyncErrorRule(logic: LogicFn<any, AsyncValidationResult<ValidationError.WithField>>): void;
1372
- addMetadataRule<T>(key: MetadataKey<unknown, T, any>, logic: LogicFn<any, T>): void;
1373
- getChild(key: PropertyKey): LogicNodeBuilder;
1374
- hasLogic(builder: AbstractLogicNodeBuilder): boolean;
1375
- /**
1376
- * Merges logic from another `LogicNodeBuilder` into this one.
1377
- * If a `predicate` is provided, all logic from the `other` builder will only apply
1378
- * when the predicate evaluates to true.
1379
- * @param other The `LogicNodeBuilder` to merge in.
1380
- * @param predicate An optional predicate to gate the merged logic.
1381
- */
1382
- mergeIn(other: LogicNodeBuilder, predicate?: Predicate): void;
1383
- /**
1384
- * Gets the current `NonMergeableLogicNodeBuilder` for adding rules directly to this
1385
- * `LogicNodeBuilder`. If no current builder exists, a new one is created.
1386
- * The current builder is cleared whenever `mergeIn` is called to preserve the order
1387
- * of rules when merging separate builder trees.
1388
- * @returns The current `NonMergeableLogicNodeBuilder`.
1389
- */
1390
- private getCurrent;
1391
- /**
1392
- * Creates a new root `LogicNodeBuilder`.
1393
- * @returns A new instance of `LogicNodeBuilder`.
1394
- */
1395
- static newRoot(): LogicNodeBuilder;
1396
- }
1397
- /**
1398
- * Represents a node in the logic tree, containing all logic applicable
1399
- * to a specific field or path in the form structure.
1400
- * LogicNodes are 1:1 with nodes in the Field tree.
1401
- */
1402
- interface LogicNode {
1403
- /** The collection of logic rules (hidden, disabled, errors, etc.) for this node. */
1404
- readonly logic: LogicContainer;
1405
- /**
1406
- * Retrieves the `LogicNode` for a child identified by the given property key.
1407
- * @param key The property key of the child.
1408
- * @returns The `LogicNode` for the specified child.
1409
- */
1410
- getChild(key: PropertyKey): LogicNode;
1411
- /**
1412
- * Checks whether the logic from a particular `AbstractLogicNodeBuilder` has been merged into this
1413
- * node.
1414
- * @param builder The builder to check for.
1415
- * @returns True if the builder has been merged, false otherwise.
1416
- */
1417
- hasLogic(builder: AbstractLogicNodeBuilder): boolean;
1418
- }
1419
-
1420
- /**
1421
- * Implements the `Schema` concept.
1345
+ * @experimental 21.2.0
1422
1346
  */
1423
- declare class SchemaImpl {
1424
- private schemaFn;
1425
- constructor(schemaFn: SchemaFn<unknown>);
1426
- /**
1427
- * Compiles this schema within the current root compilation context. If the schema was previously
1428
- * compiled within this context, we reuse the cached FieldPathNode, otherwise we create a new one
1429
- * and cache it in the compilation context.
1430
- */
1431
- compile(): FieldPathNode;
1432
- /**
1433
- * Creates a SchemaImpl from the given SchemaOrSchemaFn.
1434
- */
1435
- static create(schema: SchemaImpl | SchemaOrSchemaFn<any>): SchemaImpl;
1436
- /**
1437
- * Compiles the given schema in a fresh compilation context. This clears the cached results of any
1438
- * previous compilations.
1439
- */
1440
- static rootCompile(schema: SchemaImpl | SchemaOrSchemaFn<any> | undefined): FieldPathNode;
1441
- }
1442
-
1443
- /**
1444
- * A path in the schema on which logic is stored so that it can be added to the corresponding field
1445
- * when the field is created.
1446
- */
1447
- declare class FieldPathNode {
1448
- /** The property keys used to navigate from the root path to this path. */
1449
- readonly keys: PropertyKey[];
1450
- /** The parent of this path node. */
1451
- private readonly parent;
1452
- /** The key of this node in its parent. */
1453
- private readonly keyInParent;
1454
- /** The root path node from which this path node is descended. */
1455
- readonly root: FieldPathNode;
1347
+ interface FormSubmitOptions<TModel> {
1348
+ /** Function to run when submitting the form data (when form is valid). */
1349
+ action: (form: FieldTree<TModel>) => Promise<TreeValidationResult>;
1350
+ /** Function to run when attempting to submit the form data but validation is failing. */
1351
+ onInvalid?: (form: FieldTree<TModel>) => void;
1456
1352
  /**
1457
- * A map containing all child path nodes that have been created on this path.
1458
- * Child path nodes are created automatically on first access if they do not exist already.
1353
+ * Whether to ignore any of the validators when submitting:
1354
+ * - 'pending': Will submit if there are no invalid validators, pending validators do not block submission (default)
1355
+ * - 'none': Will not submit unless all validators are passing, pending validators block submission
1356
+ * - 'ignore': Will always submit regardless of invalid or pending validators
1459
1357
  */
1460
- private readonly children;
1461
- /**
1462
- * A proxy that wraps the path node, allowing navigation to its child paths via property access.
1463
- */
1464
- readonly fieldPathProxy: SchemaPath<any>;
1465
- /**
1466
- * For a root path node this will contain the root logic builder. For non-root nodes,
1467
- * they determine their logic builder from their parent so this is undefined.
1468
- */
1469
- private readonly logicBuilder;
1470
- protected constructor(
1471
- /** The property keys used to navigate from the root path to this path. */
1472
- keys: PropertyKey[], root: FieldPathNode | undefined,
1473
- /** The parent of this path node. */
1474
- parent: FieldPathNode | undefined,
1475
- /** The key of this node in its parent. */
1476
- keyInParent: PropertyKey | undefined);
1477
- /** The logic builder used to accumulate logic on this path node. */
1478
- get builder(): LogicNodeBuilder;
1479
- /**
1480
- * Gets the path node for the given child property key.
1481
- * Child paths are created automatically on first access if they do not exist already.
1482
- */
1483
- getChild(key: PropertyKey): FieldPathNode;
1484
- /**
1485
- * Merges in logic from another schema to this one.
1486
- * @param other The other schema to merge in the logic from
1487
- * @param predicate A predicate indicating when the merged in logic should be active.
1488
- */
1489
- mergeIn(other: SchemaImpl, predicate?: Predicate): void;
1490
- /** Extracts the underlying path node from the given path proxy. */
1491
- static unwrapFieldPath(formPath: SchemaPath<unknown, SchemaPathRules>): FieldPathNode;
1492
- /** Creates a new root path node to be passed in to a schema function. */
1493
- static newRoot(): FieldPathNode;
1494
- }
1495
-
1496
- /**
1497
- * Tracks custom metadata associated with a `FieldNode`.
1498
- */
1499
- declare class FieldMetadataState {
1500
- private readonly node;
1501
- /** A map of all `MetadataKey` that have been defined for this field. */
1502
- private readonly metadata;
1503
- constructor(node: FieldNode);
1504
- /** Gets the value of an `MetadataKey` for the field. */
1505
- get<T>(key: MetadataKey<T, unknown, unknown>): T | undefined;
1506
- /** Checks whether the current metadata state has the given metadata key. */
1507
- has(key: MetadataKey<any, any, any>): boolean;
1358
+ ignoreValidators?: 'pending' | 'none' | 'all';
1508
1359
  }
1509
-
1510
- /**
1511
- * The non-validation and non-submit state associated with a `FieldNode`, such as touched and dirty
1512
- * status, as well as derived logical state.
1513
- */
1514
- declare class FieldNodeState {
1515
- private readonly node;
1516
- /**
1517
- * Indicates whether this field has been touched directly by the user (as opposed to indirectly by
1518
- * touching a child field).
1519
- *
1520
- * A field is considered directly touched when a user stops editing it for the first time (i.e. on blur)
1521
- */
1522
- private readonly selfTouched;
1523
- /**
1524
- * Indicates whether this field has been dirtied directly by the user (as opposed to indirectly by
1525
- * dirtying a child field).
1526
- *
1527
- * A field is considered directly dirtied if a user changed the value of the field at least once.
1528
- */
1529
- private readonly selfDirty;
1530
- /**
1531
- * Marks this specific field as touched.
1532
- */
1533
- markAsTouched(): void;
1534
- /**
1535
- * Marks this specific field as dirty.
1536
- */
1537
- markAsDirty(): void;
1538
- /**
1539
- * Marks this specific field as not dirty.
1540
- */
1541
- markAsPristine(): void;
1542
- /**
1543
- * Marks this specific field as not touched.
1544
- */
1545
- markAsUntouched(): void;
1546
- /** The {@link FormField} directives that bind this field to a UI control. */
1547
- readonly formFieldBindings: i0.WritableSignal<readonly FormField<unknown>[]>;
1548
- constructor(node: FieldNode);
1549
- /**
1550
- * Whether this field is considered dirty.
1551
- *
1552
- * A field is considered dirty if one of the following is true:
1553
- * - It was directly dirtied and is interactive
1554
- * - One of its children is considered dirty
1555
- */
1556
- readonly dirty: Signal<boolean>;
1557
- /**
1558
- * Whether this field is considered touched.
1559
- *
1560
- * A field is considered touched if one of the following is true:
1561
- * - It was directly touched and is interactive
1562
- * - One of its children is considered touched
1563
- */
1564
- readonly touched: Signal<boolean>;
1565
- /**
1566
- * The reasons for this field's disablement. This includes disabled reasons for any parent field
1567
- * that may have been disabled, indirectly causing this field to be disabled as well.
1568
- * The `field` property of the `DisabledReason` can be used to determine which field ultimately
1569
- * caused the disablement.
1570
- */
1571
- readonly disabledReasons: Signal<readonly DisabledReason[]>;
1572
- /**
1573
- * Whether this field is considered disabled.
1574
- *
1575
- * A field is considered disabled if one of the following is true:
1576
- * - The schema contains logic that directly disabled it
1577
- * - Its parent field is considered disabled
1578
- */
1579
- readonly disabled: Signal<boolean>;
1580
- /**
1581
- * Whether this field is considered readonly.
1582
- *
1583
- * A field is considered readonly if one of the following is true:
1584
- * - The schema contains logic that directly made it readonly
1585
- * - Its parent field is considered readonly
1586
- */
1587
- readonly readonly: Signal<boolean>;
1588
- /**
1589
- * Whether this field is considered hidden.
1590
- *
1591
- * A field is considered hidden if one of the following is true:
1592
- * - The schema contains logic that directly hides it
1593
- * - Its parent field is considered hidden
1594
- */
1595
- readonly hidden: Signal<boolean>;
1596
- readonly name: Signal<string>;
1597
- /**
1598
- * An optional {@link Debouncer} factory for this field.
1599
- */
1600
- readonly debouncer: Signal<((signal: AbortSignal) => Promise<void> | void) | undefined>;
1601
- /** Whether this field is considered non-interactive.
1602
- *
1603
- * A field is considered non-interactive if one of the following is true:
1604
- * - It is hidden
1605
- * - It is disabled
1606
- * - It is readonly
1607
- */
1608
- private readonly isNonInteractive;
1609
- }
1610
-
1611
- /**
1612
- * State of a `FieldNode` that's associated with form submission.
1613
- */
1614
- declare class FieldSubmitState {
1615
- private readonly node;
1616
- /**
1617
- * Whether this field was directly submitted (as opposed to indirectly by a parent field being submitted)
1618
- * and is still in the process of submitting.
1619
- */
1620
- readonly selfSubmitting: WritableSignal<boolean>;
1621
- /** Submission errors that are associated with this field. */
1622
- readonly submissionErrors: WritableSignal<readonly ValidationError.WithField[]>;
1623
- constructor(node: FieldNode);
1624
- /**
1625
- * Whether this form is currently in the process of being submitted.
1626
- * Either because the field was submitted directly, or because a parent field was submitted.
1627
- */
1628
- readonly submitting: Signal<boolean>;
1629
- }
1630
-
1631
- interface ValidationState {
1632
- /**
1633
- * The full set of synchronous tree errors visible to this field. This includes ones that are
1634
- * targeted at a descendant field rather than at this field.
1635
- */
1636
- rawSyncTreeErrors: Signal<ValidationError.WithField[]>;
1637
- /**
1638
- * The full set of synchronous errors for this field, including synchronous tree errors and submission
1639
- * errors. Submission errors are considered "synchronous" because they are imperatively added. From
1640
- * the perspective of the field state they are either there or not, they are never in a pending
1641
- * state.
1642
- */
1643
- syncErrors: Signal<ValidationError.WithField[]>;
1644
- /**
1645
- * Whether the field is considered valid according solely to its synchronous validators.
1646
- * Errors resulting from a previous submit attempt are also considered for this state.
1647
- */
1648
- syncValid: Signal<boolean>;
1649
- /**
1650
- * The full set of asynchronous tree errors visible to this field. This includes ones that are
1651
- * targeted at a descendant field rather than at this field, as well as sentinel 'pending' values
1652
- * indicating that the validator is still running and an error could still occur.
1653
- */
1654
- rawAsyncErrors: Signal<(ValidationError.WithField | 'pending')[]>;
1655
- /**
1656
- * The asynchronous tree errors visible to this field that are specifically targeted at this field
1657
- * rather than a descendant. This also includes all 'pending' sentinel values, since those could
1658
- * theoretically result in errors for this field.
1659
- */
1660
- asyncErrors: Signal<(ValidationError.WithField | 'pending')[]>;
1661
- /**
1662
- * The combined set of all errors that currently apply to this field.
1663
- */
1664
- errors: Signal<ValidationError.WithField[]>;
1665
- /**
1666
- * The combined set of all errors that currently apply to this field and its descendants.
1667
- */
1668
- errorSummary: Signal<ValidationError.WithField[]>;
1669
- /**
1670
- * Whether this field has any asynchronous validators still pending.
1671
- */
1672
- pending: Signal<boolean>;
1673
- /**
1674
- * The validation status of the field.
1675
- * - The status is 'valid' if neither the field nor any of its children has any errors or pending
1676
- * validators.
1677
- * - The status is 'invalid' if the field or any of its children has an error
1678
- * (regardless of pending validators)
1679
- * - The status is 'unknown' if neither the field nor any of its children has any errors,
1680
- * but the field or any of its children does have a pending validator.
1681
- *
1682
- * A field is considered valid if *all* of the following are true:
1683
- * - It has no errors or pending validators
1684
- * - All of its children are considered valid
1685
- * A field is considered invalid if *any* of the following are true:
1686
- * - It has an error
1687
- * - Any of its children is considered invalid
1688
- * A field is considered to have unknown validity status if it is not valid or invalid.
1689
- */
1690
- status: Signal<'valid' | 'invalid' | 'unknown'>;
1691
- /**
1692
- * Whether the field is considered valid.
1693
- *
1694
- * A field is considered valid if *all* of the following are true:
1695
- * - It has no errors or pending validators
1696
- * - All of its children are considered valid
1697
- *
1698
- * Note: `!valid()` is *not* the same as `invalid()`. Both `valid()` and `invalid()` can be false
1699
- * if there are currently no errors, but validators are still pending.
1700
- */
1701
- valid: Signal<boolean>;
1702
- /**
1703
- * Whether the field is considered invalid.
1704
- *
1705
- * A field is considered invalid if *any* of the following are true:
1706
- * - It has an error
1707
- * - Any of its children is considered invalid
1708
- *
1709
- * Note: `!invalid()` is *not* the same as `valid()`. Both `valid()` and `invalid()` can be false
1710
- * if there are currently no errors, but validators are still pending.
1711
- */
1712
- invalid: Signal<boolean>;
1713
- /**
1714
- * Indicates whether validation should be skipped for this field because it is hidden, disabled,
1715
- * or readonly.
1716
- */
1717
- shouldSkipValidation: Signal<boolean>;
1718
- }
1719
-
1720
- /**
1721
- * Internal node in the form tree for a given field.
1722
- *
1723
- * Field nodes have several responsibilities:
1724
- * - They track instance state for the particular field (touched)
1725
- * - They compute signals for derived state (valid, disabled, etc) based on their associated
1726
- * `LogicNode`
1727
- * - They act as the public API for the field (they implement the `FieldState` interface)
1728
- * - They implement navigation of the form tree via `.parent` and `.getChild()`.
1729
- *
1730
- * This class is largely a wrapper that aggregates several smaller pieces that each manage a subset of
1731
- * the responsibilities.
1732
- */
1733
- declare class FieldNode implements FieldState<unknown> {
1734
- readonly structure: FieldNodeStructure;
1735
- readonly validationState: ValidationState;
1736
- readonly metadataState: FieldMetadataState;
1737
- readonly nodeState: FieldNodeState;
1738
- readonly submitState: FieldSubmitState;
1739
- readonly fieldAdapter: FieldAdapter;
1740
- private _context;
1741
- get context(): FieldContext<unknown>;
1742
- /**
1743
- * Proxy to this node which allows navigation of the form graph below it.
1744
- */
1745
- readonly fieldProxy: FieldTree<any>;
1746
- private readonly pathNode;
1747
- constructor(options: FieldNodeOptions);
1748
- focusBoundControl(options?: FocusOptions): void;
1749
- /**
1750
- * Gets the Field directive binding that should be focused when the developer calls
1751
- * `focusBoundControl` on this node.
1752
- *
1753
- * This will prioritize focusable bindings to this node, and if multiple exist, it will return
1754
- * the first one in the DOM. If no focusable bindings exist on this node, it will return the
1755
- * first focusable binding in the DOM for any descendant node of this one.
1756
- */
1757
- private getBindingForFocus;
1758
- /**
1759
- * The `AbortController` for the currently debounced sync, or `undefined` if there is none.
1760
- *
1761
- * This is used to cancel a pending debounced sync when {@link setControlValue} is called again
1762
- * before the pending debounced sync resolves. It will also cancel any pending debounced sync
1763
- * automatically when recomputed due to `value` being set directly from others sources.
1764
- */
1765
- private readonly pendingSync;
1766
- get logicNode(): LogicNode;
1767
- get value(): WritableSignal<unknown>;
1768
- private _controlValue;
1769
- get controlValue(): Signal<unknown>;
1770
- get keyInParent(): Signal<string | number>;
1771
- get errors(): Signal<ValidationError.WithField[]>;
1772
- get errorSummary(): Signal<ValidationError.WithField[]>;
1773
- get pending(): Signal<boolean>;
1774
- get valid(): Signal<boolean>;
1775
- get invalid(): Signal<boolean>;
1776
- get dirty(): Signal<boolean>;
1777
- get touched(): Signal<boolean>;
1778
- get disabled(): Signal<boolean>;
1779
- get disabledReasons(): Signal<readonly DisabledReason[]>;
1780
- get hidden(): Signal<boolean>;
1781
- get readonly(): Signal<boolean>;
1782
- get formFieldBindings(): Signal<readonly FormField<unknown>[]>;
1783
- get submitting(): Signal<boolean>;
1784
- get name(): Signal<string>;
1785
- get max(): Signal<number | undefined> | undefined;
1786
- get maxLength(): Signal<number | undefined> | undefined;
1787
- get min(): Signal<number | undefined> | undefined;
1788
- get minLength(): Signal<number | undefined> | undefined;
1789
- get pattern(): Signal<readonly RegExp[]>;
1790
- get required(): Signal<boolean>;
1791
- metadata<M>(key: MetadataKey<M, any, any>): M | undefined;
1792
- hasMetadata(key: MetadataKey<any, any, any>): boolean;
1793
- /**
1794
- * Marks this specific field as touched.
1795
- */
1796
- markAsTouched(): void;
1797
- /**
1798
- * Marks this specific field as dirty.
1799
- */
1800
- markAsDirty(): void;
1801
- /**
1802
- * Resets the {@link touched} and {@link dirty} state of the field and its descendants.
1803
- *
1804
- * Note this does not change the data model, which can be reset directly if desired.
1805
- *
1806
- * @param value Optional value to set to the form. If not passed, the value will not be changed.
1807
- */
1808
- reset(value?: unknown): void;
1809
- private _reset;
1810
- /**
1811
- * Sets the control value of the field. This value may be debounced before it is synchronized with
1812
- * the field's {@link value} signal, depending on the debounce configuration.
1813
- */
1814
- setControlValue(newValue: unknown): void;
1815
- /**
1816
- * Synchronizes the {@link controlValue} with the {@link value} signal immediately.
1817
- */
1818
- private sync;
1819
- /**
1820
- * If there is a pending sync, abort it and sync immediately.
1821
- */
1822
- private flushSync;
1823
- /**
1824
- * Initiates a debounced {@link sync}.
1825
- *
1826
- * If a debouncer is configured, the synchronization will occur after the debouncer resolves. If
1827
- * no debouncer is configured, the synchronization happens immediately. If {@link setControlValue}
1828
- * is called again while a debounce is pending, the previous debounce operation is aborted in
1829
- * favor of the new one.
1830
- */
1831
- private debounceSync;
1832
- /**
1833
- * Creates a new root field node for a new form.
1834
- */
1835
- static newRoot<T>(fieldManager: FormFieldManager, value: WritableSignal<T>, pathNode: FieldPathNode, adapter: FieldAdapter): FieldNode;
1836
- createStructure(options: FieldNodeOptions): RootFieldNodeStructure | ChildFieldNodeStructure;
1837
- private newChild;
1838
- }
1839
- /**
1840
- * Field node of a field that has children.
1841
- * This simplifies and makes certain types cleaner.
1842
- */
1843
- interface ParentFieldNode extends FieldNode {
1844
- readonly value: WritableSignal<Record<string, unknown>>;
1845
- readonly structure: FieldNodeStructure & {
1846
- value: WritableSignal<Record<string, unknown>>;
1847
- };
1848
- }
1849
-
1850
- /**
1851
- * Key by which a parent `FieldNode` tracks its children.
1852
- *
1853
- * Often this is the actual property key of the child, but in the case of arrays it could be a
1854
- * tracking key allocated for the object.
1855
- */
1856
- type TrackingKey = PropertyKey & {
1857
- __brand: 'FieldIdentity';
1858
- };
1859
- type ChildNodeCtor = (key: string, trackingKey: TrackingKey | undefined, isArray: boolean) => FieldNode;
1860
- /** Structural component of a `FieldNode` which tracks its path, parent, and children. */
1861
- declare abstract class FieldNodeStructure {
1862
- /**
1863
- * Computed map of child fields, based on the current value of this field.
1864
- *
1865
- * This structure reacts to `this.value` and produces a new `ChildrenData` when the
1866
- * value changes structurally (fields added/removed/moved).
1867
- */
1868
- protected abstract readonly childrenMap: Signal<ChildrenData | undefined>;
1869
- /** The field's value. */
1870
- abstract readonly value: WritableSignal<unknown>;
1871
- /**
1872
- * The key of this field in its parent field.
1873
- * Attempting to read this for the root field will result in an error being thrown.
1874
- */
1875
- abstract readonly keyInParent: Signal<string>;
1876
- /** The field manager responsible for managing this field. */
1877
- abstract readonly fieldManager: FormFieldManager;
1878
- /** The root field that this field descends from. */
1879
- abstract readonly root: FieldNode;
1880
- /** The list of property keys to follow to get from the `root` to this field. */
1881
- abstract readonly pathKeys: Signal<readonly string[]>;
1882
- /** The parent field of this field. */
1883
- abstract readonly parent: FieldNode | undefined;
1884
- readonly logic: LogicNode;
1885
- readonly node: FieldNode;
1886
- readonly createChildNode: ChildNodeCtor;
1887
- /** Added to array elements for tracking purposes. */
1888
- readonly identitySymbol: symbol;
1889
- /** Lazily initialized injector. Do not access directly, access via `injector` getter instead. */
1890
- private _injector;
1891
- /** Lazily initialized injector. */
1892
- get injector(): DestroyableInjector;
1893
- constructor(logic: LogicNode, node: FieldNode, createChildNode: ChildNodeCtor);
1894
- /** Gets the child fields of this field. */
1895
- children(): readonly FieldNode[];
1896
- /** Retrieve a child `FieldNode` of this node by property key. */
1897
- getChild(key: PropertyKey): FieldNode | undefined;
1898
- /**
1899
- * Perform a reduction over a field's children (if any) and return the result.
1900
- *
1901
- * Optionally, the reduction is short circuited based on the provided `shortCircuit` function.
1902
- */
1903
- reduceChildren<T>(initialValue: T, fn: (child: FieldNode, value: T) => T, shortCircuit?: (value: T) => boolean): T;
1904
- /** Destroys the field when it is no longer needed. */
1905
- destroy(): void;
1906
- /**
1907
- * Creates a keyInParent signal for a field node.
1908
- *
1909
- * For root nodes, returns ROOT_KEY_IN_PARENT which throws when accessed.
1910
- * For child nodes, creates a computed that tracks the field's current key in its parent,
1911
- * with special handling for tracked array elements.
1912
- *
1913
- * @param options The field node options
1914
- * @param identityInParent The tracking identity (only for tracked array children)
1915
- * @param initialKeyInParent The initial key in parent (only for child nodes)
1916
- * @returns A signal representing the field's key in its parent
1917
- */
1918
- protected createKeyInParent(options: FieldNodeOptions, identityInParent: TrackingKey | undefined, initialKeyInParent: string | undefined): Signal<string>;
1919
- protected createChildrenMap(): Signal<ChildrenData | undefined>;
1920
- /**
1921
- * Creates a "reader" computed for the given key.
1922
- *
1923
- * A reader is a computed signal that memoizes the access of the `FieldNode` stored at this key
1924
- * (or returns `undefined` if no such field exists). Accessing fields via the reader ensures that
1925
- * reactive consumers aren't notified unless the field at a key actually changes.
1926
- */
1927
- private createReader;
1928
- }
1929
- /** The structural component of a `FieldNode` that is the root of its field tree. */
1930
- declare class RootFieldNodeStructure extends FieldNodeStructure {
1931
- readonly fieldManager: FormFieldManager;
1932
- readonly value: WritableSignal<unknown>;
1933
- get parent(): undefined;
1934
- get root(): FieldNode;
1935
- get pathKeys(): Signal<readonly string[]>;
1936
- get keyInParent(): Signal<string>;
1937
- protected readonly childrenMap: Signal<ChildrenData | undefined>;
1938
- /**
1939
- * Creates the structure for the root node of a field tree.
1940
- *
1941
- * @param node The full field node that this structure belongs to
1942
- * @param pathNode The path corresponding to this node in the schema
1943
- * @param logic The logic to apply to this field
1944
- * @param fieldManager The field manager for this field
1945
- * @param value The value signal for this field
1946
- * @param adapter Adapter that knows how to create new fields and appropriate state.
1947
- * @param createChildNode A factory function to create child nodes for this field.
1948
- */
1949
- constructor(
1950
- /** The full field node that corresponds to this structure. */
1951
- node: FieldNode, logic: LogicNode, fieldManager: FormFieldManager, value: WritableSignal<unknown>, createChildNode: ChildNodeCtor);
1952
- }
1953
- /** The structural component of a child `FieldNode` within a field tree. */
1954
- declare class ChildFieldNodeStructure extends FieldNodeStructure {
1955
- readonly logic: LogicNode;
1956
- readonly parent: ParentFieldNode;
1957
- readonly root: FieldNode;
1958
- readonly pathKeys: Signal<readonly string[]>;
1959
- readonly keyInParent: Signal<string>;
1960
- readonly value: WritableSignal<unknown>;
1961
- readonly childrenMap: Signal<ChildrenData | undefined>;
1962
- get fieldManager(): FormFieldManager;
1963
- /**
1964
- * Creates the structure for a child field node in a field tree.
1965
- *
1966
- * @param node The full field node that this structure belongs to
1967
- * @param pathNode The path corresponding to this node in the schema
1968
- * @param logic The logic to apply to this field
1969
- * @param parent The parent field node for this node
1970
- * @param identityInParent The identity used to track this field in its parent
1971
- * @param initialKeyInParent The key of this field in its parent at the time of creation
1972
- * @param adapter Adapter that knows how to create new fields and appropriate state.
1973
- * @param createChildNode A factory function to create child nodes for this field.
1974
- */
1975
- constructor(node: FieldNode, logic: LogicNode, parent: ParentFieldNode, identityInParent: TrackingKey | undefined, initialKeyInParent: string, createChildNode: ChildNodeCtor);
1976
- }
1977
- /** Options passed when constructing a root field node. */
1978
- interface RootFieldNodeOptions {
1979
- /** Kind of node, used to differentiate root node options from child node options. */
1980
- readonly kind: 'root';
1981
- /** The path node corresponding to this field in the schema. */
1982
- readonly pathNode: FieldPathNode;
1983
- /** The logic to apply to this field. */
1984
- readonly logic: LogicNode;
1985
- /** The value signal for this field. */
1986
- readonly value: WritableSignal<unknown>;
1987
- /** The field manager for this field. */
1988
- readonly fieldManager: FormFieldManager;
1989
- /** This allows for more granular field and state management, and is currently used for compat. */
1990
- readonly fieldAdapter: FieldAdapter;
1991
- }
1992
- /** Options passed when constructing a child field node. */
1993
- interface ChildFieldNodeOptions {
1994
- /** Kind of node, used to differentiate root node options from child node options. */
1995
- readonly kind: 'child';
1996
- /** The parent field node of this field. */
1997
- readonly parent: ParentFieldNode;
1998
- /** The path node corresponding to this field in the schema. */
1999
- readonly pathNode: FieldPathNode;
2000
- /** The logic to apply to this field. */
2001
- readonly logic: LogicNode;
2002
- /** The key of this field in its parent at the time of creation. */
2003
- readonly initialKeyInParent: string;
2004
- /** The identity used to track this field in its parent. */
2005
- readonly identityInParent: TrackingKey | undefined;
2006
- /** This allows for more granular field and state management, and is currently used for compat. */
2007
- readonly fieldAdapter: FieldAdapter;
2008
- }
2009
- /** Options passed when constructing a field node. */
2010
- type FieldNodeOptions = RootFieldNodeOptions | ChildFieldNodeOptions;
2011
- /**
2012
- * Derived data regarding child fields for a specific parent field.
2013
- */
2014
- interface ChildrenData {
2015
- /**
2016
- * Tracks `ChildData` for each property key within the parent.
2017
- */
2018
- readonly byPropertyKey: ReadonlyMap<string, ChildData>;
2019
- /**
2020
- * Tracks the instance of child `FieldNode`s by their tracking key, which is always 1:1 with the
2021
- * fields, even if they move around in the parent.
2022
- */
2023
- readonly byTrackingKey?: ReadonlyMap<TrackingKey, FieldNode>;
2024
- }
2025
- /**
2026
- * Data for a specific child within a parent.
2027
- */
2028
- interface ChildData {
2029
- /**
2030
- * A computed signal to access the `FieldNode` currently stored at a specific key.
2031
- *
2032
- * Because this is a computed, it only updates whenever the `FieldNode` at that key changes.
2033
- * Because `ChildData` is always associated with a specific key via `ChildrenData.byPropertyKey`,
2034
- * this computed gives a stable way to watch the field stored for a given property and only
2035
- * receives notifications when that field changes.
2036
- */
2037
- readonly reader: Signal<FieldNode | undefined>;
2038
- /**
2039
- * The child `FieldNode` currently stored at this key.
2040
- */
2041
- node: FieldNode;
2042
- }
2043
-
2044
- /**
2045
- * Manages the collection of fields associated with a given `form`.
2046
- *
2047
- * Fields are created implicitly, through reactivity, and may create "owned" entities like effects
2048
- * or resources. When a field is no longer connected to the form, these owned entities should be
2049
- * destroyed, which is the job of the `FormFieldManager`.
2050
- */
2051
- declare class FormFieldManager {
2052
- readonly injector: Injector;
2053
- readonly rootName: string;
2054
- constructor(injector: Injector, rootName: string | undefined);
2055
- /**
2056
- * Contains all child field structures that have been created as part of the current form.
2057
- * New child structures are automatically added when they are created.
2058
- * Structures are destroyed and removed when they are no longer reachable from the root.
2059
- */
2060
- readonly structures: Set<FieldNodeStructure>;
2061
- /**
2062
- * Creates an effect that runs when the form's structure changes and checks for structures that
2063
- * have become unreachable to clean up.
2064
- *
2065
- * For example, consider a form wrapped around the following model: `signal([0, 1, 2])`.
2066
- * This form would have 4 nodes as part of its structure tree.
2067
- * One structure for the root array, and one structure for each element of the array.
2068
- * Now imagine the data is updated: `model.set([0])`. In this case the structure for the first
2069
- * element can still be reached from the root, but the structures for the second and third
2070
- * elements are now orphaned and not connected to the root. Thus they will be destroyed.
2071
- *
2072
- * @param root The root field structure.
2073
- */
2074
- createFieldManagementEffect(root: FieldNodeStructure): void;
2075
- /**
2076
- * Collects all structures reachable from the given structure into the given set.
2077
- *
2078
- * @param structure The root structure
2079
- * @param liveStructures The set of reachable structures to populate
2080
- */
2081
- private markStructuresLive;
2082
- }
2083
-
2084
- /**
2085
- * Adapter allowing customization of the creation logic for a field and its associated
2086
- * structure and state.
2087
- */
2088
- interface FieldAdapter {
2089
- /**
2090
- * Creates a node structure.
2091
- * @param node
2092
- * @param options
2093
- */
2094
- createStructure(node: FieldNode, options: FieldNodeOptions): FieldNodeStructure;
2095
- /**
2096
- * Creates node validation state
2097
- * @param param
2098
- * @param options
2099
- */
2100
- createValidationState(param: FieldNode, options: FieldNodeOptions): ValidationState;
2101
- /**
2102
- * Creates node state.
2103
- * @param param
2104
- * @param options
2105
- */
2106
- createNodeState(param: FieldNode, options: FieldNodeOptions): FieldNodeState;
2107
- /**
2108
- * Creates a custom child node.
2109
- * @param options
2110
- */
2111
- newChild(options: ChildFieldNodeOptions): FieldNode;
2112
- /**
2113
- * Creates a custom root node.
2114
- * @param fieldManager
2115
- * @param model
2116
- * @param pathNode
2117
- * @param adapter
2118
- */
2119
- newRoot<TValue>(fieldManager: FormFieldManager, model: WritableSignal<TValue>, pathNode: FieldPathNode, adapter: FieldAdapter): FieldNode;
2120
- }
2121
-
2122
1360
  /**
2123
1361
  * Options that may be specified when creating a form.
2124
1362
  *
2125
1363
  * @category structure
2126
1364
  * @experimental 21.0.0
2127
1365
  */
2128
- interface FormOptions {
1366
+ interface FormOptions<TModel> {
2129
1367
  /**
2130
1368
  * The injector to use for dependency injection. If this is not provided, the injector for the
2131
1369
  * current [injection context](guide/di/dependency-injection-context), will be used.
2132
1370
  */
2133
1371
  injector?: Injector;
1372
+ /** The name of the root form, used in generating name attributes for the fields. */
2134
1373
  name?: string;
2135
- /**
2136
- * Adapter allows managing fields in a more flexible way.
2137
- * Currently this is used to support interop with reactive forms.
2138
- */
2139
- adapter?: FieldAdapter;
1374
+ /** Options that define how to handle form submission. */
1375
+ submission?: FormSubmitOptions<TModel>;
2140
1376
  }
2141
1377
  /**
2142
1378
  * Creates a form wrapped around the given model data. A form is represented as simply a `FieldTree`
@@ -2210,7 +1446,7 @@ declare function form<TModel>(model: WritableSignal<TModel>): FieldTree<TModel>;
2210
1446
  * @category structure
2211
1447
  * @experimental 21.0.0
2212
1448
  */
2213
- declare function form<TModel>(model: WritableSignal<TModel>, schemaOrOptions: SchemaOrSchemaFn<TModel> | FormOptions): FieldTree<TModel>;
1449
+ declare function form<TModel>(model: WritableSignal<TModel>, schemaOrOptions: SchemaOrSchemaFn<TModel> | FormOptions<TModel>): FieldTree<TModel>;
2214
1450
  /**
2215
1451
  * Creates a form wrapped around the given model data. A form is represented as simply a `FieldTree`
2216
1452
  * of the model data.
@@ -2254,7 +1490,7 @@ declare function form<TModel>(model: WritableSignal<TModel>, schemaOrOptions: Sc
2254
1490
  * @category structure
2255
1491
  * @experimental 21.0.0
2256
1492
  */
2257
- declare function form<TModel>(model: WritableSignal<TModel>, schema: SchemaOrSchemaFn<TModel>, options: FormOptions): FieldTree<TModel>;
1493
+ declare function form<TModel>(model: WritableSignal<TModel>, schema: SchemaOrSchemaFn<TModel>, options: FormOptions<TModel>): FieldTree<TModel>;
2258
1494
  /**
2259
1495
  * Applies a schema to each item of an array.
2260
1496
  *
@@ -2361,21 +1597,24 @@ declare function applyWhenValue<TValue>(path: SchemaPath<TValue>, predicate: (va
2361
1597
  * }
2362
1598
  *
2363
1599
  * const registrationForm = form(signal({username: 'god', password: ''}));
2364
- * submit(registrationForm, async (f) => {
2365
- * return registerNewUser(registrationForm);
1600
+ * submit(registrationForm, {
1601
+ * action: async (f) => {
1602
+ * return registerNewUser(registrationForm);
1603
+ * }
2366
1604
  * });
2367
1605
  * registrationForm.username().errors(); // [{kind: 'server', message: 'Username already taken'}]
2368
1606
  * ```
2369
1607
  *
2370
1608
  * @param form The field to submit.
2371
- * @param action An asynchronous action used to submit the field. The action may return submission
2372
- * errors.
1609
+ * @param options Options for the submission.
1610
+ * @returns Whether the submission was successful.
2373
1611
  * @template TModel The data type of the field being submitted.
2374
1612
  *
2375
1613
  * @category submission
2376
1614
  * @experimental 21.0.0
2377
1615
  */
2378
- declare function submit<TModel>(form: FieldTree<TModel>, action: (form: FieldTree<TModel>) => Promise<TreeValidationResult>): Promise<void>;
1616
+ declare function submit<TModel>(form: FieldTree<TModel>, options?: FormSubmitOptions<TModel>): Promise<boolean>;
1617
+ declare function submit<TModel>(form: FieldTree<TModel>, action: FormSubmitOptions<TModel>['action']): Promise<boolean>;
2379
1618
  /**
2380
1619
  * Creates a `Schema` that adds logic rules to a form.
2381
1620
  * @param fn A **non-reactive** function that sets up reactive logic rules for the form.
@@ -2387,5 +1626,5 @@ declare function submit<TModel>(form: FieldTree<TModel>, action: (form: FieldTre
2387
1626
  */
2388
1627
  declare function schema<TValue>(fn: SchemaFn<TValue>): Schema<TValue>;
2389
1628
 
2390
- export { EmailValidationError, FORM_FIELD, FormField, MAX, MAX_LENGTH, MIN, MIN_LENGTH, MaxLengthValidationError, MaxValidationError, MetadataKey, MetadataReducer, MinLengthValidationError, MinValidationError, NgValidationError, PATTERN, PathKind, PatternValidationError, REQUIRED, RequiredValidationError, SchemaPathRules, StandardSchemaValidationError, ValidationError, apply, applyEach, applyWhen, applyWhenValue, createManagedMetadataKey, createMetadataKey, emailError, form, maxError, maxLengthError, metadata, minError, minLengthError, patternError, provideSignalFormsConfig, requiredError, schema, standardSchemaError, submit };
2391
- export type { AsyncValidationResult, ChildFieldContext, CompatFieldState, CompatSchemaPath, Debouncer, DisabledReason, FieldContext, FieldState, FieldTree, FieldValidator, FormFieldBindingOptions, FormOptions, ItemFieldContext, ItemType, LogicFn, MaybeFieldTree, MaybeSchemaPathTree, MetadataSetterType, OneOrMany, ReadonlyArrayLike, RootFieldContext, Schema, SchemaFn, SchemaOrSchemaFn, SchemaPath, SchemaPathTree, SignalFormsConfig, Subfields, TreeValidationResult, TreeValidator, ValidationResult, ValidationSuccess, Validator, WithField, WithOptionalField, WithoutField };
1629
+ export { BaseNgValidationError, EmailValidationError, FORM_FIELD, FormField, MAX, MAX_LENGTH, MIN, MIN_LENGTH, MaxLengthValidationError, MaxValidationError, MetadataKey, MetadataReducer, MinLengthValidationError, MinValidationError, NgValidationError, PATTERN, PathKind, PatternValidationError, REQUIRED, RequiredValidationError, SchemaPathRules, StandardSchemaValidationError, ValidationError, apply, applyEach, applyWhen, applyWhenValue, createManagedMetadataKey, createMetadataKey, emailError, form, maxError, maxLengthError, metadata, minError, minLengthError, patternError, provideSignalFormsConfig, requiredError, schema, standardSchemaError, submit, validateStandardSchema, ɵNgFieldDirective };
1630
+ export type { AsyncValidationResult, ChildFieldContext, CompatFieldState, CompatSchemaPath, Debouncer, DisabledReason, FieldContext, FieldState, FieldTree, FieldValidator, FormFieldBindingOptions, FormOptions, FormSubmitOptions, IgnoreUnknownProperties, ItemFieldContext, ItemType, LogicFn, MaybeFieldTree, MaybeSchemaPathTree, MetadataSetterType, OneOrMany, ReadonlyArrayLike, RemoveStringIndexUnknownKey, RootFieldContext, Schema, SchemaFn, SchemaOrSchemaFn, SchemaPath, SchemaPathTree, SignalFormsConfig, Subfields, TreeValidationResult, TreeValidator, ValidationErrorOptions, ValidationResult, ValidationSuccess, Validator, WithField, WithFieldTree, WithOptionalField, WithOptionalFieldTree, WithoutField, WithoutFieldTree };