UNPKG

@angular/forms

Version:

Angular - directives and services for creating forms

1,245 lines (1,237 loc) 98.5 kB
/** * @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