UNPKG

ngrx-forms

Version:

Proper integration of forms in Angular 4 applications using ngrx

525 lines (524 loc) 22.5 kB
import { Boxed } from './boxing'; export declare type FormControlValueTypes = Boxed<any> | string | number | boolean | null | undefined; export declare type NgrxFormControlId = string; /** * This type represents a collection of named errors. */ export interface ValidationErrors { readonly [key: string]: any; } export interface KeyValue { [key: string]: any; } /** * Base interface for all types of form states. */ export interface AbstractControlState<TValue> { /** * The unique ID of the form state. Usually this is the name or index * of the control in the form value prefixed by the ID of the containing * group or array, e.g. `MY_FORM.someTextInput` or `MY_FORM.0`. */ readonly id: string; /** * The value of the form state. */ readonly value: TValue; /** * This property is `true` if the state does not have any errors. */ readonly isValid: boolean; /** * This property is `true` if the state has at least one error. */ readonly isInvalid: boolean; /** * The errors of the state. This property always has a value. * If the state has no errors the property is set to `{}`. */ readonly errors: ValidationErrors; /** * The names of all asynchronous validations currently running * for the state. */ readonly pendingValidations: readonly string[]; /** * This property indicates whether the control is currently being * asynchronously validated. */ readonly isValidationPending: boolean; /** * This property indicates whether the state is enabled. When it * is `false` the `errors` are always `{}` (i.e. the state is * always valid if disabled) and `pendingValidations` is always `[]` * (i.e. all pending validations are cancelled). */ readonly isEnabled: boolean; /** * This property indicates whether the state is disabled. When it * is `true` the `errors` are always `{}` (i.e. the state is * always valid if disabled) and `pendingValidations` is always `[]` * (i.e. all pending validations are cancelled). */ readonly isDisabled: boolean; /** * This property is set to `true` as soon as the state's value changes. */ readonly isDirty: boolean; /** * This property is `true` as long as the state's value never changed. */ readonly isPristine: boolean; /** * This property is set to `true` as soon as the state is touched. */ readonly isTouched: boolean; /** * This property is `true` as long as the state is not touched. */ readonly isUntouched: boolean; /** * This property is set to `true` as soon as the state is submitted. */ readonly isSubmitted: boolean; /** * This property is `true` as long as the state is not submitted. */ readonly isUnsubmitted: boolean; /** * This property is a container for user-defined metadata (e.g. if * you wanted to count the number of times a state's value has been * changed, what keys were pressed on an input, or how often a form * has been submitted etc.). While it is possible to store this kind * of information outside of **ngrx-forms** in your own state the * `userDefinedProperties` allow you to store your own metadata * directly in the state. */ readonly userDefinedProperties: KeyValue; } /** * State associated with a form control, i.e. an HTML form * element in the view (e.g. `input`, `select`, `textarea` etc.). */ export interface FormControlState<TValue extends FormControlValueTypes> extends AbstractControlState<TValue> { /** * The value of the form state. Form controls only support values of * type `string`, `number`, `boolean`, `null`, and `undefined` to * keep the state string serializable. */ readonly value: TValue; /** * This property is `true` if the form control does not have any errors. */ readonly isValid: boolean; /** * This property is `true` if the form control has at least one error. */ readonly isInvalid: boolean; /** * The errors of the form control. This property always has a value. * If the control has no errors the property is set to `{}`. */ readonly errors: ValidationErrors; /** * The names of all asynchronous validations currently running for the * form control. */ readonly pendingValidations: readonly string[]; /** * This property indicates whether the control is currently being * asynchronously validated (i.e. this is `true` if and only if * `pendingValidations` is not empty). */ readonly isValidationPending: boolean; /** * This property indicates whether the form control is enabled. * When it is `false` the `errors` are always `{}` (i.e. the form * control is always valid if disabled) and `pendingValidations` * is always `[]` (i.e. all pending validations are cancelled). */ readonly isEnabled: boolean; /** * This property indicates whether the form control is disabled. * When it is `true` the `errors` are always `{}` (i.e. the form * control is always valid if disabled) and `pendingValidations` * is always `[]` (i.e. all pending validations are cancelled). */ readonly isDisabled: boolean; /** * This property is set to `true` as soon as the underlying * `FormViewAdapter` or `ControlValueAccessor` reports a new * value for the first time. */ readonly isDirty: boolean; /** * This property is `true` as long as the underlying * `FormViewAdapter` or `ControlValueAccessor` has never * reported a new value. */ readonly isPristine: boolean; /** * This property is set to `true` based on the rules of the * underlying `FormViewAdapter` (usually on `blur` for most form * elements). */ readonly isTouched: boolean; /** * This property is `true` as long as the control is not touched. */ readonly isUntouched: boolean; /** * This property is set to `true` as soon as the group or array * containing this form control is submitted. A form control can * never be submitted on its own. */ readonly isSubmitted: boolean; /** * This property is `true` as long as the state is not submitted. */ readonly isUnsubmitted: boolean; /** * This property is set to `true` if the form control currently * has focus. This feature is opt-in. To enable it you have to * enable it for a given form element like this: * ```html <input [ngrxFormControlState]="state" [ngrxEnableFocusTracking]="true" /> ``` */ readonly isFocused: boolean; /** * This property is `true` if the control currently does not have * focus or focus tracking is not enabled for the form control. */ readonly isUnfocused: boolean; } /** * This type represents the child control states of a form group. */ export declare type FormGroupControls<TValue extends KeyValue> = { readonly [controlId in keyof TValue]: FormState<TValue[controlId]>; }; /** * Form groups are collections of named controls. Just like controls * groups are represented as plain state objects. The state of a * group is determined almost fully by its child states. */ export interface FormGroupState<TValue extends KeyValue> extends AbstractControlState<TValue> { /** * The aggregated value of the form group. The value is computed by * aggregating the values of all children, e.g. * ```typescript { child1: 'some value', child2: { nestedChild: 10, }, } ``` */ readonly value: TValue; /** * This property is `true` if the form group does not have any errors * itself and none of its children have any errors. */ readonly isValid: boolean; /** * This property is `true` if the form group or any of its children * have at least one error. */ readonly isInvalid: boolean; /** * The errors of the form group. This property is computed by merging * the errors of the group with the errors of all its children where * the child errors are a property of the `errors` object prefixed with * an underscore, e.g. * ``` { groupError: true, _child: { childError: true, }, } ``` * * If neither the group nor any children have errors the property is * set to `{}`. */ readonly errors: ValidationErrors; /** * The names of all asynchronous validations currently running for the * form group. */ readonly pendingValidations: readonly string[]; /** * This property indicates whether the group or any of its children * are currently being asynchronously validated. */ readonly isValidationPending: boolean; /** * This property indicates whether the form group is enabled. It is * `true` if and only if at least one of its child states is * enabled. When it is `false` the `errors` are always `{}` (i.e. * the form group is always valid if disabled) and `pendingValidations` * is always `[]` (i.e. all pending validations are cancelled). */ readonly isEnabled: boolean; /** * This property indicates whether the form group is disabled. It is * `true` if and only if all of its child state are disabled. When * it is `true` the `errors` are always `{}` (i.e. the form group * is always valid if disabled) and `pendingValidations` is always * `[]` (i.e. all pending validations are cancelled). */ readonly isDisabled: boolean; /** * This property is `true` if and only if at least one of the form * group's child states is marked as dirty. */ readonly isDirty: boolean; /** * This property is `true` if and only if all of the form group's * child states are pristine. */ readonly isPristine: boolean; /** * This property is `true` if and only if at least one of the form * group's child states is marked as touched. */ readonly isTouched: boolean; /** * This property is `true` if and only if all of the form group's * child states are untouched. */ readonly isUntouched: boolean; /** * This property is set to `true` as soon as the form group is * submitted. This is tracked by the `NgrxFormDirective`, which * needs to be applied to a form like this: * ```html <form [ngrxFormState]="groupState"> </form> ``` * * Note that applying this directive to a form prevents normal form * submission since that does not make much sense for ngrx forms. */ readonly isSubmitted: boolean; /** * This property is `true` as long as the state is not submitted. */ readonly isUnsubmitted: boolean; /** * This property contains all child states of the form group. As * you may have noticed the type of each child state is * `AbstractControlState` which sometimes forces you to cast the * state explicitly. It is not possible to improve this typing * until [conditional mapped types](https://github.com/Microsoft/TypeScript/issues/12424) * are added to TypeScript. */ readonly controls: FormGroupControls<TValue>; } /** * Form arrays are collections of controls. They are represented as * plain state arrays. The state of an array is determined almost * fully by its child states. */ export interface FormArrayState<TValue> extends AbstractControlState<readonly TValue[]> { /** * The aggregated value of the form array. The value is computed by * aggregating the values of all children into an array. */ readonly value: TValue[]; /** * This property is `true` if the form array does not have any errors * itself and none of its children have any errors. */ readonly isValid: boolean; /** * This property is `true` if the form array or any of its children * have at least one error. */ readonly isInvalid: boolean; /** * The errors of the form array. This property is computed by merging * the errors of the array with the errors of all its children where * the child errors are a property of the `errors` object prefixed with * an underscore, e.g. * ``` { arrayError: true, _0: { childError: true, }, } ``` * * If neither the array nor any children have errors the property is * set to `{}`. */ readonly errors: ValidationErrors; /** * The names of all asynchronous validations currently running for the * form array. */ readonly pendingValidations: readonly string[]; /** * This property indicates whether the array or any of its children * are currently being asynchronously validated. */ readonly isValidationPending: boolean; /** * This property indicates whether the form array is enabled. It is * `true` if and only if at least one of its child states is * enabled. When it is `false` the `errors` are always `{}` (i.e. * the form array is always valid if disabled) and `pendingValidations` * is always `[]` (i.e. all pending validations are cancelled). */ readonly isEnabled: boolean; /** * This property indicates whether the form array is disabled. It is * `true` if and only if all of its child states are disabled. When * it is `true` the `errors` are always `{}` (i.e. the form array * is always valid if disabled) and `pendingValidations` is always * `[]` (i.e. all pending validations are cancelled). */ readonly isDisabled: boolean; /** * This property is `true` if and only if at least one of the form * array's child states is marked as dirty. */ readonly isDirty: boolean; /** * This property is `true` if and only if all of the form array's * child states are pristine. */ readonly isPristine: boolean; /** * This property is `true` if and only if at least one of the form * array's child states is marked as touched. */ readonly isTouched: boolean; /** * This property is `true` if and only if all of the form array's * child states are untouched. */ readonly isUntouched: boolean; /** * This property is set to `true` as soon as the form array is * submitted. This is tracked by the `NgrxFormDirective`, which * needs to be applied to a form like this: * ```html <form [ngrxFormState]="arrayState"> </form> ``` * * Note that applying this directive to a form prevents normal form * submission since that does not make much sense for ngrx forms. */ readonly isSubmitted: boolean; /** * This property is `true` as long as the state is not submitted. */ readonly isUnsubmitted: boolean; /** * This property contains all child states of the form array. As * you may have noticed the type of each child state is * `AbstractControlState` which sometimes forces you to cast the * state explicitly. It is not possible to improve this typing * until [conditional mapped types](https://github.com/Microsoft/TypeScript/issues/12424) * are added to TypeScript. */ readonly controls: readonly FormState<TValue>[]; } /** * This is a helper type that allows working around the distributiveness * of conditional types. */ export interface InferenceWrapper<T> { t: T; } /** * This is a helper type that infers the correct form state type based * on the boxed type contained in the inference wrapper. */ export declare type InferredBoxedFormState<T extends InferenceWrapper<any>> = T extends InferenceWrapper<Boxed<infer U>> ? FormControlState<Boxed<U>> : T extends InferenceWrapper<Boxed<infer U> | undefined> ? FormControlState<Boxed<U> | undefined> : T extends InferenceWrapper<Boxed<infer U> | null> ? FormControlState<Boxed<U> | null> : T extends InferenceWrapper<Boxed<infer U> | undefined | null> ? FormControlState<Boxed<U> | undefined | null> : never; /** * This is a helper type that infers the correct form state type based * on the string type contained in the inference wrapper. */ export declare type InferredStringFormState<T extends InferenceWrapper<any>> = T extends InferenceWrapper<string> ? FormControlState<string> : T extends InferenceWrapper<string | undefined> ? FormControlState<string | undefined> : T extends InferenceWrapper<string | null> ? FormControlState<string | null> : T extends InferenceWrapper<string | undefined | null> ? FormControlState<string | undefined | null> : never; /** * This is a helper type that infers the correct form state type based * on the number type contained in the inference wrapper. */ export declare type InferredNumberFormState<T extends InferenceWrapper<any>> = T extends InferenceWrapper<number> ? FormControlState<number> : T extends InferenceWrapper<number | undefined> ? FormControlState<number | undefined> : T extends InferenceWrapper<number | null> ? FormControlState<number | null> : T extends InferenceWrapper<number | undefined | null> ? FormControlState<number | undefined | null> : never; /** * This is a helper type that infers the correct form state type based * on the boolean type contained in the inference wrapper. */ export declare type InferredBooleanFormState<T extends InferenceWrapper<any>> = T extends InferenceWrapper<boolean> ? FormControlState<boolean> : T extends InferenceWrapper<boolean | undefined> ? FormControlState<boolean | undefined> : T extends InferenceWrapper<boolean | null> ? FormControlState<boolean | null> : T extends InferenceWrapper<boolean | undefined | null> ? FormControlState<boolean | undefined | null> : never; /** * This is a helper type that infers the correct form state type based * on the type contained in the inference wrapper. */ export declare type InferredFormState<T extends InferenceWrapper<any>> = T extends InferenceWrapper<symbol> ? AbstractControlState<any> : T extends InferenceWrapper<undefined> ? AbstractControlState<any> : T extends InferenceWrapper<null> ? AbstractControlState<any> : T extends InferenceWrapper<Boxed<any> | undefined | null> ? InferredBoxedFormState<T> : T extends InferenceWrapper<string | undefined | null> ? InferredStringFormState<T> : T extends InferenceWrapper<number | undefined | null> ? InferredNumberFormState<T> : T extends InferenceWrapper<boolean | undefined | null> ? InferredBooleanFormState<T> : T extends InferenceWrapper<readonly (infer U)[] | undefined | null> ? FormArrayState<U> : T extends InferenceWrapper<infer U | undefined | null> ? U extends KeyValue ? FormGroupState<U> : never : never; /** * This is a type that can infer the concrete type of a form state based * on the given type parameter. */ export declare type FormState<T> = InferredFormState<InferenceWrapper<T>>; /** * This function determines if a value is a form state. */ export declare function isFormState<TValue = any>(state: any): state is FormState<TValue>; /** * This function determines if a value is an array state. */ export declare function isArrayState<TValue = any>(state: any): state is FormArrayState<TValue>; /** * This function determines if a value is a group state. */ export declare function isGroupState<TValue extends KeyValue = any>(state: any): state is FormGroupState<TValue>; export declare function createChildState<TValue>(id: string, childValue: TValue): FormState<TValue>; export declare function verifyFormControlValueIsValid<TValue>(value: TValue): TValue; /** * This function creates a form control state with an ID and a value. */ export declare function createFormControlState<TValue extends FormControlValueTypes>(id: NgrxFormControlId, value: TValue): FormControlState<TValue>; export declare function getFormGroupValue<TValue extends KeyValue>(controls: FormGroupControls<TValue>, originalValue: TValue): TValue; export declare function getFormGroupErrors<TValue extends KeyValue>(controls: FormGroupControls<TValue>, originalErrors: ValidationErrors): ValidationErrors; export declare function computeGroupState<TValue extends KeyValue>(id: string, controls: FormGroupControls<TValue>, value: TValue, errors: ValidationErrors, pendingValidations: readonly string[], userDefinedProperties: KeyValue, flags: { wasOrShouldBeDirty?: boolean; wasOrShouldBeEnabled?: boolean; wasOrShouldBeTouched?: boolean; wasOrShouldBeSubmitted?: boolean; }): FormGroupState<TValue>; /** * This function creates a form group state with an ID and a value. * From the value the shape of the group state is inferred, i.e. * object properties are inferred as form groups, array properties * are inferred as form arrays, and primitive properties are inferred * as form controls. */ export declare function createFormGroupState<TValue extends KeyValue>(id: NgrxFormControlId, initialValue: TValue): FormGroupState<TValue>; export declare function computeArrayState<TValue>(id: string, inferredControls: readonly FormState<TValue>[], value: TValue[], errors: ValidationErrors, pendingValidations: readonly string[], userDefinedProperties: KeyValue, flags: { wasOrShouldBeDirty?: boolean; wasOrShouldBeEnabled?: boolean; wasOrShouldBeTouched?: boolean; wasOrShouldBeSubmitted?: boolean; }): FormArrayState<TValue>; /** * This function creates a form array state with an ID and a value. * From the value the shape of the array state is inferred, i.e. * object values are inferred as form groups, array values * are inferred as form arrays, and primitive values are inferred * as form controls. */ export declare function createFormArrayState<TValue>(id: NgrxFormControlId, initialValue: TValue[]): FormArrayState<TValue>;