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
1 changed files with 219 additions and 36 deletions

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
*/
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<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 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|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 {
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;