ngrx-forms
Version:
Proper integration of forms in Angular 4 applications using ngrx
525 lines (524 loc) • 22.5 kB
TypeScript
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>;