@angular/forms
Version:
Angular - directives and services for creating forms
1,244 lines (1,238 loc) • 75.7 kB
TypeScript
/**
* @license Angular v22.0.2
* (c) 2010-2026 Google LLC. https://angular.dev/
* License: MIT
*/
import * as i0 from '@angular/core';
import { Signal, Injector, WritableSignal, InjectionToken, Provider } from '@angular/core';
import { AbstractControl } from '@angular/forms';
import { StandardSchemaV1 } from '@standard-schema/spec';
/**
* Symbol used to retain generic type information when it would otherwise be lost.
*/
declare const ɵɵTYPE: unique symbol;
/**
* Options that can be specified when submitting a form.
*
* @publicApi 22.0
*/
interface FormSubmitOptions<TRootModel, TSubmittedModel> {
/**
* Function to run when submitting the form data (when form is valid).
*
* @param field The contextually relevant field for this action function (the root field when
* specified during form creation, and the submitted field when specified as part of the
* `submit()` call)
* @param detail An object containing the root field of the submitted form as well as the
* submitted field itself
*/
action: (field: FieldTree<TRootModel & TSubmittedModel>, detail: {
root: FieldTree<TRootModel>;
submitted: FieldTree<TSubmittedModel>;
}) => Promise<TreeValidationResult>;
/**
* Function to run when attempting to submit the form data but validation is failing.
*
* @param field The contextually relevant field for this onInvalid function (the root field when
* specified during form creation, and the submitted field when specified as part of the
* `submit()` call)
* @param detail An object containing the root field of the submitted form as well as the
* submitted field itself
*/
onInvalid?: (field: FieldTree<TRootModel & TSubmittedModel>, detail: {
root: FieldTree<TRootModel>;
submitted: FieldTree<TSubmittedModel>;
}) => void;
/**
* Whether to ignore any of the validators when submitting:
* - 'pending': Will submit if there are no invalid validators, pending validators do not block submission (default)
* - 'none': Will not submit unless all validators are passing, pending validators block submission
* - 'all': Will always submit regardless of invalid or pending validators
*/
ignoreValidators?: 'pending' | 'none' | 'all';
}
/**
* Options for the `markAsTouched` method.
*
* @publicApi 22.0
*/
interface MarkAsTouchedOptions {
/**
* If `true`, only marks the current field as touched.
* If `false` or not provided, marks the field and all its descendants as touched.
*/
skipDescendants?: boolean;
}
/**
* A type that represents either a single value of type `T` or a readonly array of `T`.
* @template T The type of the value(s).
*
* @publicApi 22.0
*/
type OneOrMany<T> = T | readonly T[];
/**
* The kind of `FieldPath` (`Root`, `Child` of another `FieldPath`, or `Item` in a `FieldPath` array)
*
* @publicApi 22.0
*/
type PathKind = PathKind.Root | PathKind.Child | PathKind.Item;
declare namespace PathKind {
/**
* The `PathKind` for a `FieldPath` that is at the root of its field tree.
*/
interface Root {
/**
* The `ɵɵTYPE` is constructed to allow the `extends` clause on `Child` and `Item` to narrow the
* type. Another way to think about this is, if we have a function that expects this kind of
* path, the `ɵɵTYPE` lists the kinds of path we are allowed to pass to it.
*/
[ɵɵTYPE]: 'root' | 'child' | 'item';
}
/**
* The `PathKind` for a `FieldPath` that is a child of another `FieldPath`.
*/
interface Child extends PathKind.Root {
[ɵɵTYPE]: 'child' | 'item';
}
/**
* The `PathKind` for a `FieldPath` that is an item in a `FieldPath` array.
*/
interface Item extends PathKind.Child {
[ɵɵTYPE]: 'item';
}
}
/**
* A reason for a field's disablement.
*
* @category logic
* @publicApi 22.0
*/
interface DisabledReason {
/** The field that is disabled. */
readonly fieldTree: ReadonlyFieldTree<unknown>;
/** A user-facing message describing the reason for the disablement. */
readonly message?: string;
}
/**
* The absence of an error which indicates a successful validation result.
*
* @category types
* @publicApi 22.0
*/
type ValidationSuccess = null | undefined | void;
/**
* The result of running a tree validation function.
*
* The result may be one of the following:
* 1. A {@link ValidationSuccess} to indicate no errors.
* 2. A {@link ValidationError} without a field to indicate an error on the field being validated.
* 3. A {@link ValidationError} with a field to indicate an error on the target field.
* 4. A list of {@link ValidationError} with or without fields to indicate multiple errors.
*
* @template E the type of error (defaults to {@link ValidationError}).
*
* @category types
* @publicApi 22.0
*/
type TreeValidationResult<E extends ValidationError.WithOptionalFieldTree = ValidationError.WithOptionalFieldTree> = ValidationSuccess | OneOrMany<E>;
/**
* A validation result where all errors explicitly define their target field.
*
* The result may be one of the following:
* 1. A {@link ValidationSuccess} to indicate no errors.
* 2. A {@link ValidationError} with a field to indicate an error on the target field.
* 3. A list of {@link ValidationError} with fields to indicate multiple errors.
*
* @template E the type of error (defaults to {@link ValidationError}).
*
* @category types
* @publicApi 22.0
*/
type ValidationResult<E extends ValidationError = ValidationError> = ValidationSuccess | OneOrMany<E>;
/**
* An asynchronous validation result where all errors explicitly define their target field.
*
* The result may be one of the following:
* 1. A {@link ValidationResult} to indicate the result if resolved.
* 5. 'pending' if the validation is not yet resolved.
*
* @template E the type of error (defaults to {@link ValidationError}).
*
* @category types
* @publicApi 22.0
*/
type AsyncValidationResult<E extends ValidationError = ValidationError> = ValidationResult<E> | 'pending';
/**
* A field accessor function that returns the state of the field.
*
* @template TValue The type of the value stored in the field.
* @template TKey The type of the property key which this field resides under in its parent.
*
* @category types
* @publicApi 22.0
*/
type Field<TValue, TKey extends string | number = string | number> = () => FieldState<TValue, TKey>;
/**
* An object that represents a tree of fields in a form. This includes both primitive value fields
* (e.g. fields that contain a `string` or `number`), as well as "grouping fields" that contain
* sub-fields. `FieldTree` objects are arranged in a tree whose structure mimics the structure of
* the underlying data. For example a `FieldTree<{x: number}>` has a property `x` which contains a
* `FieldTree<number>`. To access the state associated with a field, call it as a function.
*
* @template TValue The type of the data which the field is wrapped around.
* @template TKey The type of the property key which this field resides under in its parent.
* @template TMode Determines whether the field state is readonly or writable. Defaults to writable.
* For readonly, use {@link ReadonlyFieldTree}.
*
* @category types
* @publicApi 22.0
*/
type FieldTree<TModel, TKey extends string | number = string | number, TMode extends 'writable' | 'readonly' = 'writable'> = (() => [TModel] extends [AbstractControl] ? CompatFieldState<TModel, TKey, TMode> : FieldStateByMode<TModel, TKey, TMode>) & (TModel extends AbstractControl ? object : TModel extends ReadonlyArray<infer U> ? ReadonlyArrayLike<MaybeFieldTree<U, number, TMode>> : TModel extends Record<string, any> ? Subfields<TModel, TMode> : object);
/**
* A readonly {@link FieldTree}.
*
* @category types
* @publicApi 22.0
*/
type ReadonlyFieldTree<TModel, TKey extends string | number = string | number> = FieldTree<TModel, TKey, 'readonly'>;
/**
* The sub-fields that a user can navigate to from a `FieldTree<TModel>`.
*
* @template TModel The type of the data which the parent field is wrapped around.
* @template TMode Determines whether the field state is readonly or writable.
*
* @publicApi 22.0
*/
type Subfields<TModel, TMode extends 'writable' | 'readonly' = 'writable'> = {
readonly [K in keyof TModel as TModel[K] extends Function ? never : K]: MaybeFieldTree<TModel[K], string, TMode>;
} & {
[Symbol.iterator](): Iterator<[string, MaybeFieldTree<TModel[keyof TModel], string, TMode>]>;
};
/**
* An iterable object with the same shape as a readonly array.
*
* @template T The array item type.
*
* @publicApi 22.0
*/
interface ReadonlyArrayLike<T> {
readonly [n: number]: T;
readonly length: number;
[Symbol.iterator](): IterableIterator<T>;
}
/**
* Helper type for defining `FieldTree`. Given a type `TValue` that may include `undefined`,
* it extracts the `undefined` outside the `FieldTree` type.
*
* For example `MaybeFieldTree<{a: number} | undefined, TKey>` would be equivalent to
* `undefined | FieldTree<{a: number}, TKey>`.
*
* @template TModel The type of the data which the field is wrapped around.
* @template TKey The type of the property key which this field resides under in its parent.
* @template TMode Determines whether the field state is readonly or writable.
*
* @publicApi 22.0
*/
type MaybeFieldTree<TModel, TKey extends string | number = string | number, TMode extends 'writable' | 'readonly' = 'writable'> = (TModel & undefined) | FieldTree<Exclude<TModel, undefined>, TKey, TMode>;
/**
* A readonly view of a {@link FieldTree}'s state.
*
* @template TValue The type of the data which the field is wrapped around.
* @template TKey The type of the property key which this field resides under in its parent.
*
* @category structure
* @publicApi 22.0
*/
interface ReadonlyFieldState<TValue, TKey extends string | number = string | number> {
/**
* The {@link FieldTree} associated with this field state.
*/
readonly fieldTree: ReadonlyFieldTree<unknown, TKey>;
/**
* A writable signal containing the value for this field.
*
* Updating this signal will update the data model that the field is bound to.
*
* While updates from the UI control are eventually reflected here, they may be delayed if
* debounced.
*/
readonly value: Signal<TValue>;
/**
* A signal containing the value of the control to which this field is bound.
*
* This differs from {@link value} in that it's not subject to debouncing, and thus is used to
* buffer debounced updates from the control to the field. This will also not take into account
* the {@link controlValue} of children.
*/
readonly controlValue: Signal<TValue>;
/**
* A signal indicating whether the field is currently disabled.
*/
readonly disabled: Signal<boolean>;
/**
* A signal indicating the field's maximum value, if applicable.
*
* Applies to `<input>` with a numeric or date `type` attribute and custom controls.
*/
readonly max: Signal<NonNullable<TValue> | undefined> | undefined;
/**
* A signal indicating the field's maximum string length, if applicable.
*
* Applies to `<input>`, `<textarea>`, and custom controls.
*/
readonly maxLength: Signal<number | undefined> | undefined;
/**
* A signal indicating the field's minimum value, if applicable.
*
* Applies to `<input>` with a numeric or date `type` attribute and custom controls.
*/
readonly min: Signal<NonNullable<TValue> | undefined> | undefined;
/**
* A signal indicating the field's minimum string length, if applicable.
*
* Applies to `<input>`, `<textarea>`, and custom controls.
*/
readonly minLength: Signal<number | undefined> | undefined;
/**
* A signal of a unique name for the field, by default based on the name of its parent field.
*/
readonly name: Signal<string>;
/**
* A signal indicating the patterns the field must match.
*/
readonly pattern: Signal<readonly RegExp[]>;
/**
* A signal indicating whether the field is currently readonly.
*/
readonly readonly: Signal<boolean>;
/**
* A signal indicating whether the field is required.
*/
readonly required: Signal<boolean>;
/**
* A signal indicating whether the field has been touched by the user.
*/
readonly touched: Signal<boolean>;
/**
* A signal indicating whether field value has been changed by user.
*/
readonly dirty: Signal<boolean>;
/**
* A signal indicating whether a field is hidden.
*
* When a field is hidden it is ignored when determining the valid, touched, and dirty states.
*
* Note: This doesn't hide the field in the template, that must be done manually.
* ```
* @if (!field.hidden()) {
* ...
* }
* ```
*/
readonly hidden: Signal<boolean>;
readonly disabledReasons: Signal<readonly DisabledReason[]>;
readonly errors: Signal<ValidationError.WithFieldTree[]>;
/**
* A signal containing the {@link errors} of the field and its descendants.
*/
readonly errorSummary: Signal<ValidationError.WithFieldTree[]>;
/**
* A signal indicating whether the field's value is currently valid.
*
* Note: `valid()` is not the same as `!invalid()`.
* - `valid()` is `true` when there are no validation errors *and* no pending validators.
* - `invalid()` is `true` when there are validation errors, regardless of pending validators.
*
* Ex: consider the situation where a field has 3 validators, 2 of which have no errors and 1 of
* which is still pending. In this case `valid()` is `false` because of the pending validator.
* However `invalid()` is also `false` because there are no errors.
*/
readonly valid: Signal<boolean>;
/**
* A signal indicating whether the field's value is currently invalid.
*
* Note: `invalid()` is not the same as `!valid()`.
* - `invalid()` is `true` when there are validation errors, regardless of pending validators.
* - `valid()` is `true` when there are no validation errors *and* no pending validators.
*
* Ex: consider the situation where a field has 3 validators, 2 of which have no errors and 1 of
* which is still pending. In this case `invalid()` is `false` because there are no errors.
* However `valid()` is also `false` because of the pending validator.
*/
readonly invalid: Signal<boolean>;
/**
* Whether there are any validators still pending for this field.
*/
readonly pending: Signal<boolean>;
/**
* A signal indicating whether the field is currently in the process of being submitted.
*/
readonly submitting: Signal<boolean>;
/**
* The property key in the parent field under which this field is stored. If the parent field is
* array-valued, for example, this is the index of this field in that array.
*/
readonly keyInParent: Signal<TKey>;
/**
* The {@link FormField} directives that bind this field to a UI control.
*/
readonly formFieldBindings: Signal<readonly FormFieldBinding[]>;
/**
* Reads a metadata value from the field.
* @param key The metadata key to read.
*/
metadata<M>(key: MetadataKey<M, any, any>): M | undefined;
/**
* Checks whether a metadata value exists on the field.
* @param key The metadata key to check.
*/
hasMetadata(key: MetadataKey<any, any, any>): boolean;
/**
* Focuses the first UI control in the DOM that is bound to this field state.
* If no UI control is bound, does nothing.
* @param options Optional focus options to pass to the native focus() method.
*/
focusBoundControl(options?: FocusOptions): void;
}
/**
* A writable view of a {@link FieldTree}'s state.
*
* @template TValue The type of the data which the field is wrapped around.
* @template TKey The type of the property key which this field resides under in its parent.
*
* @category structure
* @publicApi 22.0
*/
interface FieldState<TValue, TKey extends string | number = string | number> extends ReadonlyFieldState<TValue, TKey> {
/**
* The {@link FieldTree} associated with this field state.
*/
readonly fieldTree: FieldTree<unknown, TKey>;
/**
* A writable signal containing the value for this field.
*
* Updating this signal will update the data model that the field is bound to.
*
* While updates from the UI control are eventually reflected here, they may be delayed if
* debounced.
*/
readonly value: WritableSignal<TValue>;
/**
* A signal containing the value of the control to which this field is bound.
*
* This differs from {@link value} in that it's not subject to debouncing, and thus is used to
* buffer debounced updates from the control to the field. This will also not take into account
* the {@link controlValue} of children.
*/
readonly controlValue: WritableSignal<TValue>;
/**
* Sets the dirty status of the field to `true`.
*/
markAsDirty(): void;
/**
* Sets the touched status of the field and its descendants to `true`.
*
* @param options Options for marking the field as touched.
*/
markAsTouched(options?: MarkAsTouchedOptions): void;
/**
* Gets the first validation error of the given kind on this field.
*
* This method is reactive and will re-evaluate when the field's errors change if called
* within a reactive context (e.g. `computed` or `effect`).
*
* @param kind The kind of error (e.g. 'required', 'min').
* @returns The first matching error, or `undefined` if none.
*/
getError<K extends NgValidationError['kind']>(kind: K): (Extract<NgValidationError, {
kind: K;
}> & ValidationError.WithFieldTree) | undefined;
getError(kind: string): ValidationError.WithFieldTree | undefined;
/**
* Resets the {@link touched} and {@link dirty} state of the field and its descendants.
*
* Note this does not change the data model, which can be reset directly if desired.
*
* @param value Optional value to set to the form. If not passed, the value will not be changed.
*/
reset(value?: TValue): void;
/**
* Reloads all asynchronous validators for this field and its descendants.
*/
reloadValidation(): void;
}
/**
* This is FieldState also providing access to the wrapped FormControl.
*
* @category interop
* @publicApi 22.0
*/
type CompatFieldState<TControl extends AbstractControl, TKey extends string | number = string | number, TMode extends 'writable' | 'readonly' = 'writable'> = FieldStateByMode<TControl extends AbstractControl<unknown, infer TValue> ? TValue : never, TKey, TMode> & {
control: Signal<TControl>;
};
/**
* A readonly {@link CompatFieldState}.
*
* @category interop
* @publicApi 22.0
*/
type ReadonlyCompatFieldState<TControl extends AbstractControl, TKey extends string | number = string | number> = CompatFieldState<TControl, TKey, 'readonly'>;
/**
* Helper type that resolves to either a {@link FieldState} or {@link ReadonlyFieldState} based on
* the access mode.
*
* @template TValue The type of the value stored in the field.
* @template TKey The type of the property key.
* @template TMode The access mode ('readonly' or 'writable').
*/
type FieldStateByMode<TValue, TKey extends string | number, TMode extends 'writable' | 'readonly'> = TMode extends 'writable' ? FieldState<TValue, TKey> : ReadonlyFieldState<TValue, TKey>;
/**
* Represents a binding between a field and a UI control through a {@link FormField} directive.
*
* @publicApi 22.0
*/
interface FormFieldBinding {
/**
* The HTML element on which the {@link FormField} directive is applied.
*/
readonly element: HTMLElement;
/**
* The node injector for the element hosting this field binding.
*/
readonly injector: Injector;
/**
* The {@link FieldState} of the field bound to the {@link FormField} directive.
*/
readonly state: Signal<ReadonlyFieldState<unknown>>;
/**
* Focuses this field binding.
*
* By default, this will focus {@link element}. However, custom controls can implement their own
* focus behavior.
*/
focus(options?: FocusOptions): void;
}
/**
* Allows declaring whether the Rules are supported for a given path.
*
* @publicApi 22.0
**/
type SchemaPathRules = SchemaPathRules.Supported | SchemaPathRules.Unsupported;
declare namespace SchemaPathRules {
/**
* Used for paths that support settings rules.
*/
type Supported = 1;
/**
* Used for paths that do not support settings rules, e.g., compatPath.
*/
type Unsupported = 2;
}
/**
* An object that represents a location in the `FieldTree` tree structure and is used to bind logic to a
* particular part of the structure prior to the creation of the form. Because the `FieldPath`
* exists prior to the form's creation, it cannot be used to access any of the field state.
*
* @template TValue The type of the data which the form is wrapped around.
* @template TPathKind The kind of path (root field, child field, or item of an array)
*
* @category types
* @publicApi 22.0
*/
type SchemaPath<TValue, TSupportsRules extends SchemaPathRules = SchemaPathRules.Supported, TPathKind extends PathKind = PathKind.Root> = {
[ɵɵTYPE]: {
value: () => TValue;
supportsRules: TSupportsRules;
pathKind: TPathKind;
};
};
/**
* Schema path used if the value is an AbstractControl.
*
* @category interop
* @publicApi 22.0
*/
type CompatSchemaPath<TControl extends AbstractControl, TPathKind extends PathKind = PathKind.Root> = SchemaPath<TControl extends AbstractControl<unknown, infer TValue> ? TValue : never, SchemaPathRules.Unsupported, TPathKind> & {
[ɵɵTYPE]: {
control: TControl;
};
};
/**
* Nested schema path.
*
* It mirrors the structure of a given data structure, and allows applying rules to the appropriate
* fields.
*
* @publicApi 22.0
*/
type SchemaPathTree<TModel, TPathKind extends PathKind = PathKind.Root> = ([TModel] extends [AbstractControl] ? CompatSchemaPath<TModel, TPathKind> : SchemaPath<TModel, SchemaPathRules.Supported, TPathKind>) & ([TModel] extends [AbstractControl] ? unknown : [
TModel
] extends [ReadonlyArray<any>] ? unknown : TModel extends Record<string, any> ? {
[K in keyof TModel]: MaybeSchemaPathTree<TModel[K], PathKind.Child>;
} : unknown);
/**
* Helper type for defining `FieldPath`. Given a type `TValue` that may include `undefined`, it
* extracts the `undefined` outside the `FieldPath` type.
*
* For example `MaybeFieldPath<{a: number} | undefined, PathKind.Child>` would be equivalent to
* `undefined | FieldTree<{a: number}, PathKind.child>`.
*
* @template TValue The type of the data which the field is wrapped around.
* @template TPathKind The kind of path (root field, child field, or item of an array)
*
* @publicApi 22.0
*/
type MaybeSchemaPathTree<TModel, TPathKind extends PathKind = PathKind.Root> = (TModel & undefined) | SchemaPathTree<Exclude<TModel, undefined>, TPathKind>;
/**
* A reusable schema that defines behavior and rules for a form.
*
* A `Schema` encapsulates form logic such as validation rules, disabled states, readonly states,
* and other field-level behaviors.
*
* Unlike raw {@link SchemaFn}, a `Schema` is created using
* the {@link schema} function and is cached per-form, even when applied to multiple fields.
*
* ### Creating a reusable schema
*
* ```typescript
* interface Address {
* street: string;
* city: string;
* }
*
* // Create a reusable schema for address fields
* const addressSchema = schema<Address>((p) => {
* required(p.street);
* required(p.city);
* });
*
* // Apply the schema to multiple forms
* const shippingForm = form(shippingModel, addressSchema, {injector});
* const billingForm = form(billingModel, addressSchema, {injector});
* ```
*
* ### Passing a schema to a form
*
* A schema can also be passed as a second argument to the {@link form} function.
*
* ```typescript
* readonly userForm = form(addressModel, addressSchema);
* ```
*
* @template TModel Data type.
*
* @category types
* @publicApi 22.0
*/
type Schema<in TModel> = {
[ɵɵTYPE]: SchemaFn<TModel, PathKind.Root>;
};
/**
* A function that receives a {@link SchemaPathTree} and applies rules to fields.
*
* A `SchemaFn` can be passed directly to {@link form} or to the {@link schema} function to create a
* cached {@link Schema}.
*
* ```typescript
* const userFormSchema: SchemaFn<User> = (p) => {
* required(p.name);
* disabled(p.email, ({valueOf}) => valueOf(p.name) === '');
* };
*
* const f = form(userModel, userFormSchema, {injector});
* ```
*
* @template TModel Data type.
* @template TPathKind The kind of path this schema function can be bound to.
*
* @category types
* @publicApi 22.0
*/
type SchemaFn<TModel, TPathKind extends PathKind = PathKind.Root> = (p: SchemaPathTree<TModel, TPathKind>) => void;
/**
* A {@link Schema} or {@link SchemaFn}.
*
* @template TModel The type of data stored in the form that this schema function is attached to.
* @template TPathKind The kind of path this schema function can be bound to.
*
* @category types
* @publicApi 22.0
*/
type SchemaOrSchemaFn<TModel, TPathKind extends PathKind = PathKind.Root> = Schema<TModel> | SchemaFn<TModel, TPathKind>;
/**
* A function that receives the `FieldContext` for the field the logic is bound to and returns
* a specific result type.
*
* @template TValue The data type for the field the logic is bound to.
* @template TReturn The type of the result returned by the logic function.
* @template TPathKind The kind of path the logic is applied to (root field, child field, or item of an array)
*
* @category types
* @publicApi 22.0
*/
type LogicFn<TValue, TReturn, TPathKind extends PathKind = PathKind.Root> = (ctx: FieldContext<TValue, TPathKind>) => TReturn;
/**
* A function that takes the `FieldContext` for the field being validated and returns a
* `ValidationResult` indicating errors for the field.
*
* @template TValue The type of value stored in the field being validated
* @template TPathKind The kind of path being validated (root field, child field, or item of an array)
*
* @category validation
* @publicApi 22.0
*/
type FieldValidator<TValue, TPathKind extends PathKind = PathKind.Root> = LogicFn<TValue, ValidationResult<ValidationError.WithoutFieldTree>, TPathKind>;
/**
* A function that takes the `FieldContext` for the field being validated and returns a
* `TreeValidationResult` indicating errors for the field and its sub-fields.
*
* @template TValue The type of value stored in the field being validated
* @template TPathKind The kind of path being validated (root field, child field, or item of an array)
*
* @category types
* @publicApi 22.0
*/
type TreeValidator<TValue, TPathKind extends PathKind = PathKind.Root> = LogicFn<TValue, TreeValidationResult, TPathKind>;
/**
* A function that takes the `FieldContext` for the field being validated and returns a
* `ValidationResult` indicating errors for the field and its sub-fields. In a `Validator` all
* errors must explicitly define their target field.
*
* @template TValue The type of value stored in the field being validated
* @template TPathKind The kind of path being validated (root field, child field, or item of an array)
* @see [Signal Form Validation](/guide/forms/signals/validation)
* @category types
* @publicApi 22.0
*/
type Validator<TValue, TPathKind extends PathKind = PathKind.Root> = LogicFn<TValue, ValidationResult, TPathKind>;
/**
* Provides access to the state of the current field as well as functions that can be used to look
* up state of other fields based on a `FieldPath`.
*
* @category types
* @publicApi 22.0
*/
type FieldContext<TValue, TPathKind extends PathKind = PathKind.Root> = TPathKind extends PathKind.Item ? ItemFieldContext<TValue> : TPathKind extends PathKind.Child ? ChildFieldContext<TValue> : RootFieldContext<TValue>;
/**
* The base field context that is available for all fields.
*
* @publicApi 22.0
*/
interface RootFieldContext<TValue> {
/** A signal containing the value of the current field. */
readonly value: Signal<TValue>;
/** The state of the current field. */
readonly state: ReadonlyFieldState<TValue>;
/** The current field. */
readonly fieldTree: ReadonlyFieldTree<TValue>;
/** Gets the value of the field represented by the given path. */
valueOf<PValue>(p: SchemaPath<PValue, SchemaPathRules>): PValue;
/** Gets the state of the field represented by the given path. */
stateOf<PControl extends AbstractControl>(p: CompatSchemaPath<PControl>): [PControl] extends [any] ? ReadonlyCompatFieldState<PControl> : never;
stateOf<PValue>(p: SchemaPath<PValue, SchemaPathRules>): [PValue] extends [any] ? ReadonlyFieldState<PValue> : never;
/** Gets the field represented by the given path. */
fieldTreeOf<PModel>(p: SchemaPathTree<PModel>): [PModel] extends [any] ? ReadonlyFieldTree<PModel> : never;
/** The list of keys that lead from the root field to the current field. */
readonly pathKeys: Signal<readonly string[]>;
}
/**
* Field context that is available for all fields that are a child of another field.
*
* @category structure
* @publicApi 22.0
*/
interface ChildFieldContext<TValue> extends RootFieldContext<TValue> {
/** The key of the current field in its parent field. */
readonly key: Signal<string>;
}
/**
* Field context that is available for all fields that are an item in an array field.
*
* @publicApi 22.0
*/
interface ItemFieldContext<TValue> extends ChildFieldContext<TValue> {
/** The index of the current field in its parent field. */
readonly index: Signal<number>;
}
/**
* Gets the item type of an object that is possibly an array.
*
* @publicApi 22.0
*/
type ItemType<T extends Object> = T extends ReadonlyArray<any> ? T[number] : T[keyof T];
/**
* A function that defines custom debounce logic for a field.
*
* @param context The field context.
* @param abortSignal An `AbortSignal` used to communicate that the debounced operation was aborted.
* @returns A `Promise<void>` to debounce an update, or `void` to apply an update immediately.
* @template TValue The type of value stored in the field.
* @template TPathKind The kind of path the debouncer is applied to (root field, child field, or item of an array).
*
* @publicApi 22.0
*/
type Debouncer<TValue, TPathKind extends PathKind = PathKind.Root> = (context: FieldContext<TValue, TPathKind>, abortSignal: AbortSignal) => Promise<void> | void;
/**
* Sets a value for the {@link MetadataKey} for this field.
*
* This value is combined via a reduce operation defined by the particular key,
* since multiple rules in the schema might set values for it.
*
* @param path The target path to set the metadata for.
* @param key The metadata key
* @param logic A function that receives the `FieldContext` and returns a value for the metadata.
* @template TValue The type of value stored in the field the logic is bound to.
* @template TKey The type of metadata key.
* @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array)
*
* @category logic
* @publicApi 22.0
*/
declare function metadata<TValue, TKey extends MetadataKey<any, any, any>, TPathKind extends PathKind = PathKind.Root>(path: SchemaPath<TValue, SchemaPathRules.Supported, TPathKind>, key: TKey, logic: NoInfer<LogicFn<TValue, TKey extends LimitSelectionKey ? LimitKey<TValue> : MetadataSetterType<TKey>, TPathKind>>): TKey;
/**
* A reducer that determines the accumulated value for a metadata key by reducing the individual
* values contributed from `metadata()` rules.
*
* @template TAcc The accumulated type of the reduce operation.
* @template TItem The type of the individual items that are reduced over.
* @publicApi 22.0
*/
interface MetadataReducer<TAcc, TItem> {
/** The reduce function. */
reduce: (acc: TAcc, item: TItem) => TAcc;
/** Gets the initial accumulated value. */
getInitial: () => TAcc;
}
declare const MetadataReducer: {
/** Creates a reducer that accumulates a list of its individual item values. */
readonly list: <TItem>() => MetadataReducer<TItem[], TItem | undefined>;
/** Creates a reducer that accumulates the min of its individual item values. */
readonly min: <T extends Date | number>() => MetadataReducer<T | undefined, T | undefined>;
/** Creates a reducer that accumulates a the max of its individual item values. */
readonly max: <T extends Date | number>() => MetadataReducer<T | undefined, T | undefined>;
/** Creates a reducer that logically or's its accumulated value with each individual item value. */
readonly or: () => MetadataReducer<boolean, boolean>;
/** Creates a reducer that logically and's its accumulated value with each individual item value. */
readonly and: () => MetadataReducer<boolean, boolean>;
/** Creates a reducer that always takes the next individual item value as the accumulated value. */
readonly override: typeof override;
};
declare function override<T>(): MetadataReducer<T | undefined, T>;
declare function override<T>(getInitial: () => T): MetadataReducer<T, T>;
/**
* A symbol used to tag a `MetadataKey` as representing an asynchronous validation resource.
*
* @category validation
* @publicApi 22.0
*/
declare const IS_ASYNC_VALIDATION_RESOURCE: unique symbol;
/**
* Represents metadata that is aggregated from multiple parts according to the key's reducer
* function. A value can be contributed to the aggregated value for a field using an
* `metadata` rule in the schema. There may be multiple rules in a schema that contribute
* values to the same `MetadataKey` of the same field.
*
* @template TRead The type read from the `FieldState` for this key
* @template TWrite The type written to this key using the `metadata()` rule
* @template TAcc The type of the reducer's accumulated value.
*
* @publicApi 22.0
*/
declare class MetadataKey<TRead, TWrite, TAcc> {
readonly reducer: MetadataReducer<TAcc, TWrite>;
readonly create: ((state: FieldState<unknown>, data: Signal<TAcc>) => TRead) | undefined;
private brand;
/** Use {@link createMetadataKey}. */
protected constructor(reducer: MetadataReducer<TAcc, TWrite>, create: ((state: FieldState<unknown>, data: Signal<TAcc>) => TRead) | undefined);
}
/**
* Represents metadata that is used to define a valid limit for a field.
*
* @template TLimit The type the limit value.
* @publicApi 22.0
*/
type LimitKey<TLimit> = MetadataKey<Signal<NonNullable<TLimit> | undefined>, NonNullable<TLimit> | undefined, NonNullable<TLimit> | undefined>;
/**
* A symbol used to tag a `MetadataKey` as representing a limit selection key.
*/
declare const LIMIT_SELECTION_KEY: unique symbol;
/**
* Used to select a {@link LimitKey}.
*
* This indirection allows rules to bind a {@link LimitKey} of a specific limit type (e.g. `number`
* or `Date`) matching the field's type to a generic {@link MetadataKey}.
*
* @publicApi 22.0
*/
type LimitSelectionKey = MetadataKey<Signal<LimitKey<unknown> | undefined>, LimitKey<unknown>, LimitKey<unknown> | undefined> & {
[LIMIT_SELECTION_KEY]: true;
};
/**
* Extracts the the type that can be set into the given metadata key type using the `metadata()` rule.
*
* @template TKey The `MetadataKey` type
*
* @publicApi 22.0
*/
type MetadataSetterType<TKey> = TKey extends MetadataKey<any, infer TWrite, any> ? TWrite : never;
/**
* Creates a metadata key used to contain a computed value.
* The last value set on a given field tree node overrides any previously set values.
*
* @template TWrite The type written to this key using the `metadata()` rule
*
* @publicApi 22.0
*/
declare function createMetadataKey<TWrite>(): MetadataKey<Signal<TWrite | undefined>, TWrite, TWrite | undefined>;
/**
* Creates a metadata key used to contain a computed value.
*
* @param reducer The reducer used to combine individually set values into the final computed value.
* @template TWrite The type written to this key using the `metadata()` rule
* @template TAcc The type of the reducer's accumulated value.
*
* @publicApi 22.0
*/
declare function createMetadataKey<TWrite, TAcc>(reducer: MetadataReducer<TAcc, TWrite>): MetadataKey<Signal<TAcc>, TWrite, TAcc>;
/**
* Creates a metadata key that exposes a managed value based on the accumulated result of the values
* written to the key. The accumulated value takes the last value set on a given field tree node,
* overriding any previously set values.
*
* @param create A function that receives a signal of the accumulated value and returns the managed
* value based on it. This function runs during the construction of the `FieldTree` node,
* and runs in the injection context of that node.
* @template TRead The type read from the `FieldState` for this key
* @template TWrite The type written to this key using the `metadata()` rule
*
* @publicApi 22.0
*/
declare function createManagedMetadataKey<TRead, TWrite>(create: (state: FieldState<unknown>, data: Signal<TWrite | undefined>) => TRead): MetadataKey<TRead, TWrite, TWrite | undefined>;
/**
* Creates a metadata key that exposes a managed value based on the accumulated result of the values
* written to the key.
*
* @param create A function that receives a signal of the accumulated value and returns the managed
* value based on it. This function runs during the construction of the `FieldTree` node,
* and runs in the injection context of that node.
* @param reducer The reducer used to combine individual value written to the key,
* this will determine the accumulated value that the create function receives.
* @template TRead The type read from the `FieldState` for this key
* @template TWrite The type written to this key using the `metadata()` rule
* @template TAcc The type of the reducer's accumulated value.
*
* @publicApi 22.0
*/
declare function createManagedMetadataKey<TRead, TWrite, TAcc>(create: (state: FieldState<unknown>, data: Signal<TAcc>) => TRead, reducer: MetadataReducer<TAcc, TWrite>): MetadataKey<TRead, TWrite, TAcc>;
/**
* Creates a {@link LimitSelectionKey}.
*
* @publicApi 22.0
*/
declare function createLimitSelectionKey(): LimitSelectionKey;
/**
* A {@link MetadataKey} representing whether the field is required.
*
* @category validation
* @publicApi 22.0
*/
declare const REQUIRED: MetadataKey<Signal<boolean>, boolean, boolean>;
/**
* A {@link MetadataKey} that points to another key determining the minimum value of the field.
*
* This indirection allows different keys to be used for different types of values with their
* own reducers, such as {@link MIN_DATE} and {@link MIN_NUMBER}.
*
* @category validation
* @publicApi 22.0
*/
declare const MIN: LimitSelectionKey;
/**
* A {@link MetadataKey} representing the minimum valid value of a date field.
*
* @category validation
* @publicApi 22.0
*/
declare const MIN_DATE: LimitKey<Date>;
/**
* A {@link MetadataKey} representing the minimum valid value of a number field.
*
* @category validation
* @publicApi 22.0
*/
declare const MIN_NUMBER: LimitKey<number>;
/**
* A {@link MetadataKey} that points to another key determining the maximum value of the field.
*
* This indirection allows different keys to be used for different types of values with their
* own reducers, such as {@link MAX_DATE} and {@link MAX_NUMBER}.
*
* @category validation
* @publicApi 22.0
*/
declare const MAX: LimitSelectionKey;
/**
* A {@link MetadataKey} representing the maximum valid value of a date field.
*
* @category validation
* @publicApi 22.0
*/
declare const MAX_DATE: LimitKey<Date>;
/**
* A {@link MetadataKey} representing the maximum valid value of a number field.
*
* @category validation
* @publicApi 22.0
*/
declare const MAX_NUMBER: LimitKey<number>;
/**
* A {@link MetadataKey} representing the min length of the field.
*
* @category validation
* @publicApi 22.0
*/
declare const MIN_LENGTH: LimitKey<number>;
/**
* A {@link MetadataKey} representing the max length of the field.
*
* @category validation
* @publicApi 22.0
*/
declare const MAX_LENGTH: LimitKey<number>;
/**
* A {@link MetadataKey} representing the patterns the field must match.
*
* @category validation
* @publicApi 22.0
*/
declare const PATTERN: MetadataKey<Signal<RegExp[]>, RegExp | undefined, RegExp[]>;
/**
* Utility type that removes a string index key when its value is `unknown`,
* i.e. `{[key: string]: unknown}`. It allows specific string keys to pass through, even if their
* value is `unknown`, e.g. `{key: unknown}`.
*
* @publicApi 22.0
*/
type RemoveStringIndexUnknownKey<K, V> = string extends K ? unknown extends V ? never : K : K;
/**
* Utility type that recursively ignores unknown string index properties on the given object.
* We use this on the `TSchema` type in `validateStandardSchema` in order to accommodate Zod's
* `looseObject` which includes `{[key: string]: unknown}` as part of the type.
*
* @publicApi 22.0
*/
type IgnoreUnknownProperties<T> = T extends Record<PropertyKey, unknown> ? {
[K in keyof T as RemoveStringIndexUnknownKey<K, T[K]>]: IgnoreUnknownProperties<T[K]>;
} : T;
/**
* Validates a field using a `StandardSchemaV1` compatible validator (e.g. a Zod validator).
*
* See https://github.com/standard-schema/standard-schema for more about standard schema.
*
* @param path The `FieldPath` to the field to validate.
* @param schema The standard schema compatible validator to use for validation, or a LogicFn that returns the schema.
* @template TSchema The type validated by the schema. This may be either the full `TValue` type,
* or a partial of it.
* @template TValue The type of value stored in the field being validated.
*
* @see [Signal Form Schema Validation](guide/forms/signals/validation#integration-with-schema-validation-libraries)
* @category validation
* @publicApi 22.0
*/
declare function validateStandardSchema<TSchema, TModel extends IgnoreUnknownProperties<TSchema>>(path: SchemaPath<TModel> & SchemaPathTree<TModel>, schema: StandardSchemaV1<TSchema> | LogicFn<TModel, StandardSchemaV1<unknown> | undefined>): void;
/**
* Create a standard schema issue error associated with the target field
* @param issue The standard schema issue
* @param options The validation error options
*
* @category validation
* @publicApi 22.0
*/
declare function standardSchemaError(issue: StandardSchemaV1.Issue, options: WithFieldTree<ValidationErrorOptions>): StandardSchemaValidationError;
/**
* Create a standard schema issue error
* @param issue The standard schema issue
* @param options The optional validation error options
*
* @category validation
* @publicApi 22.0
*/
declare function standardSchemaError(issue: StandardSchemaV1.Issue, options?: ValidationErrorOptions): WithoutFieldTree<StandardSchemaValidationError>;
/**
* An error used to indicate an issue validating against a standard schema.
*
* @category validation
* @publicApi 22.0
*/
declare class StandardSchemaValidationError extends BaseNgValidationError {
readonly issue: StandardSchemaV1.Issue;
readonly kind = "standardSchema";
constructor(issue: StandardSchemaV1.Issue, options?: ValidationErrorOptions);
}
declare const ɵNgFieldDirective: unique symbol;
interface FormFieldBindingOptions {
/**
* Focuses the binding.
*
* If not specified, Signal Forms will attempt to focus the host element of the `FormField` when
* asked to focus this binding.
*/
readonly focus?: (focusOptions?: FocusOptions) => void;
/**
* Resets the binding.
*/
readonly reset?: () => void;
}
/**
* Lightweight DI token provided by the {@link FormField} directive.
*
* @category control
* @publicApi 22.0
*/
declare const FORM_FIELD: InjectionToken<FormField<unknown>>;
/**
* Binds a form `FieldTree` to a UI control that edits it. A UI control can be one of several things:
* 1. A native HTML input or textarea
* 2. A signal forms custom control that implements `FormValueControl` or `FormCheckboxControl`
* 3. A component that provides a `ControlValueAccessor`. This should only be used for backwards
* compatibility with reactive forms. Prefer options (1) and (2).
*
* This directive has several responsibilities:
* 1. Two-way binds the field state's value with the UI control's value
* 2. Binds additional forms related state on the field state to the UI control (disabled, required, etc.)
* 3. Relays relevant events on the control to the field state (e.g. marks touched on blur)
* 4. Provides a fake `NgControl` that implements a subset of the features available on the
* reactive forms `NgControl`. This is provided to improve interoperability with controls
* designed to work with reactive forms. It should not be used by controls written for signal
* forms.
*
* @category control
* @publicApi 22.0
*/
declare class FormField<T> {
/**
* The field to bind to the underlying form control.
*/
readonly field: i0.InputSignal<Field<T>>;
/**
* `FieldState` for the currently bound field.
*/
readonly state: Signal<FieldState<T, string | number>>;
/**
* The node injector for the DOM element hosting this field binding.
*/
readonly injector: Injector;
/**
* The DOM element hosting this field binding.
*/
readonly element: HTMLElement;
private readonly elementIsNativeFormElement;
private readonly elementAcceptsTextualValues;
private _elementAcceptsMinMax;
/**
* Current focus implementation, set by `registerAsBinding`.
*/
private focuser;
/** Any `ControlValueAccessor` instances provided on the host element. */
private readonly controlValueAccessors;
private readonly config;
private readonly validityMonitor;
/** A lazily instantiated fake `NgControl`. */
private _interopNgControl;
/** Errors associated with this form field. */
readonly errors: Signal<ValidationError.WithFieldTree[]>;
/** Whether this `FormField` has been registered as a binding on its associated `FieldState`. */
private isFieldBinding;
/**
* Current reset implementation, set by `registerAsBinding`.
*/
private resetter;
private parseErrorsResetCallback?;
/**
* Creates an `afterRenderEffect` that applies the configured class bindings to the host element
* if needed.
*/
private installClassBindingEffect;
/**
* Focuses this field binding.
*
* By default, this will focus the host DOM element. However, custom `FormUiControl`s can
* implement custom focusing behavior.
*/
focus(options?: FocusOptions): void;
/**
* Resets the bound control.
*/
reset(): void;
/**
* Registers this `FormField` as a binding on its associated `FieldState`.
*
* This method should be called at most once for a given `FormField`. A `FormField` placed on a
* custom control (`FormUiControl`) automatically registers that custom control as a binding.
*/
registerAsBinding(bindingOptions?: FormFieldBindingOptions): void;
/**
* The presence of this symbol tells the template type-checker that this directive is a control
* directive and should be type-checked as such. We don't use the `ɵngControlCreate` method below
* as it's marked internal and removed from the public API. A symbol is used instead to avoid
* polluting the public API with the marker.
*/
readonly [ɵNgFieldDirective]: true;
static ɵfac: i0.ɵɵFactoryDeclaration<FormField<any>, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<FormField<any>, "[formField]", ["formField"], { "field": { "alias": "formField"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
}
/**
* Options used to create a `ValidationError`.
*/
interface ValidationErrorOptions {
/** Human readable error message. */
message?: string;
}
/**
* A type that requires the given type `T` to have a `field` property.
* @template T The type to add a `field` to.
*
* @publicApi 22.0
*/
type WithFieldTree<T> = T & {
fieldTree: ReadonlyFieldTree<unknown>;
};
/**
* A type that allows the given type `T` to optionally have a `field` property.
* @template T The type to optionally add a `field` to.
*
* @publicApi 22.0
*/
type WithOptionalFieldTree<T> = Omit<T, 'fieldTree'> & {
fieldTree?: ReadonlyFieldTree<unknown>;
};
/**
* A type that ensures the given type `T` does not have a `field` property.
* @template T The type to remove the `field` from.
*
* @publicApi 22.0
*/
type WithoutFieldTree<T> = T & {
fieldTree: never;
};
/**
* Create a required error associated with the target field
* @param options The validation error options
*
* @publicApi 22.0
*/
declare function requiredError(options: WithFieldTree<ValidationErrorOptions>): RequiredValidationError;
/**
* Create a required error
* @param options The optional validation error options
*
* @category validation
* @publicApi 22.0
*/
declare function requiredError(options?: ValidationErrorOptions): WithoutFieldTree<RequiredValidationError>;
/**
* Create a min value error associated with the target field
* @param min The min value constraint
* @param options The validation error options
*
*