diff --git a/packages/forms/src/directives/abstract_control_directive.ts b/packages/forms/src/directives/abstract_control_directive.ts index c0d049bfc8..1a682ec473 100644 --- a/packages/forms/src/directives/abstract_control_directive.ts +++ b/packages/forms/src/directives/abstract_control_directive.ts @@ -11,153 +11,181 @@ import {AbstractControl} from '../model'; import {ValidationErrors} from './validators'; /** + * @description * Base class for control directives. * - * Only used internally in the forms module. - * + * This class is only used internally in the `FormsModule`. * */ export abstract class AbstractControlDirective { /** - * The `FormControl`, `FormGroup`, or `FormArray` - * that backs this directive. Most properties fall through to that - * instance. + * @description + * A reference to the underlying control. + * + * @returns the control that backs this directive. Most properties fall through to that instance. */ abstract get control(): AbstractControl|null; - /** The value of the control. */ + /** + * @description + * The value of the control. + * + * @returns The value of the control if it is present, otherwise null. + */ get value(): any { return this.control ? this.control.value : null; } /** - * A control is `valid` when its `status === VALID`. + * @description + * Reports that the control is valid, meaning that no errors exist in the input value. * - * In order to have this status, the control must have passed all its - * validation checks. + * @returns The control's valid state if the control is present, otherwise null. */ get valid(): boolean|null { return this.control ? this.control.valid : null; } /** - * A control is `invalid` when its `status === INVALID`. + * @description + * Reports that the control is invalid, meaning that an error exists in the input value. * - * In order to have this status, the control must have failed - * at least one of its validation checks. + * @returns The control's invalid state if the control is present, otherwise null. */ get invalid(): boolean|null { return this.control ? this.control.invalid : null; } /** - * A control is `pending` when its `status === PENDING`. + * @description + * Reports that a control is pending, meaning that that async validation is occurring and + * errors are not yet available for the input value. * - * In order to have this status, the control must be in the - * middle of conducting a validation check. + * @returns The control's pending state if the control is present, otherwise null. */ get pending(): boolean|null { return this.control ? this.control.pending : null; } /** - * A control is `disabled` when its `status === DISABLED`. + * @description + * Reports that the control is disabled, meaning that the control is exempt from ancestor + * calculations of validity or value. * - * Disabled controls are exempt from validation checks and - * are not included in the aggregate value of their ancestor - * controls. + * @returns The control's disabled state if the control is present, otherwise null. */ get disabled(): boolean|null { return this.control ? this.control.disabled : null; } /** - * A control is `enabled` as long as its `status !== DISABLED`. + * @description + * Reports that the control is enabled, meaning that the control is included in ancestor + * calculations of validity or value. * - * In other words, it has a status of `VALID`, `INVALID`, or - * `PENDING`. + * @returns The control's enabled state if the control is present, otherwise null. */ get enabled(): boolean|null { return this.control ? this.control.enabled : null; } /** - * Returns any errors generated by failing validation. If there - * are no errors, it will return null. + * @description + * Reports the FormControl validation errors. + * + * @returns The control's validation errors if the control is present, otherwise null. */ get errors(): ValidationErrors|null { return this.control ? this.control.errors : null; } /** - * A control is `pristine` if the user has not yet changed + * @description + * Reports that the control is pristine, meaning that the control the user has not yet changed * the value in the UI. * - * Note that programmatic changes to a control's value will - * *not* mark it dirty. + * @returns The control's pristine state if the control is present, otherwise null. */ get pristine(): boolean|null { return this.control ? this.control.pristine : null; } /** - * A control is `dirty` if the user has changed the value - * in the UI. + * @description + * Reports that the control is dirty, meaning that the control the user has changed + * the value in the UI. * - * Note that programmatic changes to a control's value will - * *not* mark it dirty. + * @returns The control's dirty state if the control is present, otherwise null. */ get dirty(): boolean|null { return this.control ? this.control.dirty : null; } /** - * A control is marked `touched` once the user has triggered + * @description + * Reports that the control is touched, meaning that the the user has triggered * a `blur` event on it. + * + * @returns The control's touched state if the control is present, otherwise null. */ get touched(): boolean|null { return this.control ? this.control.touched : null; } + /** + * @description + * Reports that a FormControl is touched, meaning that the the user has triggered + * a `blur` event on it. + * + * @returns The control's touched state if the control is present, otherwise null. + */ get status(): string|null { return this.control ? this.control.status : null; } /** - * A control is `untouched` if the user has not yet triggered + * @description + * Reports that a FormControl is untouched, meaning that the the user has not yet triggered * a `blur` event on it. + * + * @returns The control's untouched state if the control is present, otherwise null. */ get untouched(): boolean|null { return this.control ? this.control.untouched : null; } /** - * Emits an event every time the validation status of the control - * is re-calculated. + * @description + * Reports the observable of status changes for the control. + * + * @returns An observable that emits every time the validation status of the control + * is re-calculated if the control is present, otherwise null. */ get statusChanges(): Observable|null { return this.control ? this.control.statusChanges : null; } /** - * Emits an event every time the value of the control changes, in - * the UI or programmatically. + * @description + * Reports the observable of value changes for the control. + * + * @returns An observable that emits every time the value of the control + * changes in the UI or programmatically if the control is present, otherwise null. */ get valueChanges(): Observable|null { return this.control ? this.control.valueChanges : null; } /** - * Returns an array that represents the path from the top-level form - * to this control. Each index is the string name of the control on - * that level. + * @description + * Reports an array that represents the path from the top-level form to this control. + * Each index is the string name of the control on that level. + * */ get path(): string[]|null { return null; } /** - * Resets the form control. This means by default: + * @description + * Resets the control with the provided value if the control is present * - * * it is marked as `pristine` - * * it is marked as `untouched` - * * value is set to null - * - * For more information, see `AbstractControl`. */ reset(value: any = undefined): void { if (this.control) this.control.reset(value); } /** - * Returns true if the control with the given path has the error specified. Otherwise - * returns false. - * + * @description + * Reports whether the control with the given path has the error specified. * If no path is given, it checks for the error on the present control. + * + * @returns True if the control is present and has the error specified, otherwise false */ hasError(errorCode: string, path?: string[]): boolean { return this.control ? this.control.hasError(errorCode, path) : false; } /** - * Returns error data if the control with the given path has the error specified. Otherwise + * @description + * Reports error data for the control with the given path. Otherwise * returns null or undefined. * - * If no path is given, it checks for the error on the present control. + * @returns The control's error if the control is present, otherwise null */ getError(errorCode: string, path?: string[]): any { return this.control ? this.control.getError(errorCode, path) : null; diff --git a/packages/forms/src/directives/abstract_form_group_directive.ts b/packages/forms/src/directives/abstract_form_group_directive.ts index ff1cf32125..3598a29473 100644 --- a/packages/forms/src/directives/abstract_form_group_directive.ts +++ b/packages/forms/src/directives/abstract_form_group_directive.ts @@ -18,28 +18,53 @@ import {AsyncValidatorFn, ValidatorFn} from './validators'; /** - * This is a base class for code shared between `NgModelGroup` and `FormGroupName`. - * + * @description + * A base class for code shared between the `NgModelGroup` and `FormGroupName` directives. * */ export class AbstractFormGroupDirective extends ControlContainer implements OnInit, OnDestroy { - /** @internal */ + /** + * @description + * The parent control for the group + * + * @internal + */ // TODO(issue/24571): remove '!'. _parent !: ControlContainer; - /** @internal */ + /** + * @description + * An array of synchronous validators for the group + * + * @internal + */ // TODO(issue/24571): remove '!'. _validators !: any[]; - /** @internal */ + /** + * @description + * An array of async validators for the group + * + * @internal + */ // TODO(issue/24571): remove '!'. _asyncValidators !: any[]; + /** + * @description + * The callback method triggered on the instance after the inputs are set. + * Registers the group with its parent group. + */ ngOnInit(): void { this._checkParentType(); this.formDirective !.addFormGroup(this); } + /** + * @description + * The callback method trigger before the instance is destroyed. + * Removes the group from its parent group. + */ ngOnDestroy(): void { if (this.formDirective) { this.formDirective.removeFormGroup(this); @@ -47,22 +72,35 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn } /** - * Get the `FormGroup` backing this binding. + * @description + * Reports the `FormGroup` bound to this directive from its parent */ get control(): FormGroup { return this.formDirective !.getFormGroup(this); } /** - * Get the path to this control group. + * @description + * Reports the path to this group */ get path(): string[] { return controlPath(this.name, this._parent); } /** - * Get the `Form` to which this group belongs. + * @description + * Reports the parent form for this group + * + * @returns The parent form if present, otherwise null. */ get formDirective(): Form|null { return this._parent ? this._parent.formDirective : null; } + /** + * @description + * Reports the synchronous validators registered with this group + */ get validator(): ValidatorFn|null { return composeValidators(this._validators); } + /** + * @description + * Reports the async validators registered with this group + */ get asyncValidator(): AsyncValidatorFn|null { return composeAsyncValidators(this._asyncValidators); } diff --git a/packages/forms/src/directives/control_container.ts b/packages/forms/src/directives/control_container.ts index 1f5cd0293f..a6529386a9 100644 --- a/packages/forms/src/directives/control_container.ts +++ b/packages/forms/src/directives/control_container.ts @@ -11,23 +11,27 @@ import {Form} from './form_interface'; /** - * A directive that contains multiple `NgControl`s. - * + * @description + * A base class for directives that contain multiple registered instances of `NgControl`. * Only used by the forms module. - * - * */ export abstract class ControlContainer extends AbstractControlDirective { + /** + * @description + * The name for the control + */ // TODO(issue/24571): remove '!'. name !: string; /** - * Get the form to which this container belongs. + * @description + * Reports the parent form for the control */ get formDirective(): Form|null { return null; } /** - * Get the path to this container. + * @description + * Reports the path to this group */ get path(): string[]|null { return null; } } diff --git a/packages/forms/src/directives/control_value_accessor.ts b/packages/forms/src/directives/control_value_accessor.ts index 569647c579..30f3d90147 100644 --- a/packages/forms/src/directives/control_value_accessor.ts +++ b/packages/forms/src/directives/control_value_accessor.ts @@ -9,70 +9,85 @@ import {InjectionToken} from '@angular/core'; /** - * A `ControlValueAccessor` acts as a bridge between the Angular forms API and a + * @description + * Defines an interface that acts as a bridge between the Angular forms API and a * native element in the DOM. * - * Implement this interface if you want to create a custom form control directive + * Implement this interface to create a custom form control directive * that integrates with Angular forms. - * - * + * + * @see DefaultValueAccessor */ export interface ControlValueAccessor { /** + * @description * Writes a new value to the element. * - * This method will be called by the forms API to write to the view when programmatic - * (model -> view) changes are requested. + * This method is called by the forms API to write to the view when programmatic + * changes from model to view are requested. * - * Example implementation of `writeValue`: + * ### Write a value to the element + * + * The following example writes a value to the native DOM element. * * ```ts * writeValue(value: any): void { * this._renderer.setProperty(this._elementRef.nativeElement, 'value', value); * } * ``` + * + * @param obj The new value for the element */ writeValue(obj: any): void; /** - * Registers a callback function that should be called when the control's value + * @description + * Registers a callback function that is called when the control's value * changes in the UI. * - * This is called by the forms API on initialization so it can update the form - * model when values propagate from the view (view -> model). + * This method is called by the forms API on initialization to update the form + * model when values propagate from the view to the model. * - * If you are implementing `registerOnChange` in your own value accessor, you - * will typically want to save the given function so your class can call it - * at the appropriate time. + * When implementing the `registerOnChange` method in your own value accessor, + * save the given function so your class calls it at the appropriate time. * - * ```ts + * ### Store the change function + * + * The following example stores the provided function as an internal method. + * + * ```typescript * registerOnChange(fn: (_: any) => void): void { * this._onChange = fn; * } * ``` * - * When the value changes in the UI, your class should call the registered + * When the value changes in the UI, call the registered * function to allow the forms API to update itself: * - * ```ts + * ```typescript * host: { * (change): '_onChange($event.target.value)' * } * ``` * + * @param fn The callback function to register */ registerOnChange(fn: any): void; /** - * Registers a callback function that should be called when the control receives - * a blur event. + * @description + * Registers a callback function that is called when the control receives a blur event. * * This is called by the forms API on initialization so it can update the form model * on blur. * - * If you are implementing `registerOnTouched` in your own value accessor, you - * will typically want to save the given function so your class can call it - * when the control should be considered blurred (a.k.a. "touched"). + * When implementing `registerOnTouched` in your own value accessor, save the given + * function so your class calls it when the control should be considered + * blurred or "touched". + * + * ### Store the callback function + * + * The folliwing example stores the provided function as an internal method. * * ```ts * registerOnTouched(fn: any): void { @@ -80,7 +95,7 @@ export interface ControlValueAccessor { * } * ``` * - * On blur (or equivalent), your class should call the registered function to allow + * On blur (or equivalent), your class calls the registered function to allow * the forms API to update itself: * * ```ts @@ -88,15 +103,18 @@ export interface ControlValueAccessor { * '(blur)': '_onTouched()' * } * ``` + * + * @param fn The callback function to register */ registerOnTouched(fn: any): void; /** - * This function is called by the forms API when the control status changes to - * or from "DISABLED". Depending on the value, it should enable or disable the + * @description + * Registers a callback function that is called when the control status is disabled or + * re-enabled. Depending on the status, it enables or disables the * appropriate DOM element. * - * Example implementation of `setDisabledState`: + * The following is an example of writing the disabled property to a native DOM element: * * ```ts * setDisabledState(isDisabled: boolean): void { @@ -104,7 +122,7 @@ export interface ControlValueAccessor { * } * ``` * - * @param isDisabled + * @param isDisabled The disabled status to set on the element */ setDisabledState?(isDisabled: boolean): void; } diff --git a/packages/forms/src/directives/form_interface.ts b/packages/forms/src/directives/form_interface.ts index d2790aa1d8..1a52b623cc 100644 --- a/packages/forms/src/directives/form_interface.ts +++ b/packages/forms/src/directives/form_interface.ts @@ -14,45 +14,66 @@ import {NgControl} from './ng_control'; /** - * An interface that `FormGroupDirective` and `NgForm` implement. - * - * Only used by the forms module. - * + * @description + * An interface implemented by `FormGroupDirective` and `NgForm` directives. * + * Only used by the `FormsModule`. */ export interface Form { /** + * @description * Add a control to this form. + * + * @param dir The control to add to the form */ addControl(dir: NgControl): void; /** + * @description * Remove a control from this form. + * + * @param dir: The control to remove from the form */ removeControl(dir: NgControl): void; /** - * Look up the `FormControl` associated with a particular `NgControl`. + * @description + * Reports the `FormControl` associated with the provided `NgControl`. + * + * @param dir: The form control instance */ getControl(dir: NgControl): FormControl; /** + * @description * Add a group of controls to this form. + * + * @param dir: The control group to remove */ addFormGroup(dir: AbstractFormGroupDirective): void; /** - * Remove a group of controls from this form. + * @description + * Remove a group of controls to this form. + * + * @param dir: The control group to remove */ removeFormGroup(dir: AbstractFormGroupDirective): void; /** - * Look up the `FormGroup` associated with a particular `AbstractFormGroupDirective`. + * @description + * Reports the form group for the provided control + * + * @param dir: The form group to query */ getFormGroup(dir: AbstractFormGroupDirective): FormGroup; /** + * @description * Update the model for a particular control with a new value. + * + * @param dir: The control to update + * @param value: The new value for the control */ updateModel(dir: NgControl, value: any): void; } diff --git a/packages/forms/src/directives/ng_control.ts b/packages/forms/src/directives/ng_control.ts index b5b028c7de..4becf75adb 100644 --- a/packages/forms/src/directives/ng_control.ts +++ b/packages/forms/src/directives/ng_control.ts @@ -17,25 +17,67 @@ function unimplemented(): any { } /** - * A base class that all control directive extend. - * It binds a `FormControl` object to a DOM element. - * - * Used internally by Angular forms. - * - * + * @description + * A base class that all control directives extend. It binds a `FormControl` object to a + * DOM element and is only used internally by Angular forms. */ export abstract class NgControl extends AbstractControlDirective { - /** @internal */ + /** + * @description + * The parent form for the control + * @internal + */ _parent: ControlContainer|null = null; + + /** + * @description + * The name for the control + */ name: string|null = null; + + /** + * @description + * The value accessor for the control + */ valueAccessor: ControlValueAccessor|null = null; - /** @internal */ + + /** + * @description + * The uncomposed array of synchronous validators for the control + * + * @internal + */ _rawValidators: Array = []; - /** @internal */ + + /** + * @description + * The uncomposed array of async validators for the control + * + * @internal + */ _rawAsyncValidators: Array = []; + /** + * @description + * The registered synchronous validator function for the control + * + * @throws An exception that this method is not implemented + */ get validator(): ValidatorFn|null { return unimplemented(); } + + /** + * @description + * The registered async validator function for the control + * + * @throws An exception that this method is not implemented + */ get asyncValidator(): AsyncValidatorFn|null { return unimplemented(); } + /** + * @description + * The callback method to update the model from the view when requested + * + * @param newValue The new value for the view + */ abstract viewToModelUpdate(newValue: any): void; } diff --git a/packages/forms/src/directives/validators.ts b/packages/forms/src/directives/validators.ts index 2297c42b58..5b615d0f1b 100644 --- a/packages/forms/src/directives/validators.ts +++ b/packages/forms/src/directives/validators.ts @@ -13,15 +13,26 @@ import {AbstractControl} from '../model'; import {NG_VALIDATORS, Validators} from '../validators'; -/** @experimental */ +/** + * @description + * Defines the map of errors returned from failed validation checks + * + * @experimental + */ export type ValidationErrors = { [key: string]: any }; /** - * An interface that can be implemented by classes that can act as validators. + * @description + * An interface implemented by classes that perform synchronous validation. * - * ## Usage + * @usageNotes + * + * ### Provide a custom validator + * + * The following example implements the `Validator` interface to create a + * validator directive with a custom error key. * * ```typescript * @Directive({ @@ -29,21 +40,71 @@ export type ValidationErrors = { * providers: [{provide: NG_VALIDATORS, useExisting: CustomValidatorDirective, multi: true}] * }) * class CustomValidatorDirective implements Validator { - * validate(c: Control): {[key: string]: any} { - * return {"custom": true}; + * validate(c: AbstractControl): {[key: string]: any} { + * return {'custom': true}; + * } + * } + * ``` + */ +export interface Validator { + /** + * @description + * Method that performs synchronous verification against the provided control. + * + * @param c The control to validate against. + * + * @returns A map of validation errors if validation fails, + * otherwise null. + */ + validate(c: AbstractControl): ValidationErrors|null; + + /** + * @description + * Registers a callback function to call when the validator inputs change. + * + * @param fn The callback function + */ + registerOnValidatorChange?(fn: () => void): void; +} + +/** + * @description + * An interface implemented by classes that perform asynchronous validation. + * + * @usageNotes + * + * ### Provide a custom async validator directive + * + * The following example implements the `AsyncValidator` interface to create an + * async validator directive with a custom error key. + * + * ```typescript + * import { of as observableOf } from 'rxjs'; + * + * @Directive({ + * selector: '[custom-async-validator]', + * providers: [{provide: NG_ASYNC_VALIDATORS, useExisting: CustomAsyncValidatorDirective, multi: + * true}] + * }) + * class CustomAsyncValidatorDirective implements AsyncValidator { + * validate(c: AbstractControl): Observable<{[key: string]: any}> { + * return observableOf({'custom': true}); * } * } * ``` * - * + * @experimental */ -export interface Validator { - validate(c: AbstractControl): ValidationErrors|null; - registerOnValidatorChange?(fn: () => void): void; -} - -/** @experimental */ export interface AsyncValidator extends Validator { + /** + * @description + * Method that performs async verification against the provided control. + * + * @param c The control to validate against. + * + * @returns A promise or observable that resolves a map of validation errors + * if validation fails, otherwise null. + */ validate(c: AbstractControl): Promise|Observable; }