@angular/forms
Version:
Angular - directives and services for creating forms
1,235 lines (1,227 loc) • 67.9 kB
TypeScript
/**
* @license Angular v21.2.0
* (c) 2010-2026 Google LLC. https://angular.dev/
* License: MIT
*/
import * as i0 from '@angular/core';
import { WritableSignal, Signal, InjectionToken, Injector, Provider } from '@angular/core';
import { AbstractControl, ValidationErrors, FormControlStatus, ControlValueAccessor, ValidatorFn } 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.
*
* @experimental 21.2.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
* - 'ignore': Will always submit regardless of invalid or pending validators
*/
ignoreValidators?: 'pending' | 'none' | 'all';
}
/**
* 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 reason for a field's disablement.
*
* @category logic
* @experimental 21.0.0
*/
interface DisabledReason {
/** The field that is disabled. */
readonly fieldTree: 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.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
* @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';
/**
* 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
* @experimental 21.2.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.
*
* @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 [ReadonlyArray<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> {
/**
* 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 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<number | undefined>;
/**
* A signal indicating the field's maximum string length, if applicable.
*
* Applies to `<input>`, `<textarea>`, and custom controls.
*/
readonly maxLength?: Signal<number | 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<number | undefined>;
/**
* A signal indicating the field's minimum string length, if applicable.
*
* Applies to `<input>`, `<textarea>`, and custom controls.
*/
readonly minLength?: Signal<number | 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 FormField<unknown>[]>;
/**
* 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 to `true`.
*/
markAsTouched(): void;
/**
* 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;
/**
* 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;
}
/**
* 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 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)
*
* @experimental 21.0.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
* @experimental 21.0.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
* @experimental 21.0.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
* @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.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
* @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)
* @see [Signal Form Validation](/guide/forms/signals/validation)
* @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 fieldTree: 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;
/**
* 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[]>;
/**
* 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}`.
*
* @experimental 21.0.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.
*
* @experimental 21.0.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
* @experimental 21.0.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
* @experimental 21.0.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
* @experimental 21.0.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
* @experimental 21.0.0
*/
declare class StandardSchemaValidationError extends BaseNgValidationError {
readonly issue: StandardSchemaV1.Issue;
readonly kind = "standardSchema";
constructor(issue: StandardSchemaV1.Issue, options?: ValidationErrorOptions);
}
/**
* Represents a combination of `NgControl` and `AbstractControl`.
*
* Note: We have this separate interface, rather than implementing the relevant parts of the two
* controls with something like `InteropNgControl implements Pick<NgControl, ...>, Pick<AbstractControl, ...>`
* because it confuses the internal JS minifier which can cause collisions in field names.
*/
interface CombinedControl {
value: any;
valid: boolean;
invalid: boolean;
touched: boolean;
untouched: boolean;
disabled: boolean;
enabled: boolean;
errors: ValidationErrors | null;
pristine: boolean;
dirty: boolean;
status: FormControlStatus;
control: AbstractControl<any, any>;
valueAccessor: ControlValueAccessor | null;
hasValidator(validator: ValidatorFn): boolean;
updateValueAndValidity(): void;
}
/**
* 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 CombinedControl {
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;
}
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;
}
/**
* Lightweight DI token provided by the {@link FormField} directive.
*
* @category control
* @experimental 21.0.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
* @experimental 21.0.0
*/
declare class FormField<T> {
readonly field: i0.InputSignal<Field<T>>;
/**
* `FieldState` for the currently bound field.
*/
readonly state: Signal<FieldState<T, string | number>>;
/**
* The node injector for the element this field binding.
*/
readonly injector: Injector;
/**
* The DOM element hosting this field binding.
*/
readonly element: HTMLElement;
private readonly elementIsNativeFormElement;
private readonly elementAcceptsNumericValues;
private readonly elementAcceptsTextualValues;
/**
* Current focus implementation, set by `registerAsBinding`.
*/
private focuser;
/** Any `ControlValueAccessor` instances provided on the host element. */
private readonly controlValueAccessors;
private readonly config;
private readonly parseErrorsSource;
/** A lazily instantiated fake `NgControl`. */
private _interopNgControl;
/** Lazily instantiates a fake `NgControl` for this form field. */
protected get interopNgControl(): 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;
/**
* 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;
/**
* 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.
*
* @experimental 21.0.0
*/
type WithFieldTree<T> = T & {
fieldTree: FieldTree<unknown>;
};
/** @deprecated Use `WithFieldTree` instead */
type WithField<T> = WithFieldTree<T>;
/**
* 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 WithOptionalFieldTree<T> = Omit<T, 'fieldTree'> & {
fieldTree?: FieldTree<unknown>;
};
/** @deprecated Use `WithOptionalFieldTree` instead */
type WithOptionalField<T> = WithOptionalFieldTree<T>;
/**
* 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 WithoutFieldTree<T> = T & {
fieldTree: never;
};
/** @deprecated Use `WithoutFieldTree` instead */
type WithoutField<T> = WithoutFieldTree<T>;
/**
* Create a required error associated with the target field
* @param options The validation error options
*
* @experimental 21.0.0
*/
declare function requiredError(options: WithFieldTree<ValidationErrorOptions>): RequiredValidationError;
/**
* Create a required error
* @param options The optional validation error options
*
* @category validation
* @experimental 21.0.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
*
* @category validation
* @experimental 21.0.0
*/
declare function minError(min: number, options: WithFieldTree<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): WithoutFieldTree<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: WithFieldTree<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): WithoutFieldTree<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: WithFieldTree<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): WithoutFieldTree<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: WithFieldTree<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): WithoutFieldTree<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: WithFieldTree<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): WithoutFieldTree<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: WithFieldTree<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): WithoutFieldTree<EmailValidationError>;
/**
* 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.).
*
* @see [Signal Form Validation](guide/forms/signals/validation)
* @see [Signal Form Validation Errors](guide/forms/signals/validation#validation-errors)
* @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 an associated field tree.
*
* This is returned from field state, e.g., catField.errors() would be of a list of errors with
* `field: catField` bound to state.
*/
interface WithFieldTree extends ValidationError {
/** The field associated with this error. */
readonly fieldTree: FieldTree<unknown>;
readonly formField?: FormField<unknown>;
}
/** @deprecated Use `ValidationError.WithFieldTree` instead */
type WithField = WithFieldTree;
/**
* Validation error with an associated field tree and specific form field binding.
*/
interface WithFormField extends WithFieldTree {
readonly formField: FormField<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.