docs(forms): add docs for AbstractControl

This commit is contained in:
Kara Erickson 2016-09-12 20:31:13 -07:00 committed by Evan Martin
parent 00a24b63da
commit 21516c32e6

View File

@ -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 * @stable
*/ */
export abstract class AbstractControl { export abstract class AbstractControl {
@ -95,49 +103,146 @@ export abstract class AbstractControl {
constructor(public validator: ValidatorFn, public asyncValidator: AsyncValidatorFn) {} constructor(public validator: ValidatorFn, public asyncValidator: AsyncValidatorFn) {}
/**
* The value of the control.
*/
get value(): any { return this._value; } 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; } 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; } 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; } 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; } 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; } 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; } 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; } 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; } 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<any> { return this._valueChanges; } get valueChanges(): Observable<any> { return this._valueChanges; }
/**
* Emits an event every time the validation status of the control
* is re-calculated.
*/
get statusChanges(): Observable<any> { return this._statusChanges; } get statusChanges(): Observable<any> { return this._statusChanges; }
get pending(): boolean { return this._status == PENDING; } /**
* Sets the synchronous validators that are active on this control. Calling
get disabled(): boolean { return this._status === DISABLED; } * this will overwrite any existing sync validators.
*/
get enabled(): boolean { return this._status !== DISABLED; }
setAsyncValidators(newValidator: AsyncValidatorFn|AsyncValidatorFn[]): void {
this.asyncValidator = coerceToAsyncValidator(newValidator);
}
clearAsyncValidators(): void { this.asyncValidator = null; }
setValidators(newValidator: ValidatorFn|ValidatorFn[]): void { setValidators(newValidator: ValidatorFn|ValidatorFn[]): void {
this.validator = coerceToValidator(newValidator); 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; } 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 { markAsTouched({onlySelf}: {onlySelf?: boolean} = {}): void {
onlySelf = normalizeBool(onlySelf); onlySelf = normalizeBool(onlySelf);
this._touched = true; this._touched = true;
@ -147,25 +252,13 @@ export abstract class AbstractControl {
} }
} }
markAsDirty({onlySelf}: {onlySelf?: boolean} = {}): void { /**
onlySelf = normalizeBool(onlySelf); * Marks the control as `untouched`.
this._pristine = false; *
* If the control has any children, it will also mark all children as `untouched`
if (isPresent(this._parent) && !onlySelf) { * to maintain the model, and re-calculate the `touched` status of all parent
this._parent.markAsDirty({onlySelf: onlySelf}); * 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});
}
}
markAsUntouched({onlySelf}: {onlySelf?: boolean} = {}): void { markAsUntouched({onlySelf}: {onlySelf?: boolean} = {}): void {
this._touched = false; 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 { markAsPending({onlySelf}: {onlySelf?: boolean} = {}): void {
onlySelf = normalizeBool(onlySelf); onlySelf = normalizeBool(onlySelf);
this._status = PENDING; 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 { disable({onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
emitEvent = isPresent(emitEvent) ? emitEvent : true; emitEvent = isPresent(emitEvent) ? emitEvent : true;
@ -203,6 +337,13 @@ export abstract class AbstractControl {
this._onDisabledChange(true); 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 { enable({onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
this._status = VALID; this._status = VALID;
this._forEachChild((control: AbstractControl) => { control.enable({onlySelf: true}); }); this._forEachChild((control: AbstractControl) => { control.enable({onlySelf: true}); });
@ -222,12 +363,26 @@ export abstract class AbstractControl {
setParent(parent: FormGroup|FormArray): void { this._parent = parent; } 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; 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; abstract patchValue(value: any, options?: Object): void;
/**
* Resets the control. Abstract method (implemented in sub-classes).
*/
abstract reset(value?: any, options?: Object): void; 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} = {}): updateValueAndValidity({onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}):
void { void {
onlySelf = normalizeBool(onlySelf); onlySelf = normalizeBool(onlySelf);
@ -286,14 +441,14 @@ export abstract class AbstractControl {
/** /**
* Sets errors on a form control. * 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. * 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({ * login.setErrors({
* "notUnique": true * "notUnique": true
* }); * });
@ -301,7 +456,7 @@ export abstract class AbstractControl {
* expect(login.valid).toEqual(false); * expect(login.valid).toEqual(false);
* expect(login.errors).toEqual({"notUnique": true}); * expect(login.errors).toEqual({"notUnique": true});
* *
* login.updateValue("someOtherLogin"); * login.setValue("someOtherLogin");
* *
* expect(login.valid).toEqual(true); * expect(login.valid).toEqual(true);
* ``` * ```
@ -313,8 +468,27 @@ export abstract class AbstractControl {
this._updateControlsErrors(emitEvent); 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|number>|string): AbstractControl { return _find(this, path, '.'); } get(path: Array<string|number>|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 { getError(errorCode: string, path: string[] = null): any {
var control = isPresent(path) && !ListWrapper.isEmpty(path) ? this.get(path) : this; var control = isPresent(path) && !ListWrapper.isEmpty(path) ? this.get(path) : this;
if (isPresent(control) && isPresent(control._errors)) { 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 { hasError(errorCode: string, path: string[] = null): boolean {
return isPresent(this.getError(errorCode, path)); return isPresent(this.getError(errorCode, path));
} }
/**
* Retrieves the top-level ancestor of this control.
*/
get root(): AbstractControl { get root(): AbstractControl {
let x: AbstractControl = this; let x: AbstractControl = this;