@angular/forms
Version:
Angular - directives and services for creating forms
1,245 lines (1,237 loc) • 98.5 kB
TypeScript
/**
* @license Angular v21.0.5
* (c) 2010-2025 Google LLC. https://angular.dev/
* License: MIT
*/
import * as i0 from '@angular/core';
import { InjectionToken, Injector, ɵCONTROL as _CONTROL, ɵɵcontrolCreate as __controlCreate, ɵcontrolUpdate as _controlUpdate, Signal, ɵFieldState as _FieldState, Provider, WritableSignal, DestroyableInjector } from '@angular/core';
import * as _angular_forms from '@angular/forms';
import { NgControl, AbstractControl, ValidationErrors, FormControlStatus, ControlValueAccessor, ValidatorFn } from '@angular/forms';
import { StandardSchemaV1 } from '@standard-schema/spec';
/**
* Properties of both NgControl & AbstractControl that are supported by the InteropNgControl.
*/
type InteropSharedKeys = 'value' | 'valid' | 'invalid' | 'touched' | 'untouched' | 'disabled' | 'enabled' | 'errors' | 'pristine' | 'dirty' | 'status';
/**
* A fake version of `NgControl` provided by the `Field` directive. This allows interoperability
* with a wider range of components designed to work with reactive forms, in particular ones that
* inject the `NgControl`. The interop control does not implement *all* properties and methods of
* the real `NgControl`, but does implement some of the most commonly used ones that have a clear
* equivalent in signal forms.
*/
declare class InteropNgControl implements Pick<NgControl, InteropSharedKeys | 'control' | 'valueAccessor'>, Pick<AbstractControl<unknown>, InteropSharedKeys | 'hasValidator'> {
protected field: () => FieldState<unknown>;
constructor(field: () => FieldState<unknown>);
readonly control: AbstractControl<any, any>;
get value(): any;
get valid(): boolean;
get invalid(): boolean;
get pending(): boolean | null;
get disabled(): boolean;
get enabled(): boolean;
get errors(): ValidationErrors | null;
get pristine(): boolean;
get dirty(): boolean;
get touched(): boolean;
get untouched(): boolean;
get status(): FormControlStatus;
valueAccessor: ControlValueAccessor | null;
hasValidator(validator: ValidatorFn): boolean;
updateValueAndValidity(): void;
}
/**
* Lightweight DI token provided by the {@link Field} directive.
*
* @category control
* @experimental 21.0.0
*/
declare const FIELD: InjectionToken<Field<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's value with the UI control's value
* 2. Binds additional forms related state on the field to the UI control (disabled, required, etc.)
* 3. Relays relevant events on the control to the field (e.g. marks field 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
* @experimental 21.0.0
*/
declare class Field<T> {
readonly element: HTMLElement;
readonly injector: Injector;
readonly field: i0.InputSignal<FieldTree<T>>;
readonly state: i0.Signal<[T] extends [_angular_forms.AbstractControl<any, any, any>] ? CompatFieldState<T, string | number> : FieldState<T, string | number>>;
readonly [_CONTROL]: {
readonly create: typeof __controlCreate;
readonly update: typeof _controlUpdate;
};
private config;
/** Any `ControlValueAccessor` instances provided on the host element. */
private readonly controlValueAccessors;
/** A lazily instantiated fake `NgControl`. */
private interopNgControl;
/** Lazily instantiates a fake `NgControl` for this field. */
protected getOrCreateNgControl(): InteropNgControl;
static ɵfac: i0.ɵɵFactoryDeclaration<Field<any>, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<Field<any>, "[field]", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
}
/**
* 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
* @experimental 21.0.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, 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.
* @experimental 21.0.2
*/
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: () => MetadataReducer<number | undefined, number | undefined>;
/** Creates a reducer that accumulates a the max of its individual item values. */
readonly max: () => MetadataReducer<number | undefined, number | 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>;
/**
* 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.
*
* @experimental 21.0.0
*/
declare class MetadataKey<TRead, TWrite, TAcc> {
readonly reducer: MetadataReducer<TAcc, TWrite>;
readonly create: ((s: Signal<TAcc>) => TRead) | undefined;
private brand;
/** Use {@link reducedMetadataKey}. */
protected constructor(reducer: MetadataReducer<TAcc, TWrite>, create: ((s: Signal<TAcc>) => TRead) | undefined);
}
/**
* Extracts the the type that can be set into the given metadata key type using the `metadata()` rule.
*
* @template TKey The `MetadataKey` type
*
* @experimental 21.0.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
*
* @experimental 21.0.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.
*
* @experimental 21.0.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
*
* @experimental 21.0.0
*/
declare function createManagedMetadataKey<TRead, TWrite>(create: (s: 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.
*
* @experimental 21.0.0
*/
declare function createManagedMetadataKey<TRead, TWrite, TAcc>(create: (s: Signal<TAcc>) => TRead, reducer: MetadataReducer<TAcc, TWrite>): MetadataKey<TRead, TWrite, TAcc>;
/**
* A {@link MetadataKey} representing whether the field is required.
*
* @category validation
* @experimental 21.0.0
*/
declare const REQUIRED: MetadataKey<Signal<boolean>, boolean, boolean>;
/**
* A {@link MetadataKey} representing the min value of the field.
*
* @category validation
* @experimental 21.0.0
*/
declare const MIN: MetadataKey<Signal<number | undefined>, number | undefined, number | undefined>;
/**
* A {@link MetadataKey} representing the max value of the field.
*
* @category validation
* @experimental 21.0.0
*/
declare const MAX: MetadataKey<Signal<number | undefined>, number | undefined, number | undefined>;
/**
* A {@link MetadataKey} representing the min length of the field.
*
* @category validation
* @experimental 21.0.0
*/
declare const MIN_LENGTH: MetadataKey<Signal<number | undefined>, number | undefined, number | undefined>;
/**
* A {@link MetadataKey} representing the max length of the field.
*
* @category validation
* @experimental 21.0.0
*/
declare const MAX_LENGTH: MetadataKey<Signal<number | undefined>, number | undefined, number | undefined>;
/**
* A {@link MetadataKey} representing the patterns the field must match.
*
* @category validation
* @experimental 21.0.0
*/
declare const PATTERN: MetadataKey<Signal<RegExp[]>, RegExp | undefined, RegExp[]>;
/**
* Symbol used to retain generic type information when it would otherwise be lost.
*/
declare const ɵɵTYPE: unique symbol;
/**
* 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).
*
* @experimental 21.0.0
*/
type OneOrMany<T> = T | readonly T[];
/**
* The kind of `FieldPath` (`Root`, `Child` of another `FieldPath`, or `Item` in a `FieldPath` array)
*
* @experimental 21.0.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 status indicating whether a field is unsubmitted, submitted, or currently submitting.
*
* @category types
* @experimental 21.0.0
*/
type SubmittedStatus = 'unsubmitted' | 'submitted' | 'submitting';
/**
* A reason for a field's disablement.
*
* @category logic
* @experimental 21.0.0
*/
interface DisabledReason {
/** The field that is disabled. */
readonly field: FieldTree<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
* @experimental 21.0.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
* @experimental 21.0.0
*/
type TreeValidationResult<E extends ValidationError.WithOptionalField = ValidationError.WithOptionalField> = 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
* @experimental 21.0.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
* @experimental 21.0.0
*/
type AsyncValidationResult<E extends ValidationError = ValidationError> = ValidationResult<E> | 'pending';
/**
* 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.
*
* @category types
* @experimental 21.0.0
*/
type FieldTree<TModel, TKey extends string | number = string | number> = (() => [TModel] extends [AbstractControl] ? CompatFieldState<TModel, TKey> : FieldState<TModel, TKey>) & ([TModel] extends [AbstractControl] ? object : [TModel] extends [Array<infer U>] ? ReadonlyArrayLike<MaybeFieldTree<U, number>> : TModel extends Record<string, any> ? Subfields<TModel> : object);
/**
* 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.
*
* @experimental 21.0.0
*/
type Subfields<TModel> = {
readonly [K in keyof TModel as TModel[K] extends Function ? never : K]: MaybeFieldTree<TModel[K], string>;
} & {
[Symbol.iterator](): Iterator<[string, MaybeFieldTree<TModel[keyof TModel], string>]>;
};
/**
* An iterable object with the same shape as a readonly array.
*
* @template T The array item type.
*
* @experimental 21.0.0
*/
type ReadonlyArrayLike<T> = Pick<ReadonlyArray<T>, number | 'length' | typeof Symbol.iterator>;
/**
* Helper type for defining `FieldTree`. Given a type `TValue` that may include `undefined`, it extracts
* the `undefined` outside the `FieldTree` type.
*
* For example `MaybeField<{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.
*
* @experimental 21.0.0
*/
type MaybeFieldTree<TModel, TKey extends string | number = string | number> = (TModel & undefined) | FieldTree<Exclude<TModel, undefined>, TKey>;
/**
* Contains all of the state (e.g. value, statuses, etc.) associated with a `FieldTree`, exposed as
* signals.
*
* @category structure
* @experimental 21.0.0
*/
interface FieldState<TValue, TKey extends string | number = string | number> extends _FieldState<TValue> {
/**
* 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.WithField[]>;
/**
* A signal containing the {@link errors} of the field and its descendants.
*/
readonly errorSummary: Signal<ValidationError.WithField[]>;
/**
* 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 Field} directives that bind this field to a UI control.
*/
readonly fieldBindings: Signal<readonly Field<unknown>[]>;
/**
* Reads a metadata value from the field.
* @param key The metadata key to read.
*/
metadata<M>(key: MetadataKey<M, any, any>): M | 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;
}
/**
* This is FieldState also providing access to the wrapped FormControl.
*
* @category interop
* @experimental 21.0.0
*/
type CompatFieldState<TControl extends AbstractControl, TKey extends string | number = string | number> = FieldState<TControl extends AbstractControl<unknown, infer TValue> ? TValue : never, TKey> & {
control: Signal<TControl>;
};
/**
* Allows declaring whether the Rules are supported for a given path.
*
* @experimental 21.0.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
* @experimental 21.0.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
* @experimental 21.0.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.
*
* @experimental 21.0.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 Array<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)
*
* @experimental 21.0.0
*/
type MaybeSchemaPathTree<TModel, TPathKind extends PathKind = PathKind.Root> = (TModel & undefined) | SchemaPathTree<Exclude<TModel, undefined>, TPathKind>;
/**
* Defines logic for a form.
*
* @template TValue The type of data stored in the form that this schema is attached to.
*
* @category types
* @experimental 21.0.0
*/
type Schema<in TModel> = {
[ɵɵTYPE]: SchemaFn<TModel, PathKind.Root>;
};
/**
* Function that defines rules for a schema.
*
* @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
* @experimental 21.0.0
*/
type SchemaFn<TModel, TPathKind extends PathKind = PathKind.Root> = (p: SchemaPathTree<TModel, TPathKind>) => void;
/**
* A schema or schema definition function.
*
* @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
* @experimental 21.0.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
* @experimental 21.0.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
* @experimental 21.0.0
*/
type FieldValidator<TValue, TPathKind extends PathKind = PathKind.Root> = LogicFn<TValue, ValidationResult<ValidationError.WithoutField>, 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
* @experimental 21.0.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)
*
* @category types
* @experimental 21.0.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
* @experimental 21.0.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.
*
* @experimental 21.0.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: FieldState<TValue>;
/** The current field. */
readonly field: FieldTree<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>): CompatFieldState<PControl>;
stateOf<PValue>(p: SchemaPath<PValue, SchemaPathRules>): FieldState<PValue>;
/** Gets the field represented by the given path. */
fieldTreeOf<PModel>(p: SchemaPathTree<PModel>): FieldTree<PModel>;
/** 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
* @experimental 21.0.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.
*
* @experimental 21.0.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.
*
* @experimental 21.0.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).
*
* @experimental 21.0.0
*/
type Debouncer<TValue, TPathKind extends PathKind = PathKind.Root> = (context: FieldContext<TValue, TPathKind>, abortSignal: AbortSignal) => Promise<void> | void;
/**
* 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.
*
* @experimental 21.0.0
*/
type WithField<T> = T & {
field: FieldTree<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.
*
* @experimental 21.0.0
*/
type WithOptionalField<T> = Omit<T, 'field'> & {
field?: FieldTree<unknown>;
};
/**
* A type that ensures the given type `T` does not have a `field` property.
* @template T The type to remove the `field` from.
*
* @experimental 21.0.0
*/
type WithoutField<T> = T & {
field: never;
};
/**
* Create a required error associated with the target field
* @param options The validation error options
*
* @experimental 21.0.0
*/
declare function requiredError(options: WithField<ValidationErrorOptions>): RequiredValidationError;
/**
* Create a required error
* @param options The optional validation error options
*
* @category validation
* @experimental 21.0.0
*/
declare function requiredError(options?: ValidationErrorOptions): WithoutField<RequiredValidationError>;
/**
* Create a min value error associated with the target field
* @param min The min value constraint
* @param options The validation error options
*
* @category validation
* @experimental 21.0.0
*/
declare function minError(min: number, options: WithField<ValidationErrorOptions>): MinValidationError;
/**
* Create a min value error
* @param min The min value constraint
* @param options The optional validation error options
*
* @category validation
* @experimental 21.0.0
*/
declare function minError(min: number, options?: ValidationErrorOptions): WithoutField<MinValidationError>;
/**
* Create a max value error associated with the target field
* @param max The max value constraint
* @param options The validation error options
*
* @category validation
* @experimental 21.0.0
*/
declare function maxError(max: number, options: WithField<ValidationErrorOptions>): MaxValidationError;
/**
* Create a max value error
* @param max The max value constraint
* @param options The optional validation error options
*
* @category validation
* @experimental 21.0.0
*/
declare function maxError(max: number, options?: ValidationErrorOptions): WithoutField<MaxValidationError>;
/**
* Create a minLength error associated with the target field
* @param minLength The minLength constraint
* @param options The validation error options
*
* @category validation
* @experimental 21.0.0
*/
declare function minLengthError(minLength: number, options: WithField<ValidationErrorOptions>): MinLengthValidationError;
/**
* Create a minLength error
* @param minLength The minLength constraint
* @param options The optional validation error options
*
* @category validation
* @experimental 21.0.0
*/
declare function minLengthError(minLength: number, options?: ValidationErrorOptions): WithoutField<MinLengthValidationError>;
/**
* Create a maxLength error associated with the target field
* @param maxLength The maxLength constraint
* @param options The validation error options
*
* @category validation
* @experimental 21.0.0
*/
declare function maxLengthError(maxLength: number, options: WithField<ValidationErrorOptions>): MaxLengthValidationError;
/**
* Create a maxLength error
* @param maxLength The maxLength constraint
* @param options The optional validation error options
*
* @category validation
* @experimental 21.0.0
*/
declare function maxLengthError(maxLength: number, options?: ValidationErrorOptions): WithoutField<MaxLengthValidationError>;
/**
* Create a pattern matching error associated with the target field
* @param pattern The violated pattern
* @param options The validation error options
*
* @category validation
* @experimental 21.0.0
*/
declare function patternError(pattern: RegExp, options: WithField<ValidationErrorOptions>): PatternValidationError;
/**
* Create a pattern matching error
* @param pattern The violated pattern
* @param options The optional validation error options
*
* @category validation
* @experimental 21.0.0
*/
declare function patternError(pattern: RegExp, options?: ValidationErrorOptions): WithoutField<PatternValidationError>;
/**
* Create an email format error associated with the target field
* @param options The validation error options
*
* @category validation
* @experimental 21.0.0
*/
declare function emailError(options: WithField<ValidationErrorOptions>): EmailValidationError;
/**
* Create an email format error
* @param options The optional validation error options
*
* @category validation
* @experimental 21.0.0
*/
declare function emailError(options?: ValidationErrorOptions): WithoutField<EmailValidationError>;
/**
* 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
* @experimental 21.0.0
*/
declare function standardSchemaError(issue: StandardSchemaV1.Issue, options: WithField<ValidationErrorOptions>): StandardSchemaValidationError;
/**
* Create a standard schema issue error
* @param issue The standard schema issue
* @param options The optional validation error options
*
* @category validation
* @experimental 21.0.0
*/
declare function standardSchemaError(issue: StandardSchemaV1.Issue, options?: ValidationErrorOptions): WithoutField<StandardSchemaValidationError>;
/**
* Create a custom error associated with the target field
* @param obj The object to create an error from
*
* @category validation
* @experimental 21.0.0
*/
declare function customError<E extends Partial<ValidationError.WithField>>(obj: WithField<E>): CustomValidationError;
/**
* Create a custom error
* @param obj The object to create an error from
*
* @category validation
* @experimental 21.0.0
*/
declare function customError<E extends Partial<ValidationError.WithField>>(obj?: E): WithoutField<CustomValidationError>;
/**
* Common interface for all validation errors.
*
* This can be returned from validators.
*
* It's also used by the creation functions to create an instance
* (e.g. `requiredError`, `minError`, etc.).
*
* @category validation
* @experimental 21.0.0
*/
interface ValidationError {
/** Identifies the kind of error. */
readonly kind: string;
/** Human readable error message. */
readonly message?: string;
}
declare namespace ValidationError {
/**
* Validation error with a field.
*
* This is returned from field state, e.g., catField.errors() would be of a list of errors with
* `field: catField` bound to state.
*/
interface WithField extends ValidationError {
/** The field associated with this error. */
readonly field: FieldTree<unknown>;
}
/**
* Validation error with optional field.
*
* This is generally used in places where the result might have a field.
* e.g., as a result of a `validateTree`, or when handling form submission.
*/
interface WithOptionalField extends ValidationError {
/** The field associated with this error. */
readonly field?: FieldTree<unknown>;
}
/**
* Validation error with no field.
*
* This is used to strongly enforce that fields are not allowed in validation result.
*/
interface WithoutField extends ValidationError {
/** The field associated with this error. */
readonly field?: never;
}
}
/**
* A custom error that may contain additional properties
*
* @category validation
* @experimental 21.0.0
*/
declare class CustomValidationError implements ValidationError {
/** Brand the class to avoid Typescript structural matching */
private __brand;
/**
* Allow the user to attach arbitrary other properties.
*/
[key: PropertyKey]: unknown;
/** Identifies the kind of error. */
readonly kind: string;
/** The field associated with this error. */
readonly field: FieldTree<unknown>;
/** Human readable error message. */
readonly message?: string;
constructor(options?: ValidationErrorOptions);
}
/**
* Internal version of `NgValidationError`, we create this separately so we can change its type on
* the exported version to a type union of the possible sub-classes.
*
* @experimental 21.0.0
*/
declare abstract class _NgValidationError implements ValidationError {
/** Brand the class to avoid Typescript structural matching */
private __brand;
/** Identifies the kind of error. */
readonly kind: string;
/** The field associated with this error. */
readonly field: FieldTree<unknown>;
/** Human readable error message. */
readonly message?: string;
constructor(options?: ValidationErrorOptions);
}
/**
* An error used to indicate that a required field is empty.
*
* @category validation
* @experimental 21.0.0
*/
declare class RequiredValidationError extends _NgValidationError {
readonly kind = "required";
}
/**
* An error used to indicate that a value is lower than the minimum allowed.
*
* @category validation
* @experimental 21.0.0
*/
declare class MinValidationError extends _NgValidationError {
readonly min: number;
readonly kind = "min";
constructor(min: number, options?: ValidationErrorOptions);
}
/**
* An error used to indicate that a value is higher than the maximum allowed.
*
* @category validation
* @experimental 21.0.0
*/
declare class MaxValidationError extends _NgValidationError {
readonly max: number;
readonly kind = "max";
constructor(max: number, options?: ValidationErrorOptions);
}
/**
* An error used to indicate that a value is shorter than the minimum allowed length.
*
* @category validation
* @experimental 21.0.0
*/
declare class MinLengthValidationError extends _NgValidationError {
readonly minLength: number;
readonly kind = "minLength";
constructor(minLength: number, options?: ValidationErrorOptions);
}
/**
* An error used to indicate that a value is longer than the maximum allowed length.
*
* @category validation
* @experimental 21.0.0
*/
declare class MaxLengthValidationError extends _NgValidationError {
readonly maxLength: number;
readonly kind = "maxLength";
constructor(maxLength: number, options?: ValidationErrorOptions);
}
/**
* An error used to indicate that a value does not match the required pattern.
*
* @category validation
* @experimental 21.0.0
*/
declare class PatternValidationError extends _NgValidationError {
readonly pattern: RegExp;
readonly kind = "pattern";
constructor(pattern: RegExp, options?: ValidationErrorOptions);
}
/**
* An error used to indicate that a value is not a valid email.
*
* @category validation
* @experimental 21.0.0
*/
declare class EmailValidationError extends _NgValidationError {
readonly kind = "email";
}
/**
* An error used to indicate an issue validating against a standard schema.
*
* @category validation
* @experimental 21.0.0
*/
declare class StandardSchemaValidationError extends _NgValidationError {
readonly issue: StandardSchemaV1.Issue;
readonly kind = "standardSchema";
constructor(issue: StandardSchemaV1.Issue, options?: ValidationErrorOptions);
}
/**
* The base class for all built-in, non-custom errors. This class can be used to check if an error
* is one of the standard kinds, allowing you to switch on the kind to further narrow the type.
*
* @example
* ```ts
* const f = form(...);
* for (const e of form().errors()) {
* if (e instanceof NgValidationError) {
* switch(e.kind) {
* case 'required':
* console.log('This is required!');
* break;
* case 'min':
* console.log(`Must be at least ${e.min}`);
* break;
* ...
* }
* }
* }
* ```
*
* @category validation
* @experimental 21.0.0
*/
declare const NgValidationError: abstract new () => NgValidationError;
type NgValidationError = RequiredValidationError | MinValidationError | MaxValidationError | MinLengthValidationError | MaxLengthValidationError | PatternValidationError | EmailValidationError | StandardSchemaValidationError;
/**
* Configuration options for signal forms.
*
* @experimental 21.0.1
*/
interface SignalFormsConfig {
/** A map of CSS class names to predicate functions that determine when to apply them. */
classes?: {
[className: string]: (state: FieldState<unknown>) => boolean;
};
}
/**
* Provides configuration options for signal forms.
*
* @experimental 21.0.1
*/
declare function provideSignalFormsConfig(config: SignalFormsConfig): Provider[];
/** Represents a result that should be ignored because its predicate indicates it is not active. */
declare const IGNORED: unique symbol;
/**
* A predicate that indicates whether an `AbstractLogic` instance is currently active, or should be
* ignored.
*/
interface Predicate {
/** A boolean logic function that returns true if the logic is considered active. */
readonly fn: LogicFn<any, boolean>;
/**
* The path which this predicate was created for. This is used to determine the correct
* `FieldContext` to pass to the predicate function.
*/
readonly path: SchemaPath<any>;
}
/**
* Represents a predicate that is bound to a particular depth in the field tree. This is needed for
* recursively applied logic to ensure that the predicate is evaluated against the correct
* application of that logic.
*
* Consider the following example:
*
* ```
* const s = schema(p => {
* disabled(p.data);
* applyWhen(p.next, ({valueOf}) => valueOf(p.data) === 1, s);
* });
*
* const f = form(signal({data: 0, next: {data: 1, next: {data: 2, next: undefined}}}), s);
*
* const isDisabled = f.next.next.data().disabled();
* ```
*
* In order to determine `isDisabled` we need to evaluate the predicate from `applyWhen` *twice*.
* Once to see if the schema should be applied to `f.next` and again to see if it should be applied
* to `f.next.next`. The `depth` tells us which field we should be evaluating against each time.
*/
interface BoundPredicate extends Predicate {
/** The depth in the field tree at which this predicate is bound. */
readonly depth: number;
}
/**
* Base class for all logic. It is responsible for combining the results from multiple individual
* logic functions registered in the schema, and using them to derive the value for some associated
* piece of field state.
*/
declare abstract class AbstractLogic<TReturn, TValue = TReturn> {
/**
* A list of predicates that conditionally enable all logic in this logic instance.
* The logic is only enabled when *all* of the predicates evaluate to true.
*/
private predicates;
/** The set of logic functions that contribute to the value of the associated state. */
protected readonly fns: Array<LogicFn<any, TValue | typeof IGNORED>>;
constructor(
/**
* A list of predicates that conditionally enable all logic in this logic instance.
* The logic is only enabled when *all* of the predicates evaluate to true.
*/
predicates: ReadonlyArray<BoundPredicate>);
/**
* Computes the value of the associated field state based on the logic functions and predicates
* registered with this logic instance.
*/
abstract compute(arg: FieldContext<any>): TReturn;
/**
* The default value that the associated field state should assume if there are no logic functions
* registered by the schema (or if the logic is disabled by a predicate).
*/
abstract get defaultValue(): TReturn;
/** Registers a logic function with this logic instance. */
push(logicFn: LogicFn<any, TValue>): void;
/**
* Merges in the logic from another logic instance, subject to the predicates of both the other
* instance and this instance.
*/
mergeIn(other: AbstractLogic<TReturn, TValue>): void;
}
/** Logic that combines its individual logic function results with logical OR. */
declare class BooleanOrLogic extends AbstractLogic<boolean> {
get defaultValue(): boolean;
compute(arg: FieldContext<any>): boolean;
}
/**
* Logic that combines its individual logic function results by aggregating them in an array.
* Depending on its `ignore` function it may ignore certain values, omitting them from the array.
*/
declare class ArrayMergeIgnoreLogic<TElement, TIgnore = never> extends AbstractLogic<readonly TElement[], TElement | readonly (TElement | TIgnore)[] | TIgnore | undefined | void> {
private ignore;
/** Creates an instance of this class that ignores `null` values. */
static ignoreNull<TElement>(predicates: ReadonlyArray<BoundPredicate>): ArrayMergeIgnoreLogic<TElement, null>;
constructor(predicates: ReadonlyArray<BoundPredicate>, ignore: undefined | ((e: TElement | undefined | TIgnore) => e is TIgnore));
get defaultValue(): never[];
compute(arg: FieldContext<any>): readonly TElement[];
}
/** Logic that combines its individual logic function results by aggregating them in an array. */
declare class ArrayMergeLogic<TElement> extends ArrayMergeIgnoreLogic<TElement, never> {
constructor(predicates: ReadonlyArray<BoundPredicate>);
}
/**
* Container for all the different types of logic that can be applied to a field
* (disabled, hidden, errors, etc.)
*/
declare class LogicContainer {
private predicates;
/** Logic that determines if the field is hidden. */
readonly hidden: BooleanOrLogic;
/** Logic that determines reasons for the field being disabled. */
readonly disabledReasons: ArrayMergeLogic<DisabledReason>;
/** Logic that determines if the field is read-only. */
readonly readonly: BooleanOrLogic;
/** Logic that produces synchronous validation errors for the field. */
readonly syncErrors: ArrayMergeIgnoreLogic<ValidationError.WithField, null>;
/** Logic that produces synchronous validation errors for the field's subtree. */
readonly syncTreeErrors: ArrayMergeIgnoreLogic<ValidationError.WithField, null>;
/** Logic that produces asynchronous validation results (errors or 'pending'). */
readonly asyncErrors: ArrayMergeIgnoreLogic<ValidationError.WithField | 'pending', null>;
/** A map of metadata keys to the `AbstractLogic` instances that compute the