From 21516c32e63451908aa7c2ec81a790b625c42fc5 Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Mon, 12 Sep 2016 20:31:13 -0700 Subject: [PATCH] docs(forms): add docs for AbstractControl --- modules/@angular/forms/src/model.ts | 255 ++++++++++++++++++++++++---- 1 file changed, 219 insertions(+), 36 deletions(-) diff --git a/modules/@angular/forms/src/model.ts b/modules/@angular/forms/src/model.ts index 20a110cb98..a866b2cf31 100644 --- a/modules/@angular/forms/src/model.ts +++ b/modules/@angular/forms/src/model.ts @@ -76,6 +76,14 @@ function coerceToAsyncValidator(asyncValidator: AsyncValidatorFn | AsyncValidato } /** + * @whatItDoes This is the base class for {@link FormControl}, {@link FormGroup}, and + * {@link FormArray}. + * + * It provides some of the shared behavior that all controls and groups of controls have, like + * running validators, calculating status, and resetting state. It also defines the properties + * that are shared between all sub-classes, like `value`, `valid`, and `dirty`. It shouldn't be + * instantiated directly. + * * @stable */ export abstract class AbstractControl { @@ -95,49 +103,146 @@ export abstract class AbstractControl { constructor(public validator: ValidatorFn, public asyncValidator: AsyncValidatorFn) {} + /** + * The value of the control. + */ get value(): any { return this._value; } + /** + * The validation status of the control. There are four possible + * validation statuses: + * + * * **VALID**: control has passed all validation checks + * * **INVALID**: control has failed at least one validation check + * * **PENDING**: control is in the midst of conducting a validation check + * * **DISABLED**: control is exempt from validation checks + * + * These statuses are mutually exclusive, so a control cannot be + * both valid AND invalid or invalid AND disabled. + */ get status(): string { return this._status; } + /** + * A control is `valid` when its `status === VALID`. + * + * In order to have this status, the control must have passed all its + * validation checks. + */ get valid(): boolean { return this._status === VALID; } + /** + * A control is `invalid` when its `status === INVALID`. + * + * In order to have this status, the control must have failed + * at least one of its validation checks. + */ get invalid(): boolean { return this._status === INVALID; } /** - * Returns the errors of this control. + * A control is `pending` when its `status === PENDING`. + * + * In order to have this status, the control must be in the + * middle of conducting a validation check. + */ + get pending(): boolean { return this._status == PENDING; } + + /** + * A control is `disabled` when its `status === DISABLED`. + * + * Disabled controls are exempt from validation checks and + * are not included in the aggregate value of their ancestor + * controls. + */ + get disabled(): boolean { return this._status === DISABLED; } + + /** + * A control is `enabled` as long as its `status !== DISABLED`. + * + * In other words, it has a status of `VALID`, `INVALID`, or + * `PENDING`. + */ + get enabled(): boolean { return this._status !== DISABLED; } + + /** + * Returns any errors generated by failing validation. If there + * are no errors, it will return null. */ get errors(): {[key: string]: any} { return this._errors; } + /** + * A control is `pristine` if 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. + */ get pristine(): boolean { return this._pristine; } + /** + * A control is `dirty` if the user has changed the value + * in the UI. + * + * Note that programmatic changes to a control's value will + * *not* mark it dirty. + */ get dirty(): boolean { return !this.pristine; } + /** + * A control is marked `touched` once the user has triggered + * a `blur` event on it. + */ get touched(): boolean { return this._touched; } + /** + * A control is `untouched` if the user has not yet triggered + * a `blur` event on it. + */ get untouched(): boolean { return !this._touched; } + /** + * Emits an event every time the value of the control changes, in + * the UI or programmatically. + */ get valueChanges(): Observable { return this._valueChanges; } + /** + * Emits an event every time the validation status of the control + * is re-calculated. + */ get statusChanges(): Observable { return this._statusChanges; } - get pending(): boolean { return this._status == PENDING; } - - get disabled(): boolean { return this._status === DISABLED; } - - get enabled(): boolean { return this._status !== DISABLED; } - - setAsyncValidators(newValidator: AsyncValidatorFn|AsyncValidatorFn[]): void { - this.asyncValidator = coerceToAsyncValidator(newValidator); - } - - clearAsyncValidators(): void { this.asyncValidator = null; } - + /** + * Sets the synchronous validators that are active on this control. Calling + * this will overwrite any existing sync validators. + */ setValidators(newValidator: ValidatorFn|ValidatorFn[]): void { this.validator = coerceToValidator(newValidator); } + /** + * Sets the async validators that are active on this control. Calling this + * will overwrite any existing async validators. + */ + setAsyncValidators(newValidator: AsyncValidatorFn|AsyncValidatorFn[]): void { + this.asyncValidator = coerceToAsyncValidator(newValidator); + } + + /** + * Empties out the sync validator list. + */ clearValidators(): void { this.validator = null; } + /** + * Empties out the async validator list. + */ + clearAsyncValidators(): void { this.asyncValidator = null; } + + /** + * Marks the control as `touched`. + * + * This will also mark all direct ancestors as `touched` to maintain + * the model. + */ markAsTouched({onlySelf}: {onlySelf?: boolean} = {}): void { onlySelf = normalizeBool(onlySelf); this._touched = true; @@ -147,25 +252,13 @@ export abstract class AbstractControl { } } - markAsDirty({onlySelf}: {onlySelf?: boolean} = {}): void { - onlySelf = normalizeBool(onlySelf); - this._pristine = false; - - if (isPresent(this._parent) && !onlySelf) { - this._parent.markAsDirty({onlySelf: onlySelf}); - } - } - - markAsPristine({onlySelf}: {onlySelf?: boolean} = {}): void { - this._pristine = true; - - this._forEachChild((control: AbstractControl) => { control.markAsPristine({onlySelf: true}); }); - - if (isPresent(this._parent) && !onlySelf) { - this._parent._updatePristine({onlySelf: onlySelf}); - } - } - + /** + * Marks the control as `untouched`. + * + * If the control has any children, it will also mark all children as `untouched` + * to maintain the model, and re-calculate the `touched` status of all parent + * controls. + */ markAsUntouched({onlySelf}: {onlySelf?: boolean} = {}): void { this._touched = false; @@ -177,6 +270,41 @@ export abstract class AbstractControl { } } + /** + * Marks the control as `dirty`. + * + * This will also mark all direct ancestors as `dirty` to maintain + * the model. + */ + markAsDirty({onlySelf}: {onlySelf?: boolean} = {}): void { + onlySelf = normalizeBool(onlySelf); + this._pristine = false; + + if (isPresent(this._parent) && !onlySelf) { + this._parent.markAsDirty({onlySelf: onlySelf}); + } + } + + /** + * Marks the control as `pristine`. + * + * If the control has any children, it will also mark all children as `pristine` + * to maintain the model, and re-calculate the `pristine` status of all parent + * controls. + */ + markAsPristine({onlySelf}: {onlySelf?: boolean} = {}): void { + this._pristine = true; + + this._forEachChild((control: AbstractControl) => { control.markAsPristine({onlySelf: true}); }); + + if (isPresent(this._parent) && !onlySelf) { + this._parent._updatePristine({onlySelf: onlySelf}); + } + } + + /** + * Marks the control as `pending`. + */ markAsPending({onlySelf}: {onlySelf?: boolean} = {}): void { onlySelf = normalizeBool(onlySelf); this._status = PENDING; @@ -186,6 +314,12 @@ export abstract class AbstractControl { } } + /** + * Disables the control. This means the control will be exempt from validation checks and + * excluded from the aggregate value of any parent. Its status is `DISABLED`. + * + * If the control has children, all children will be disabled to maintain the model. + */ disable({onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}): void { emitEvent = isPresent(emitEvent) ? emitEvent : true; @@ -203,6 +337,13 @@ export abstract class AbstractControl { this._onDisabledChange(true); } + /** + * Enables the control. This means the control will be included in validation checks and + * the aggregate value of its parent. Its status is re-calculated based on its value and + * its validators. + * + * If the control has children, all children will be enabled. + */ enable({onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}): void { this._status = VALID; this._forEachChild((control: AbstractControl) => { control.enable({onlySelf: true}); }); @@ -222,12 +363,26 @@ export abstract class AbstractControl { setParent(parent: FormGroup|FormArray): void { this._parent = parent; } + /** + * Sets the value of the control. Abstract method (implemented in sub-classes). + */ abstract setValue(value: any, options?: Object): void; + /** + * Patches the value of the control. Abstract method (implemented in sub-classes). + */ abstract patchValue(value: any, options?: Object): void; + /** + * Resets the control. Abstract method (implemented in sub-classes). + */ abstract reset(value?: any, options?: Object): void; + /** + * Re-calculates the value and validation status of the control. + * + * By default, it will also update the value and validity of its ancestors. + */ updateValueAndValidity({onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}): void { onlySelf = normalizeBool(onlySelf); @@ -286,14 +441,14 @@ export abstract class AbstractControl { /** * Sets errors on a form control. * - * This is used when validations are run not automatically, but manually by the user. + * This is used when validations are run manually by the user, rather than automatically. * * Calling `setErrors` will also update the validity of the parent control. * - * ## Usage + * ### Example * * ``` - * var login = new FormControl("someLogin"); + * const login = new FormControl("someLogin"); * login.setErrors({ * "notUnique": true * }); @@ -301,7 +456,7 @@ export abstract class AbstractControl { * expect(login.valid).toEqual(false); * expect(login.errors).toEqual({"notUnique": true}); * - * login.updateValue("someOtherLogin"); + * login.setValue("someOtherLogin"); * * expect(login.valid).toEqual(true); * ``` @@ -313,8 +468,27 @@ export abstract class AbstractControl { this._updateControlsErrors(emitEvent); } + /** + * Retrieves a child control given the control's name or path. + * + * Paths can be passed in as an array or a string delimited by a dot. + * + * To get a control nested within a `person` sub-group: + * + * * `this.form.get('person.name');` + * + * -OR- + * + * * `this.form.get(['person', 'name']);` + */ get(path: Array|string): AbstractControl { return _find(this, path, '.'); } + /** + * Returns true if the control with the given path has the error specified. Otherwise + * returns null or undefined. + * + * If no path is given, it checks for the error on the present control. + */ getError(errorCode: string, path: string[] = null): any { var control = isPresent(path) && !ListWrapper.isEmpty(path) ? this.get(path) : this; if (isPresent(control) && isPresent(control._errors)) { @@ -324,10 +498,19 @@ export abstract class AbstractControl { } } + /** + * Returns true if the control with the given path has the error specified. Otherwise + * returns false. + * + * If no path is given, it checks for the error on the present control. + */ hasError(errorCode: string, path: string[] = null): boolean { return isPresent(this.getError(errorCode, path)); } + /** + * Retrieves the top-level ancestor of this control. + */ get root(): AbstractControl { let x: AbstractControl = this;