2016-06-23 12:47:54 -04:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
2017-03-16 13:15:17 -04:00
|
|
|
import {EventEmitter} from '@angular/core';
|
2018-02-27 17:06:06 -05:00
|
|
|
import {Observable} from 'rxjs';
|
2016-06-13 14:27:04 -04:00
|
|
|
import {composeAsyncValidators, composeValidators} from './directives/shared';
|
2017-02-23 12:53:29 -05:00
|
|
|
import {AsyncValidatorFn, ValidationErrors, ValidatorFn} from './directives/validators';
|
2017-03-16 13:15:17 -04:00
|
|
|
import {toObservable} from './validators';
|
2017-02-01 18:41:14 -05:00
|
|
|
|
2016-06-08 18:36:24 -04:00
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* Reports that a FormControl is valid, meaning that no errors exist in the input value.
|
2018-06-07 14:31:25 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* @see `status`
|
2016-06-08 18:36:24 -04:00
|
|
|
*/
|
2016-06-08 19:38:52 -04:00
|
|
|
export const VALID = 'VALID';
|
2016-06-08 18:36:24 -04:00
|
|
|
|
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* Reports that a FormControl is invalid, meaning that an error exists in the input value.
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* @see `status`
|
2016-06-08 18:36:24 -04:00
|
|
|
*/
|
2016-06-08 19:38:52 -04:00
|
|
|
export const INVALID = 'INVALID';
|
2016-06-08 18:36:24 -04:00
|
|
|
|
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* Reports that a FormControl is pending, meaning that that async validation is occurring and
|
2016-06-08 18:36:24 -04:00
|
|
|
* errors are not yet available for the input value.
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-07 14:31:25 -04:00
|
|
|
* @see `markAsPending`
|
2018-06-11 15:24:00 -04:00
|
|
|
* @see `status`
|
2016-06-08 18:36:24 -04:00
|
|
|
*/
|
2016-06-08 19:38:52 -04:00
|
|
|
export const PENDING = 'PENDING';
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-08-24 19:58:43 -04:00
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* Reports that a FormControl is disabled, meaning that the control is exempt from ancestor
|
2016-08-24 19:58:43 -04:00
|
|
|
* calculations of validity or value.
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-07 14:31:25 -04:00
|
|
|
* @see `markAsDisabled`
|
2018-06-11 15:24:00 -04:00
|
|
|
* @see `status`
|
2016-08-24 19:58:43 -04:00
|
|
|
*/
|
|
|
|
export const DISABLED = 'DISABLED';
|
|
|
|
|
2016-08-01 17:22:50 -04:00
|
|
|
function _find(control: AbstractControl, path: Array<string|number>| string, delimiter: string) {
|
2016-10-21 18:14:44 -04:00
|
|
|
if (path == null) return null;
|
2016-06-08 18:36:24 -04:00
|
|
|
|
|
|
|
if (!(path instanceof Array)) {
|
2016-08-01 17:22:50 -04:00
|
|
|
path = (<string>path).split(delimiter);
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
2016-10-19 12:55:50 -04:00
|
|
|
if (path instanceof Array && (path.length === 0)) return null;
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2017-04-17 14:13:30 -04:00
|
|
|
return (<Array<string|number>>path).reduce((v: AbstractControl, name) => {
|
2016-06-10 14:15:59 -04:00
|
|
|
if (v instanceof FormGroup) {
|
2018-04-07 03:11:02 -04:00
|
|
|
return v.controls.hasOwnProperty(name as string) ? v.controls[name] : null;
|
2016-06-08 19:38:52 -04:00
|
|
|
}
|
2016-10-07 21:11:37 -04:00
|
|
|
|
|
|
|
if (v instanceof FormArray) {
|
|
|
|
return v.at(<number>name) || null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
2016-06-08 19:38:52 -04:00
|
|
|
}, control);
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
2017-02-23 12:53:29 -05:00
|
|
|
|
2017-07-25 18:01:04 -04:00
|
|
|
function coerceToValidator(
|
|
|
|
validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null): ValidatorFn|
|
|
|
|
null {
|
|
|
|
const validator =
|
|
|
|
(isOptionsObj(validatorOrOpts) ? (validatorOrOpts as AbstractControlOptions).validators :
|
|
|
|
validatorOrOpts) as ValidatorFn |
|
|
|
|
ValidatorFn[] | null;
|
|
|
|
|
2017-04-17 14:13:30 -04:00
|
|
|
return Array.isArray(validator) ? composeValidators(validator) : validator || null;
|
2016-06-13 14:27:04 -04:00
|
|
|
}
|
|
|
|
|
2017-07-25 18:01:04 -04:00
|
|
|
function coerceToAsyncValidator(
|
|
|
|
asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null, validatorOrOpts?: ValidatorFn |
|
|
|
|
ValidatorFn[] | AbstractControlOptions | null): AsyncValidatorFn|null {
|
|
|
|
const origAsyncValidator =
|
|
|
|
(isOptionsObj(validatorOrOpts) ? (validatorOrOpts as AbstractControlOptions).asyncValidators :
|
|
|
|
asyncValidator) as AsyncValidatorFn |
|
|
|
|
AsyncValidatorFn | null;
|
|
|
|
|
|
|
|
return Array.isArray(origAsyncValidator) ? composeAsyncValidators(origAsyncValidator) :
|
|
|
|
origAsyncValidator || null;
|
|
|
|
}
|
|
|
|
|
2017-08-07 18:39:25 -04:00
|
|
|
export type FormHooks = 'change' | 'blur' | 'submit';
|
2017-08-02 21:10:10 -04:00
|
|
|
|
2018-01-31 22:25:14 -05:00
|
|
|
/**
|
2018-04-05 05:58:12 -04:00
|
|
|
* Interface for options provided to an `AbstractControl`.
|
2018-01-31 22:25:14 -05:00
|
|
|
*
|
2018-10-19 07:12:20 -04:00
|
|
|
* @publicApi
|
2018-01-31 22:25:14 -05:00
|
|
|
*/
|
2017-07-25 18:01:04 -04:00
|
|
|
export interface AbstractControlOptions {
|
2018-01-31 22:25:14 -05:00
|
|
|
/**
|
2018-11-01 14:51:37 -04:00
|
|
|
* @description
|
|
|
|
* The list of validators applied to a control.
|
2018-01-31 22:25:14 -05:00
|
|
|
*/
|
2017-07-25 18:01:04 -04:00
|
|
|
validators?: ValidatorFn|ValidatorFn[]|null;
|
2018-01-31 22:25:14 -05:00
|
|
|
/**
|
2018-11-01 14:51:37 -04:00
|
|
|
* @description
|
|
|
|
* The list of async validators applied to control.
|
2018-01-31 22:25:14 -05:00
|
|
|
*/
|
2017-07-25 18:01:04 -04:00
|
|
|
asyncValidators?: AsyncValidatorFn|AsyncValidatorFn[]|null;
|
2018-01-31 22:25:14 -05:00
|
|
|
/**
|
2018-11-01 14:51:37 -04:00
|
|
|
* @description
|
2018-01-31 22:25:14 -05:00
|
|
|
* The event name for control to update upon.
|
|
|
|
*/
|
|
|
|
updateOn?: 'change'|'blur'|'submit';
|
2016-06-13 14:27:04 -04:00
|
|
|
}
|
|
|
|
|
2017-08-02 21:10:10 -04:00
|
|
|
|
2017-07-25 18:01:04 -04:00
|
|
|
function isOptionsObj(
|
|
|
|
validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null): boolean {
|
|
|
|
return validatorOrOpts != null && !Array.isArray(validatorOrOpts) &&
|
|
|
|
typeof validatorOrOpts === 'object';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-06-08 18:36:24 -04:00
|
|
|
/**
|
2018-04-05 05:58:12 -04:00
|
|
|
* This is the base class for `FormControl`, `FormGroup`, and `FormArray`.
|
2016-09-12 23:31:13 -04:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
2018-02-08 10:00:53 -05:00
|
|
|
* @see [Forms Guide](/guide/forms)
|
|
|
|
* @see [Reactive Forms Guide](/guide/reactive-forms)
|
|
|
|
* @see [Dynamic Forms Guide](/guide/dynamic-form)
|
2018-04-05 17:31:44 -04:00
|
|
|
*
|
2018-10-19 12:36:24 -04:00
|
|
|
* @publicApi
|
2016-06-08 18:36:24 -04:00
|
|
|
*/
|
|
|
|
export abstract class AbstractControl {
|
2017-08-07 18:39:25 -04:00
|
|
|
/** @internal */
|
2018-06-18 19:38:33 -04:00
|
|
|
// TODO(issue/24571): remove '!'.
|
|
|
|
_pendingDirty !: boolean;
|
2017-08-07 18:39:25 -04:00
|
|
|
|
|
|
|
/** @internal */
|
2018-06-18 19:38:33 -04:00
|
|
|
// TODO(issue/24571): remove '!'.
|
|
|
|
_pendingTouched !: boolean;
|
2017-08-07 18:39:25 -04:00
|
|
|
|
2016-09-02 18:57:35 -04:00
|
|
|
/** @internal */
|
|
|
|
_onCollectionChange = () => {};
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2017-08-09 18:41:53 -04:00
|
|
|
/** @internal */
|
2018-06-18 19:38:33 -04:00
|
|
|
// TODO(issue/24571): remove '!'.
|
|
|
|
_updateOn !: FormHooks;
|
2017-08-09 18:41:53 -04:00
|
|
|
|
2018-06-18 19:38:33 -04:00
|
|
|
// TODO(issue/24571): remove '!'.
|
|
|
|
private _parent !: FormGroup | FormArray;
|
2016-06-08 18:36:24 -04:00
|
|
|
private _asyncValidationSubscription: any;
|
2018-06-07 14:31:25 -04:00
|
|
|
|
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* The current value of the control.
|
|
|
|
*
|
|
|
|
* * For a `FormControl`, the current value.
|
|
|
|
* * For a `FormGroup`, the values of enabled controls as an object
|
|
|
|
* with a key-value pair for each member of the group.
|
|
|
|
* * For a `FormArray`, the values of enabled controls as an array.
|
2018-06-07 14:37:01 -04:00
|
|
|
*
|
2018-06-07 14:31:25 -04:00
|
|
|
*/
|
2017-09-13 17:00:10 -04:00
|
|
|
public readonly value: any;
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2018-02-08 10:00:53 -05:00
|
|
|
/**
|
|
|
|
* Initialize the AbstractControl instance.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* @param validator The function that determines the synchronous validity of this control.
|
|
|
|
* @param asyncValidator The function that determines the asynchronous validity of this
|
2018-02-08 10:00:53 -05:00
|
|
|
* control.
|
|
|
|
*/
|
2017-04-17 14:13:30 -04:00
|
|
|
constructor(public validator: ValidatorFn|null, public asyncValidator: AsyncValidatorFn|null) {}
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-10-19 12:55:50 -04:00
|
|
|
/**
|
|
|
|
* The parent control.
|
|
|
|
*/
|
|
|
|
get parent(): FormGroup|FormArray { return this._parent; }
|
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
|
|
|
* The validation status of the control. There are four possible
|
2018-06-06 13:17:30 -04:00
|
|
|
* validation status values:
|
2016-09-12 23:31:13 -04:00
|
|
|
*
|
2018-06-06 13:17:30 -04:00
|
|
|
* * **VALID**: This control has passed all validation checks.
|
|
|
|
* * **INVALID**: This control has failed at least one validation check.
|
|
|
|
* * **PENDING**: This control is in the midst of conducting a validation check.
|
|
|
|
* * **DISABLED**: This control is exempt from validation checks.
|
2016-09-12 23:31:13 -04:00
|
|
|
*
|
2018-06-06 13:17:30 -04:00
|
|
|
* These status values are mutually exclusive, so a control cannot be
|
2016-09-12 23:31:13 -04:00
|
|
|
* both valid AND invalid or invalid AND disabled.
|
|
|
|
*/
|
2018-06-18 19:38:33 -04:00
|
|
|
// TODO(issue/24571): remove '!'.
|
|
|
|
public readonly status !: string;
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
2018-06-01 08:35:06 -04:00
|
|
|
* A control is `valid` when its `status` is `VALID`.
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2019-01-11 06:16:54 -05:00
|
|
|
* @see {@link AbstractControl.status}
|
2016-09-12 23:31:13 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* @returns True if the control has passed all of its validation tests,
|
2018-06-06 13:17:30 -04:00
|
|
|
* false otherwise.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-09-13 17:00:10 -04:00
|
|
|
get valid(): boolean { return this.status === VALID; }
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
2018-06-01 08:35:06 -04:00
|
|
|
* A control is `invalid` when its `status` is `INVALID`.
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2019-01-11 06:16:54 -05:00
|
|
|
* @see {@link AbstractControl.status}
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* @returns True if this control has failed one or more of its validation checks,
|
2018-06-06 13:17:30 -04:00
|
|
|
* false otherwise.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-09-13 17:00:10 -04:00
|
|
|
get invalid(): boolean { return this.status === INVALID; }
|
2016-08-01 21:41:25 -04:00
|
|
|
|
2016-06-08 18:36:24 -04:00
|
|
|
/**
|
2018-06-01 08:35:06 -04:00
|
|
|
* A control is `pending` when its `status` is `PENDING`.
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2019-01-11 06:16:54 -05:00
|
|
|
* @see {@link AbstractControl.status}
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* @returns True if this control is in the process of conducting a validation check,
|
2018-06-06 13:17:30 -04:00
|
|
|
* false otherwise.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-09-13 17:00:10 -04:00
|
|
|
get pending(): boolean { return this.status == PENDING; }
|
2016-09-12 23:31:13 -04:00
|
|
|
|
|
|
|
/**
|
2018-06-01 08:35:06 -04:00
|
|
|
* A control is `disabled` when its `status` is `DISABLED`.
|
2018-06-07 14:37:01 -04:00
|
|
|
*
|
2016-09-12 23:31:13 -04:00
|
|
|
* Disabled controls are exempt from validation checks and
|
|
|
|
* are not included in the aggregate value of their ancestor
|
|
|
|
* controls.
|
2018-06-11 15:24:00 -04:00
|
|
|
*
|
2019-01-11 06:16:54 -05:00
|
|
|
* @see {@link AbstractControl.status}
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* @returns True if the control is disabled, false otherwise.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-09-13 17:00:10 -04:00
|
|
|
get disabled(): boolean { return this.status === DISABLED; }
|
2016-09-12 23:31:13 -04:00
|
|
|
|
|
|
|
/**
|
2018-06-01 08:35:06 -04:00
|
|
|
* A control is `enabled` as long as its `status` is not `DISABLED`.
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* @returns True if the control has any status other than 'DISABLED',
|
2018-06-06 13:17:30 -04:00
|
|
|
* false if the status is 'DISABLED'.
|
2018-06-07 14:37:01 -04:00
|
|
|
*
|
2019-01-11 06:16:54 -05:00
|
|
|
* @see {@link AbstractControl.status}
|
|
|
|
*
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-09-13 17:00:10 -04:00
|
|
|
get enabled(): boolean { return this.status !== DISABLED; }
|
2016-09-12 23:31:13 -04:00
|
|
|
|
|
|
|
/**
|
2018-06-06 13:17:30 -04:00
|
|
|
* An object containing any errors generated by failing validation,
|
|
|
|
* or null if there are no errors.
|
2016-06-08 18:36:24 -04:00
|
|
|
*/
|
2018-06-18 19:38:33 -04:00
|
|
|
// TODO(issue/24571): remove '!'.
|
|
|
|
public readonly errors !: ValidationErrors | null;
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
|
|
|
* A control is `pristine` if the user has not yet changed
|
|
|
|
* the value in the UI.
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* @returns True if the user has not yet changed the value in the UI; compare `dirty`.
|
2018-06-06 13:17:30 -04:00
|
|
|
* Programmatic changes to a control's value do not mark it dirty.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-09-13 17:00:10 -04:00
|
|
|
public readonly pristine: boolean = true;
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
|
|
|
* A control is `dirty` if the user has changed the value
|
|
|
|
* in the UI.
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* @returns True if the user has changed the value of this control in the UI; compare `pristine`.
|
2018-06-06 13:17:30 -04:00
|
|
|
* Programmatic changes to a control's value do not mark it dirty.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2016-06-08 18:36:24 -04:00
|
|
|
get dirty(): boolean { return !this.pristine; }
|
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* True if the control is marked as `touched`.
|
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* A control is marked `touched` once the user has triggered
|
|
|
|
* a `blur` event on it.
|
|
|
|
*/
|
2017-09-13 17:00:10 -04:00
|
|
|
public readonly touched: boolean = false;
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* True if the control has not been marked as touched
|
|
|
|
*
|
2016-09-12 23:31:13 -04:00
|
|
|
* A control is `untouched` if the user has not yet triggered
|
|
|
|
* a `blur` event on it.
|
|
|
|
*/
|
2017-09-13 17:00:10 -04:00
|
|
|
get untouched(): boolean { return !this.touched; }
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* A multicasting observable that emits an event every time the value of the control changes, in
|
2016-09-12 23:31:13 -04:00
|
|
|
* the UI or programmatically.
|
|
|
|
*/
|
2018-06-18 19:38:33 -04:00
|
|
|
// TODO(issue/24571): remove '!'.
|
|
|
|
public readonly valueChanges !: Observable<any>;
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* A multicasting observable that emits an event every time the validation `status` of the control
|
|
|
|
* recalculates.
|
2019-01-11 06:16:54 -05:00
|
|
|
*
|
|
|
|
* @see {@link AbstractControl.status}
|
|
|
|
*
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2018-06-18 19:38:33 -04:00
|
|
|
// TODO(issue/24571): remove '!'.
|
|
|
|
public readonly statusChanges !: Observable<any>;
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2017-08-09 18:41:53 -04:00
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* Reports the update strategy of the `AbstractControl` (meaning
|
|
|
|
* the event on which the control updates itself).
|
2018-06-01 08:35:06 -04:00
|
|
|
* Possible values: `'change'` | `'blur'` | `'submit'`
|
|
|
|
* Default value: `'change'`
|
2017-08-09 18:41:53 -04:00
|
|
|
*/
|
|
|
|
get updateOn(): FormHooks {
|
|
|
|
return this._updateOn ? this._updateOn : (this.parent ? this.parent.updateOn : 'change');
|
|
|
|
}
|
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
|
|
|
* Sets the synchronous validators that are active on this control. Calling
|
2018-06-11 15:24:00 -04:00
|
|
|
* this overwrites any existing sync validators.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-04-17 14:13:30 -04:00
|
|
|
setValidators(newValidator: ValidatorFn|ValidatorFn[]|null): void {
|
2016-09-12 23:31:13 -04:00
|
|
|
this.validator = coerceToValidator(newValidator);
|
|
|
|
}
|
2016-08-24 19:58:43 -04:00
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
|
|
|
* Sets the async validators that are active on this control. Calling this
|
2018-06-11 15:24:00 -04:00
|
|
|
* overwrites any existing async validators.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-11-10 03:09:22 -05:00
|
|
|
setAsyncValidators(newValidator: AsyncValidatorFn|AsyncValidatorFn[]|null): void {
|
2016-06-23 11:18:07 -04:00
|
|
|
this.asyncValidator = coerceToAsyncValidator(newValidator);
|
|
|
|
}
|
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
|
|
|
* Empties out the sync validator list.
|
|
|
|
*/
|
2016-06-23 11:18:07 -04:00
|
|
|
clearValidators(): void { this.validator = null; }
|
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
|
|
|
* Empties out the async validator list.
|
|
|
|
*/
|
|
|
|
clearAsyncValidators(): void { this.asyncValidator = null; }
|
|
|
|
|
|
|
|
/**
|
2018-06-07 14:31:25 -04:00
|
|
|
* Marks the control as `touched`. A control is touched by focus and
|
2019-01-11 06:16:54 -05:00
|
|
|
* blur events that do not change the value.
|
|
|
|
*
|
|
|
|
* @see `markAsUntouched()`
|
|
|
|
* @see `markAsDirty()`
|
|
|
|
* @see `markAsPristine()`
|
2016-09-12 23:31:13 -04:00
|
|
|
*
|
2019-01-11 06:16:54 -05:00
|
|
|
* @param opts Configuration options that determine how the control propagates changes
|
2018-06-11 16:48:35 -04:00
|
|
|
* and emits events events after marking is applied.
|
2018-06-06 13:17:30 -04:00
|
|
|
* * `onlySelf`: When true, mark only this control. When false or not supplied,
|
2018-06-11 16:48:35 -04:00
|
|
|
* marks all direct ancestors. Default is false.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-06-12 13:59:29 -04:00
|
|
|
markAsTouched(opts: {onlySelf?: boolean} = {}): void {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{touched: boolean}).touched = true;
|
2016-07-01 18:36:04 -04:00
|
|
|
|
2017-06-12 13:59:29 -04:00
|
|
|
if (this._parent && !opts.onlySelf) {
|
|
|
|
this._parent.markAsTouched(opts);
|
2016-07-01 18:36:04 -04:00
|
|
|
}
|
|
|
|
}
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
|
|
|
* Marks the control as `untouched`.
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* If the control has any children, also marks all children as `untouched`
|
|
|
|
* and recalculates the `touched` status of all parent controls.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
2019-01-11 06:16:54 -05:00
|
|
|
* @see `markAsTouched()`
|
|
|
|
* @see `markAsDirty()`
|
|
|
|
* @see `markAsPristine()`
|
|
|
|
*
|
|
|
|
* @param opts Configuration options that determine how the control propagates changes
|
2018-06-11 16:48:35 -04:00
|
|
|
* and emits events after the marking is applied.
|
2018-06-06 13:17:30 -04:00
|
|
|
* * `onlySelf`: When true, mark only this control. When false or not supplied,
|
2018-06-11 16:48:35 -04:00
|
|
|
* marks all direct ancestors. Default is false.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-06-12 13:59:29 -04:00
|
|
|
markAsUntouched(opts: {onlySelf?: boolean} = {}): void {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{touched: boolean}).touched = false;
|
2017-08-07 18:39:25 -04:00
|
|
|
this._pendingTouched = false;
|
2016-09-12 23:31:13 -04:00
|
|
|
|
|
|
|
this._forEachChild(
|
|
|
|
(control: AbstractControl) => { control.markAsUntouched({onlySelf: true}); });
|
|
|
|
|
2017-06-12 13:59:29 -04:00
|
|
|
if (this._parent && !opts.onlySelf) {
|
|
|
|
this._parent._updateTouched(opts);
|
2016-09-12 23:31:13 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* Marks the control as `dirty`. A control becomes dirty when
|
2018-11-19 14:45:47 -05:00
|
|
|
* the control's value is changed through the UI; compare `markAsTouched`.
|
2016-09-12 23:31:13 -04:00
|
|
|
*
|
2019-01-11 06:16:54 -05:00
|
|
|
* @see `markAsTouched()`
|
|
|
|
* @see `markAsUntouched()`
|
|
|
|
* @see `markAsPristine()`
|
|
|
|
*
|
|
|
|
* @param opts Configuration options that determine how the control propagates changes
|
2018-06-11 16:48:35 -04:00
|
|
|
* and emits events after marking is applied.
|
2018-06-06 13:17:30 -04:00
|
|
|
* * `onlySelf`: When true, mark only this control. When false or not supplied,
|
2018-06-11 16:48:35 -04:00
|
|
|
* marks all direct ancestors. Default is false.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-06-12 13:59:29 -04:00
|
|
|
markAsDirty(opts: {onlySelf?: boolean} = {}): void {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{pristine: boolean}).pristine = false;
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2017-06-12 13:59:29 -04:00
|
|
|
if (this._parent && !opts.onlySelf) {
|
|
|
|
this._parent.markAsDirty(opts);
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
|
|
|
* Marks the control as `pristine`.
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* If the control has any children, marks all children as `pristine`,
|
|
|
|
* and recalculates the `pristine` status of all parent
|
2016-09-12 23:31:13 -04:00
|
|
|
* controls.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
2019-01-11 06:16:54 -05:00
|
|
|
* @see `markAsTouched()`
|
|
|
|
* @see `markAsUntouched()`
|
|
|
|
* @see `markAsDirty()`
|
|
|
|
*
|
|
|
|
* @param opts Configuration options that determine how the control emits events after
|
2018-06-11 15:24:00 -04:00
|
|
|
* marking is applied.
|
2018-06-06 13:17:30 -04:00
|
|
|
* * `onlySelf`: When true, mark only this control. When false or not supplied,
|
2018-06-11 16:48:35 -04:00
|
|
|
* marks all direct ancestors. Default is false..
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-06-12 13:59:29 -04:00
|
|
|
markAsPristine(opts: {onlySelf?: boolean} = {}): void {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{pristine: boolean}).pristine = true;
|
2017-08-07 18:39:25 -04:00
|
|
|
this._pendingDirty = false;
|
2016-07-12 18:02:25 -04:00
|
|
|
|
|
|
|
this._forEachChild((control: AbstractControl) => { control.markAsPristine({onlySelf: true}); });
|
|
|
|
|
2017-06-12 13:59:29 -04:00
|
|
|
if (this._parent && !opts.onlySelf) {
|
|
|
|
this._parent._updatePristine(opts);
|
2016-07-12 18:02:25 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
|
|
|
* Marks the control as `pending`.
|
2017-11-06 03:59:09 -05:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* A control is pending while the control performs async validation.
|
2017-11-06 03:59:09 -05:00
|
|
|
*
|
2019-01-11 06:16:54 -05:00
|
|
|
* @see {@link AbstractControl.status}
|
|
|
|
*
|
|
|
|
* @param opts Configuration options that determine how the control propagates changes and
|
2018-06-11 15:24:00 -04:00
|
|
|
* emits events after marking is applied.
|
2018-06-06 13:17:30 -04:00
|
|
|
* * `onlySelf`: When true, mark only this control. When false or not supplied,
|
2018-06-11 16:48:35 -04:00
|
|
|
* marks all direct ancestors. Default is false..
|
|
|
|
* * `emitEvent`: When true or not supplied (the default), the `statusChanges`
|
|
|
|
* observable emits an event with the latest status the control is marked pending.
|
|
|
|
* When false, no events are emitted.
|
2018-06-07 14:37:01 -04:00
|
|
|
*
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-11-06 03:59:09 -05:00
|
|
|
markAsPending(opts: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{status: string}).status = PENDING;
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2017-11-06 03:59:09 -05:00
|
|
|
if (opts.emitEvent !== false) {
|
|
|
|
(this.statusChanges as EventEmitter<any>).emit(this.status);
|
|
|
|
}
|
|
|
|
|
2017-06-12 13:59:29 -04:00
|
|
|
if (this._parent && !opts.onlySelf) {
|
|
|
|
this._parent.markAsPending(opts);
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* Disables the control. This means the control is exempt from validation checks and
|
2016-09-12 23:31:13 -04:00
|
|
|
* excluded from the aggregate value of any parent. Its status is `DISABLED`.
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* If the control has children, all children are also disabled.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
2019-01-11 06:16:54 -05:00
|
|
|
* @see {@link AbstractControl.status}
|
|
|
|
*
|
|
|
|
* @param opts Configuration options that determine how the control propagates
|
2018-06-11 15:24:00 -04:00
|
|
|
* changes and emits events after the control is disabled.
|
2018-06-06 13:17:30 -04:00
|
|
|
* * `onlySelf`: When true, mark only this control. When false or not supplied,
|
2018-06-11 16:48:35 -04:00
|
|
|
* marks all direct ancestors. Default is false..
|
|
|
|
* * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
|
|
|
|
* `valueChanges`
|
|
|
|
* observables emit events with the latest status and value when the control is disabled.
|
|
|
|
* When false, no events are emitted.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-06-12 13:59:29 -04:00
|
|
|
disable(opts: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{status: string}).status = DISABLED;
|
|
|
|
(this as{errors: ValidationErrors | null}).errors = null;
|
2017-12-14 10:51:05 -05:00
|
|
|
this._forEachChild(
|
|
|
|
(control: AbstractControl) => { control.disable({...opts, onlySelf: true}); });
|
2016-08-24 19:58:43 -04:00
|
|
|
this._updateValue();
|
|
|
|
|
2017-06-12 13:59:29 -04:00
|
|
|
if (opts.emitEvent !== false) {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this.valueChanges as EventEmitter<any>).emit(this.value);
|
|
|
|
(this.statusChanges as EventEmitter<string>).emit(this.status);
|
2016-08-24 19:58:43 -04:00
|
|
|
}
|
|
|
|
|
2017-12-14 10:51:05 -05:00
|
|
|
this._updateAncestors(opts);
|
2016-09-20 12:08:12 -04:00
|
|
|
this._onDisabledChange.forEach((changeFn) => changeFn(true));
|
2016-08-24 19:58:43 -04:00
|
|
|
}
|
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* Enables the control. This means the control is included in validation checks and
|
|
|
|
* the aggregate value of its parent. Its status recalculates based on its value and
|
2016-09-12 23:31:13 -04:00
|
|
|
* its validators.
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* By default, if the control has children, all children are enabled.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
2019-01-11 06:16:54 -05:00
|
|
|
* @see {@link AbstractControl.status}
|
|
|
|
*
|
|
|
|
* @param opts Configure options that control how the control propagates changes and
|
2018-06-11 15:24:00 -04:00
|
|
|
* emits events when marked as untouched
|
2018-06-06 13:17:30 -04:00
|
|
|
* * `onlySelf`: When true, mark only this control. When false or not supplied,
|
2018-06-11 16:48:35 -04:00
|
|
|
* marks all direct ancestors. Default is false..
|
|
|
|
* * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
|
|
|
|
* `valueChanges`
|
|
|
|
* observables emit events with the latest status and value when the control is enabled.
|
|
|
|
* When false, no events are emitted.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-06-12 13:59:29 -04:00
|
|
|
enable(opts: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{status: string}).status = VALID;
|
2017-12-14 10:51:05 -05:00
|
|
|
this._forEachChild(
|
|
|
|
(control: AbstractControl) => { control.enable({...opts, onlySelf: true}); });
|
2017-06-12 13:59:29 -04:00
|
|
|
this.updateValueAndValidity({onlySelf: true, emitEvent: opts.emitEvent});
|
2016-08-24 19:58:43 -04:00
|
|
|
|
2017-12-14 10:51:05 -05:00
|
|
|
this._updateAncestors(opts);
|
2016-09-20 12:08:12 -04:00
|
|
|
this._onDisabledChange.forEach((changeFn) => changeFn(false));
|
2016-08-24 19:58:43 -04:00
|
|
|
}
|
|
|
|
|
2017-12-14 10:51:05 -05:00
|
|
|
private _updateAncestors(opts: {onlySelf?: boolean, emitEvent?: boolean}) {
|
|
|
|
if (this._parent && !opts.onlySelf) {
|
|
|
|
this._parent.updateValueAndValidity(opts);
|
2016-08-24 19:58:43 -04:00
|
|
|
this._parent._updatePristine();
|
|
|
|
this._parent._updateTouched();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-01 08:35:06 -04:00
|
|
|
/**
|
|
|
|
* @param parent Sets the parent of the control
|
|
|
|
*/
|
2016-06-10 14:15:59 -04:00
|
|
|
setParent(parent: FormGroup|FormArray): void { this._parent = parent; }
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
|
|
|
* Sets the value of the control. Abstract method (implemented in sub-classes).
|
|
|
|
*/
|
2016-08-05 16:35:17 -04:00
|
|
|
abstract setValue(value: any, options?: Object): void;
|
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
|
|
|
* Patches the value of the control. Abstract method (implemented in sub-classes).
|
|
|
|
*/
|
2016-08-05 16:35:17 -04:00
|
|
|
abstract patchValue(value: any, options?: Object): void;
|
2016-07-08 16:04:25 -04:00
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
|
|
|
* Resets the control. Abstract method (implemented in sub-classes).
|
|
|
|
*/
|
2016-07-12 18:02:25 -04:00
|
|
|
abstract reset(value?: any, options?: Object): void;
|
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* Recalculates the value and validation status of the control.
|
2016-09-12 23:31:13 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* By default, it also updates the value and validity of its ancestors.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
2018-06-11 16:48:35 -04:00
|
|
|
* @param opts Configuration options determine how the control propagates changes and emits events
|
|
|
|
* after updates and validity checks are applied.
|
2018-06-06 13:17:30 -04:00
|
|
|
* * `onlySelf`: When true, only update this control. When false or not supplied,
|
2018-06-11 16:48:35 -04:00
|
|
|
* update all direct ancestors. Default is false..
|
|
|
|
* * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
|
|
|
|
* `valueChanges`
|
|
|
|
* observables emit events with the latest status and value when the control is updated.
|
|
|
|
* When false, no events are emitted.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2017-06-12 13:59:29 -04:00
|
|
|
updateValueAndValidity(opts: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
2016-09-09 15:00:38 -04:00
|
|
|
this._setInitialStatus();
|
2016-06-08 18:36:24 -04:00
|
|
|
this._updateValue();
|
|
|
|
|
2016-09-09 15:00:38 -04:00
|
|
|
if (this.enabled) {
|
2016-12-03 17:17:09 -05:00
|
|
|
this._cancelExistingSubscription();
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{errors: ValidationErrors | null}).errors = this._runValidator();
|
|
|
|
(this as{status: string}).status = this._calculateStatus();
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2017-09-13 17:00:10 -04:00
|
|
|
if (this.status === VALID || this.status === PENDING) {
|
2017-06-12 13:59:29 -04:00
|
|
|
this._runAsyncValidator(opts.emitEvent);
|
2016-09-09 15:00:38 -04:00
|
|
|
}
|
2016-08-24 19:58:43 -04:00
|
|
|
}
|
|
|
|
|
2017-06-12 13:59:29 -04:00
|
|
|
if (opts.emitEvent !== false) {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this.valueChanges as EventEmitter<any>).emit(this.value);
|
|
|
|
(this.statusChanges as EventEmitter<string>).emit(this.status);
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
2017-06-12 13:59:29 -04:00
|
|
|
if (this._parent && !opts.onlySelf) {
|
|
|
|
this._parent.updateValueAndValidity(opts);
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-29 14:33:49 -04:00
|
|
|
/** @internal */
|
2017-06-12 13:59:29 -04:00
|
|
|
_updateTreeValidity(opts: {emitEvent?: boolean} = {emitEvent: true}) {
|
|
|
|
this._forEachChild((ctrl: AbstractControl) => ctrl._updateTreeValidity(opts));
|
|
|
|
this.updateValueAndValidity({onlySelf: true, emitEvent: opts.emitEvent});
|
2016-08-29 14:33:49 -04:00
|
|
|
}
|
|
|
|
|
2017-09-13 17:00:10 -04:00
|
|
|
private _setInitialStatus() {
|
|
|
|
(this as{status: string}).status = this._allControlsDisabled() ? DISABLED : VALID;
|
|
|
|
}
|
2016-09-09 15:00:38 -04:00
|
|
|
|
2017-02-23 12:53:29 -05:00
|
|
|
private _runValidator(): ValidationErrors|null {
|
2016-10-21 18:14:44 -04:00
|
|
|
return this.validator ? this.validator(this) : null;
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
2017-04-17 14:13:30 -04:00
|
|
|
private _runAsyncValidator(emitEvent?: boolean): void {
|
2016-10-21 18:14:44 -04:00
|
|
|
if (this.asyncValidator) {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{status: string}).status = PENDING;
|
2016-10-21 18:14:44 -04:00
|
|
|
const obs = toObservable(this.asyncValidator(this));
|
2016-10-19 12:54:54 -04:00
|
|
|
this._asyncValidationSubscription =
|
2017-02-23 12:53:29 -05:00
|
|
|
obs.subscribe((errors: ValidationErrors | null) => this.setErrors(errors, {emitEvent}));
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private _cancelExistingSubscription(): void {
|
2016-10-21 18:14:44 -04:00
|
|
|
if (this._asyncValidationSubscription) {
|
2016-08-02 18:53:34 -04:00
|
|
|
this._asyncValidationSubscription.unsubscribe();
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* Sets errors on a form control when running validations manually, rather than automatically.
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* Calling `setErrors` also updates the validity of the parent control.
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
2018-09-20 10:18:14 -04:00
|
|
|
* @usageNotes
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Manually set the errors for a control
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
|
|
|
* ```
|
2018-06-01 08:35:06 -04:00
|
|
|
* const login = new FormControl('someLogin');
|
2016-06-08 18:36:24 -04:00
|
|
|
* login.setErrors({
|
2018-06-01 08:35:06 -04:00
|
|
|
* notUnique: true
|
2016-06-08 18:36:24 -04:00
|
|
|
* });
|
|
|
|
*
|
|
|
|
* expect(login.valid).toEqual(false);
|
2018-06-01 08:35:06 -04:00
|
|
|
* expect(login.errors).toEqual({ notUnique: true });
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* login.setValue('someOtherLogin');
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
|
|
|
* expect(login.valid).toEqual(true);
|
|
|
|
* ```
|
|
|
|
*/
|
2017-06-12 13:59:29 -04:00
|
|
|
setErrors(errors: ValidationErrors|null, opts: {emitEvent?: boolean} = {}): void {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{errors: ValidationErrors | null}).errors = errors;
|
2017-06-12 13:59:29 -04:00
|
|
|
this._updateControlsErrors(opts.emitEvent !== false);
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
|
|
|
* Retrieves a child control given the control's name or path.
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* @param path A dot-delimited string or array of string/number values that define the path to the
|
2018-06-01 08:35:06 -04:00
|
|
|
* control.
|
|
|
|
*
|
2018-09-20 10:18:14 -04:00
|
|
|
* @usageNotes
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Retrieve a nested control
|
2016-09-12 23:31:13 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* For example, to get a `name` control nested within a `person` sub-group:
|
2016-09-12 23:31:13 -04:00
|
|
|
*
|
|
|
|
* * `this.form.get('person.name');`
|
|
|
|
*
|
|
|
|
* -OR-
|
|
|
|
*
|
|
|
|
* * `this.form.get(['person', 'name']);`
|
|
|
|
*/
|
2017-04-17 14:13:30 -04:00
|
|
|
get(path: Array<string|number>|string): AbstractControl|null { return _find(this, path, '.'); }
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
2018-12-28 14:00:50 -05:00
|
|
|
* @description
|
|
|
|
* Reports error data for the control with the given path.
|
2016-09-12 23:31:13 -04:00
|
|
|
*
|
2018-12-28 14:00:50 -05:00
|
|
|
* @param errorCode The code of the error to check
|
|
|
|
* @param path A list of control names that designates how to move from the current control
|
|
|
|
* to the control that should be queried for errors.
|
|
|
|
*
|
|
|
|
* @usageNotes
|
|
|
|
* For example, for the following `FormGroup`:
|
2018-06-11 15:24:00 -04:00
|
|
|
*
|
2018-12-28 14:00:50 -05:00
|
|
|
* ```
|
|
|
|
* form = new FormGroup({
|
|
|
|
* address: new FormGroup({ street: new FormControl() })
|
|
|
|
* });
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* The path to the 'street' control from the root form would be 'address' -> 'street'.
|
|
|
|
*
|
|
|
|
* It can be provided to this method in one of two formats:
|
|
|
|
*
|
|
|
|
* 1. An array of string control names, e.g. `['address', 'street']`
|
|
|
|
* 1. A period-delimited list of control names in one string, e.g. `'address.street'`
|
|
|
|
*
|
|
|
|
* @returns error data for that particular error. If the control or error is not present,
|
|
|
|
* null is returned.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2018-12-21 19:39:37 -05:00
|
|
|
getError(errorCode: string, path?: Array<string|number>|string): any {
|
2016-10-21 18:14:44 -04:00
|
|
|
const control = path ? this.get(path) : this;
|
2017-09-13 17:00:10 -04:00
|
|
|
return control && control.errors ? control.errors[errorCode] : null;
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
2018-12-28 14:00:50 -05:00
|
|
|
* @description
|
2018-06-06 13:17:30 -04:00
|
|
|
* Reports whether the control with the given path has the error specified.
|
2016-09-12 23:31:13 -04:00
|
|
|
*
|
2018-12-28 14:00:50 -05:00
|
|
|
* @param errorCode The code of the error to check
|
|
|
|
* @param path A list of control names that designates how to move from the current control
|
|
|
|
* to the control that should be queried for errors.
|
|
|
|
*
|
|
|
|
* @usageNotes
|
|
|
|
* For example, for the following `FormGroup`:
|
|
|
|
*
|
|
|
|
* ```
|
|
|
|
* form = new FormGroup({
|
|
|
|
* address: new FormGroup({ street: new FormControl() })
|
|
|
|
* });
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* The path to the 'street' control from the root form would be 'address' -> 'street'.
|
|
|
|
*
|
|
|
|
* It can be provided to this method in one of two formats:
|
|
|
|
*
|
|
|
|
* 1. An array of string control names, e.g. `['address', 'street']`
|
|
|
|
* 1. A period-delimited list of control names in one string, e.g. `'address.street'`
|
|
|
|
*
|
|
|
|
* If no path is given, this method checks for the error on the current control.
|
|
|
|
*
|
|
|
|
* @returns whether the given error is present in the control at the given path.
|
|
|
|
*
|
|
|
|
* If the control is not present, false is returned.
|
2016-09-12 23:31:13 -04:00
|
|
|
*/
|
2018-12-21 19:39:37 -05:00
|
|
|
hasError(errorCode: string, path?: Array<string|number>|string): boolean {
|
|
|
|
return !!this.getError(errorCode, path);
|
|
|
|
}
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-09-12 23:31:13 -04:00
|
|
|
/**
|
|
|
|
* Retrieves the top-level ancestor of this control.
|
|
|
|
*/
|
2016-06-08 18:36:24 -04:00
|
|
|
get root(): AbstractControl {
|
|
|
|
let x: AbstractControl = this;
|
|
|
|
|
2016-10-21 18:14:44 -04:00
|
|
|
while (x._parent) {
|
2016-06-08 18:36:24 -04:00
|
|
|
x = x._parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal */
|
2016-06-27 23:01:24 -04:00
|
|
|
_updateControlsErrors(emitEvent: boolean): void {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{status: string}).status = this._calculateStatus();
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-06-27 23:01:24 -04:00
|
|
|
if (emitEvent) {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this.statusChanges as EventEmitter<string>).emit(this.status);
|
2016-06-27 23:01:24 -04:00
|
|
|
}
|
|
|
|
|
2016-10-21 18:14:44 -04:00
|
|
|
if (this._parent) {
|
2016-06-27 23:01:24 -04:00
|
|
|
this._parent._updateControlsErrors(emitEvent);
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
_initObservables() {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{valueChanges: Observable<any>}).valueChanges = new EventEmitter();
|
|
|
|
(this as{statusChanges: Observable<any>}).statusChanges = new EventEmitter();
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private _calculateStatus(): string {
|
2016-09-01 19:51:42 -04:00
|
|
|
if (this._allControlsDisabled()) return DISABLED;
|
2017-09-13 17:00:10 -04:00
|
|
|
if (this.errors) return INVALID;
|
2016-06-08 18:36:24 -04:00
|
|
|
if (this._anyControlsHaveStatus(PENDING)) return PENDING;
|
|
|
|
if (this._anyControlsHaveStatus(INVALID)) return INVALID;
|
|
|
|
return VALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
abstract _updateValue(): void;
|
|
|
|
|
|
|
|
/** @internal */
|
2016-07-12 18:02:25 -04:00
|
|
|
abstract _forEachChild(cb: Function): void;
|
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
abstract _anyControls(condition: Function): boolean;
|
|
|
|
|
2016-08-24 19:58:43 -04:00
|
|
|
/** @internal */
|
|
|
|
abstract _allControlsDisabled(): boolean;
|
|
|
|
|
2017-08-07 18:39:25 -04:00
|
|
|
/** @internal */
|
|
|
|
abstract _syncPendingControls(): boolean;
|
|
|
|
|
2016-07-12 18:02:25 -04:00
|
|
|
/** @internal */
|
|
|
|
_anyControlsHaveStatus(status: string): boolean {
|
2016-10-19 12:55:50 -04:00
|
|
|
return this._anyControls((control: AbstractControl) => control.status === status);
|
2016-07-12 18:02:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
_anyControlsDirty(): boolean {
|
|
|
|
return this._anyControls((control: AbstractControl) => control.dirty);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
_anyControlsTouched(): boolean {
|
|
|
|
return this._anyControls((control: AbstractControl) => control.touched);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal */
|
2017-06-12 13:59:29 -04:00
|
|
|
_updatePristine(opts: {onlySelf?: boolean} = {}): void {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{pristine: boolean}).pristine = !this._anyControlsDirty();
|
2016-07-12 18:02:25 -04:00
|
|
|
|
2017-06-12 13:59:29 -04:00
|
|
|
if (this._parent && !opts.onlySelf) {
|
|
|
|
this._parent._updatePristine(opts);
|
2016-07-12 18:02:25 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal */
|
2017-06-12 13:59:29 -04:00
|
|
|
_updateTouched(opts: {onlySelf?: boolean} = {}): void {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{touched: boolean}).touched = this._anyControlsTouched();
|
2016-07-12 18:02:25 -04:00
|
|
|
|
2017-06-12 13:59:29 -04:00
|
|
|
if (this._parent && !opts.onlySelf) {
|
|
|
|
this._parent._updateTouched(opts);
|
2016-07-12 18:02:25 -04:00
|
|
|
}
|
|
|
|
}
|
2016-08-24 19:58:43 -04:00
|
|
|
|
|
|
|
/** @internal */
|
2016-09-20 12:08:12 -04:00
|
|
|
_onDisabledChange: Function[] = [];
|
2016-08-24 19:58:43 -04:00
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
_isBoxedValue(formState: any): boolean {
|
2016-10-19 16:42:39 -04:00
|
|
|
return typeof formState === 'object' && formState !== null &&
|
|
|
|
Object.keys(formState).length === 2 && 'value' in formState && 'disabled' in formState;
|
2016-08-24 19:58:43 -04:00
|
|
|
}
|
2016-09-02 18:57:35 -04:00
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
_registerOnCollectionChange(fn: () => void): void { this._onCollectionChange = fn; }
|
2017-08-09 18:41:53 -04:00
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
_setUpdateStrategy(opts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions|null): void {
|
|
|
|
if (isOptionsObj(opts) && (opts as AbstractControlOptions).updateOn != null) {
|
|
|
|
this._updateOn = (opts as AbstractControlOptions).updateOn !;
|
|
|
|
}
|
|
|
|
}
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-04-05 06:02:42 -04:00
|
|
|
* Tracks the value and validation status of an individual form control.
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
2018-04-05 06:02:42 -04:00
|
|
|
* This is one of the three fundamental building blocks of Angular forms, along with
|
2018-06-11 16:48:35 -04:00
|
|
|
* `FormGroup` and `FormArray`. It extends the `AbstractControl` class that
|
|
|
|
* implements most of the base functionality for accessing the value, validation status,
|
2018-06-01 08:35:06 -04:00
|
|
|
* user interactions and events.
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* @see `AbstractControl`
|
|
|
|
* @see [Reactive Forms Guide](guide/reactive-forms)
|
|
|
|
* @see [Usage Notes](#usage-notes)
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* @usageNotes
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* ### Initializing Form Controls
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* Instantiate a `FormControl`, with an initial value.
|
2016-09-13 03:14:07 -04:00
|
|
|
*
|
|
|
|
* ```ts
|
2018-07-26 11:57:58 -04:00
|
|
|
* const control = new FormControl('some value');
|
|
|
|
* console.log(control.value); // 'some value'
|
2016-09-13 03:14:07 -04:00
|
|
|
*```
|
|
|
|
*
|
2018-06-11 16:48:35 -04:00
|
|
|
* The following example initializes the control with a form state object. The `value`
|
2018-06-11 15:24:00 -04:00
|
|
|
* and `disabled` keys are required in this case.
|
2016-09-13 03:14:07 -04:00
|
|
|
*
|
|
|
|
* ```ts
|
2018-07-26 11:57:58 -04:00
|
|
|
* const control = new FormControl({ value: 'n/a', disabled: true });
|
|
|
|
* console.log(control.value); // 'n/a'
|
|
|
|
* console.log(control.status); // 'DISABLED'
|
2016-09-13 03:14:07 -04:00
|
|
|
* ```
|
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* The following example initializes the control with a sync validator.
|
2016-09-13 03:14:07 -04:00
|
|
|
*
|
|
|
|
* ```ts
|
2018-07-26 11:57:58 -04:00
|
|
|
* const control = new FormControl('', Validators.required);
|
|
|
|
* console.log(control.value); // ''
|
|
|
|
* console.log(control.status); // 'INVALID'
|
2016-09-13 03:14:07 -04:00
|
|
|
* ```
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* The following example initializes the control using an options object.
|
2017-07-25 18:01:04 -04:00
|
|
|
*
|
|
|
|
* ```ts
|
2018-07-26 11:57:58 -04:00
|
|
|
* const control = new FormControl('', {
|
2017-07-25 18:01:04 -04:00
|
|
|
* validators: Validators.required,
|
|
|
|
* asyncValidators: myAsyncValidator
|
|
|
|
* });
|
|
|
|
* ```
|
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Configure the control to update on a blur event
|
|
|
|
*
|
|
|
|
* Set the `updateOn` option to `'blur'` to update on the blur `event`.
|
2017-08-02 21:10:10 -04:00
|
|
|
*
|
|
|
|
* ```ts
|
2018-07-26 11:57:58 -04:00
|
|
|
* const control = new FormControl('', { updateOn: 'blur' });
|
2017-08-02 21:10:10 -04:00
|
|
|
* ```
|
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Configure the control to update on a submit event
|
2017-08-07 18:39:25 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* Set the `updateOn` option to `'submit'` to update on a submit `event`.
|
2016-09-13 03:14:07 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ```ts
|
2018-07-26 11:57:58 -04:00
|
|
|
* const control = new FormControl('', { updateOn: 'submit' });
|
2018-06-01 08:35:06 -04:00
|
|
|
* ```
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Reset the control back to an initial value
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* You reset to a specific form state by passing through a standalone
|
2018-06-01 08:35:06 -04:00
|
|
|
* value or a form state object that contains both a value and a disabled state
|
|
|
|
* (these are the only two properties that cannot be calculated).
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ```ts
|
2018-07-26 11:57:58 -04:00
|
|
|
* const control = new FormControl('Nancy');
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* console.log(control.value); // 'Nancy'
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* control.reset('Drew');
|
2018-04-05 17:31:44 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* console.log(control.value); // 'Drew'
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* ### Reset the control back to an initial value and disabled
|
|
|
|
*
|
|
|
|
* ```
|
2018-07-26 11:57:58 -04:00
|
|
|
* const control = new FormControl('Nancy');
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* console.log(control.value); // 'Nancy'
|
2018-07-26 11:57:58 -04:00
|
|
|
* console.log(control.status); // 'VALID'
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* control.reset({ value: 'Drew', disabled: true });
|
|
|
|
*
|
2018-07-26 11:57:58 -04:00
|
|
|
* console.log(control.value); // 'Drew'
|
|
|
|
* console.log(control.status); // 'DISABLED'
|
2018-11-20 07:40:33 -05:00
|
|
|
* ```
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
2018-10-19 12:36:24 -04:00
|
|
|
* @publicApi
|
|
|
|
*/
|
2016-06-10 14:15:59 -04:00
|
|
|
export class FormControl extends AbstractControl {
|
2016-06-08 18:36:24 -04:00
|
|
|
/** @internal */
|
2016-06-15 18:15:41 -04:00
|
|
|
_onChange: Function[] = [];
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2017-08-02 21:10:10 -04:00
|
|
|
/** @internal */
|
|
|
|
_pendingValue: any;
|
|
|
|
|
2017-11-11 05:54:36 -05:00
|
|
|
/** @internal */
|
|
|
|
_pendingChange: any;
|
|
|
|
|
2018-06-01 08:35:06 -04:00
|
|
|
/**
|
|
|
|
* Creates a new `FormControl` instance.
|
|
|
|
*
|
2018-06-27 15:33:16 -04:00
|
|
|
* @param formState Initializes the control with an initial value,
|
|
|
|
* or an object that defines the initial value and disabled state.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
2018-06-07 14:37:01 -04:00
|
|
|
* @param validatorOrOpts A synchronous validator function, or an array of
|
|
|
|
* such functions, or an `AbstractControlOptions` object that contains validation functions
|
2018-06-06 13:17:30 -04:00
|
|
|
* and a validation trigger.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
|
|
|
* @param asyncValidator A single async validator or array of async validator functions
|
|
|
|
*
|
|
|
|
*/
|
2016-06-08 19:38:52 -04:00
|
|
|
constructor(
|
2017-07-25 18:01:04 -04:00
|
|
|
formState: any = null,
|
|
|
|
validatorOrOpts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions|null,
|
2017-04-17 14:13:30 -04:00
|
|
|
asyncValidator?: AsyncValidatorFn|AsyncValidatorFn[]|null) {
|
2017-07-25 18:01:04 -04:00
|
|
|
super(
|
|
|
|
coerceToValidator(validatorOrOpts),
|
|
|
|
coerceToAsyncValidator(asyncValidator, validatorOrOpts));
|
2016-08-24 19:58:43 -04:00
|
|
|
this._applyFormState(formState);
|
2017-08-02 21:10:10 -04:00
|
|
|
this._setUpdateStrategy(validatorOrOpts);
|
2016-06-08 18:36:24 -04:00
|
|
|
this.updateValueAndValidity({onlySelf: true, emitEvent: false});
|
|
|
|
this._initObservables();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-06-06 13:17:30 -04:00
|
|
|
* Sets a new value for the form control.
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
2018-06-06 13:17:30 -04:00
|
|
|
* @param value The new value for the control.
|
2018-06-11 16:48:35 -04:00
|
|
|
* @param options Configuration options that determine how the control proopagates changes
|
|
|
|
* and emits events when the value changes.
|
2018-06-07 14:37:01 -04:00
|
|
|
* The configuration options are passed to the {@link AbstractControl#updateValueAndValidity
|
|
|
|
* updateValueAndValidity} method.
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2018-06-07 14:37:01 -04:00
|
|
|
* * `onlySelf`: When true, each change only affects this control, and not its parent. Default is
|
|
|
|
* false.
|
2018-06-11 16:48:35 -04:00
|
|
|
* * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
|
|
|
|
* `valueChanges`
|
|
|
|
* observables emit events with the latest status and value when the control value is updated.
|
|
|
|
* When false, no events are emitted.
|
|
|
|
* * `emitModelToViewChange`: When true or not supplied (the default), each change triggers an
|
|
|
|
* `onChange` event to
|
2018-06-07 14:37:01 -04:00
|
|
|
* update the view.
|
2018-06-11 16:48:35 -04:00
|
|
|
* * `emitViewToModelChange`: When true or not supplied (the default), each change triggers an
|
|
|
|
* `ngModelChange`
|
2018-06-07 14:37:01 -04:00
|
|
|
* event to update the model.
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
|
|
|
*/
|
2017-04-27 16:39:21 -04:00
|
|
|
setValue(value: any, options: {
|
2016-06-08 18:36:24 -04:00
|
|
|
onlySelf?: boolean,
|
|
|
|
emitEvent?: boolean,
|
2016-07-12 18:02:25 -04:00
|
|
|
emitModelToViewChange?: boolean,
|
|
|
|
emitViewToModelChange?: boolean
|
2016-06-08 18:36:24 -04:00
|
|
|
} = {}): void {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{value: any}).value = this._pendingValue = value;
|
2017-04-27 16:39:21 -04:00
|
|
|
if (this._onChange.length && options.emitModelToViewChange !== false) {
|
|
|
|
this._onChange.forEach(
|
2017-09-13 17:00:10 -04:00
|
|
|
(changeFn) => changeFn(this.value, options.emitViewToModelChange !== false));
|
2016-06-15 18:15:41 -04:00
|
|
|
}
|
2017-04-27 16:39:21 -04:00
|
|
|
this.updateValueAndValidity(options);
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
2016-08-05 16:35:17 -04:00
|
|
|
/**
|
2016-09-13 03:14:07 -04:00
|
|
|
* Patches the value of a control.
|
|
|
|
*
|
2017-10-13 15:27:38 -04:00
|
|
|
* This function is functionally the same as {@link FormControl#setValue setValue} at this level.
|
|
|
|
* It exists for symmetry with {@link FormGroup#patchValue patchValue} on `FormGroups` and
|
|
|
|
* `FormArrays`, where it does behave differently.
|
2018-06-11 16:48:35 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* @see `setValue` for options
|
2016-08-05 16:35:17 -04:00
|
|
|
*/
|
|
|
|
patchValue(value: any, options: {
|
|
|
|
onlySelf?: boolean,
|
|
|
|
emitEvent?: boolean,
|
|
|
|
emitModelToViewChange?: boolean,
|
|
|
|
emitViewToModelChange?: boolean
|
|
|
|
} = {}): void {
|
|
|
|
this.setValue(value, options);
|
|
|
|
}
|
|
|
|
|
2016-09-13 03:14:07 -04:00
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* Resets the form control, marking it `pristine` and `untouched`, and setting
|
|
|
|
* the value to null.
|
2016-09-13 03:14:07 -04:00
|
|
|
*
|
2018-06-27 15:33:16 -04:00
|
|
|
* @param formState Resets the control with an initial value,
|
|
|
|
* or an object that defines the initial value and disabled state.
|
2016-09-13 03:14:07 -04:00
|
|
|
*
|
2018-06-11 16:48:35 -04:00
|
|
|
* @param options Configuration options that determine how the control propagates changes
|
|
|
|
* and emits events after the value changes.
|
2016-09-13 03:14:07 -04:00
|
|
|
*
|
2018-06-07 14:37:01 -04:00
|
|
|
* * `onlySelf`: When true, each change only affects this control, and not its parent. Default is
|
|
|
|
* false.
|
2018-06-11 16:48:35 -04:00
|
|
|
* * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
|
|
|
|
* `valueChanges`
|
|
|
|
* observables emit events with the latest status and value when the control is reset.
|
|
|
|
* When false, no events are emitted.
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2016-09-13 03:14:07 -04:00
|
|
|
*/
|
2017-04-27 16:39:21 -04:00
|
|
|
reset(formState: any = null, options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
2016-08-24 19:58:43 -04:00
|
|
|
this._applyFormState(formState);
|
2017-04-27 16:39:21 -04:00
|
|
|
this.markAsPristine(options);
|
|
|
|
this.markAsUntouched(options);
|
2017-09-13 17:00:10 -04:00
|
|
|
this.setValue(this.value, options);
|
2017-11-11 05:54:36 -05:00
|
|
|
this._pendingChange = false;
|
2016-07-12 18:02:25 -04:00
|
|
|
}
|
|
|
|
|
2016-06-08 18:36:24 -04:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
_updateValue() {}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
2016-07-12 18:02:25 -04:00
|
|
|
_anyControls(condition: Function): boolean { return false; }
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-08-24 19:58:43 -04:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
_allControlsDisabled(): boolean { return this.disabled; }
|
|
|
|
|
2016-06-08 18:36:24 -04:00
|
|
|
/**
|
|
|
|
* Register a listener for change events.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
|
|
|
* @param fn The method that is called when the value changes
|
2016-06-08 18:36:24 -04:00
|
|
|
*/
|
2016-06-15 18:15:41 -04:00
|
|
|
registerOnChange(fn: Function): void { this._onChange.push(fn); }
|
2016-07-12 18:02:25 -04:00
|
|
|
|
2016-08-25 17:37:57 -04:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
_clearChangeFns(): void {
|
|
|
|
this._onChange = [];
|
2016-09-20 12:08:12 -04:00
|
|
|
this._onDisabledChange = [];
|
2016-09-02 18:57:35 -04:00
|
|
|
this._onCollectionChange = () => {};
|
2016-08-25 17:37:57 -04:00
|
|
|
}
|
|
|
|
|
2016-08-24 19:58:43 -04:00
|
|
|
/**
|
|
|
|
* Register a listener for disabled events.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
|
|
|
* @param fn The method that is called when the disabled status changes.
|
2016-08-24 19:58:43 -04:00
|
|
|
*/
|
2016-09-20 12:08:12 -04:00
|
|
|
registerOnDisabledChange(fn: (isDisabled: boolean) => void): void {
|
|
|
|
this._onDisabledChange.push(fn);
|
|
|
|
}
|
2016-08-24 19:58:43 -04:00
|
|
|
|
2016-07-12 18:02:25 -04:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
_forEachChild(cb: Function): void {}
|
2016-08-24 19:58:43 -04:00
|
|
|
|
2017-08-07 18:39:25 -04:00
|
|
|
/** @internal */
|
|
|
|
_syncPendingControls(): boolean {
|
2017-08-09 18:41:53 -04:00
|
|
|
if (this.updateOn === 'submit') {
|
2017-08-07 18:39:25 -04:00
|
|
|
if (this._pendingDirty) this.markAsDirty();
|
|
|
|
if (this._pendingTouched) this.markAsTouched();
|
2017-11-11 05:54:36 -05:00
|
|
|
if (this._pendingChange) {
|
|
|
|
this.setValue(this._pendingValue, {onlySelf: true, emitModelToViewChange: false});
|
|
|
|
return true;
|
|
|
|
}
|
2017-08-07 18:39:25 -04:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-08-24 19:58:43 -04:00
|
|
|
private _applyFormState(formState: any) {
|
|
|
|
if (this._isBoxedValue(formState)) {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{value: any}).value = this._pendingValue = formState.value;
|
2016-08-24 19:58:43 -04:00
|
|
|
formState.disabled ? this.disable({onlySelf: true, emitEvent: false}) :
|
|
|
|
this.enable({onlySelf: true, emitEvent: false});
|
|
|
|
} else {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{value: any}).value = this._pendingValue = formState;
|
2017-08-02 21:10:10 -04:00
|
|
|
}
|
|
|
|
}
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-04-05 05:58:12 -04:00
|
|
|
* Tracks the value and validity state of a group of `FormControl` instances.
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
2018-04-05 05:58:12 -04:00
|
|
|
* A `FormGroup` aggregates the values of each child `FormControl` into one object,
|
2018-06-06 13:17:30 -04:00
|
|
|
* with each control name as the key. It calculates its status by reducing the status values
|
2016-09-13 14:43:08 -04:00
|
|
|
* of its children. For example, if one of the controls in a group is invalid, the entire
|
|
|
|
* group becomes invalid.
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
2016-06-10 14:15:59 -04:00
|
|
|
* `FormGroup` is one of the three fundamental building blocks used to define forms in Angular,
|
2018-04-05 05:58:12 -04:00
|
|
|
* along with `FormControl` and `FormArray`.
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2018-04-05 05:58:12 -04:00
|
|
|
* When instantiating a `FormGroup`, pass in a collection of child controls as the first
|
2018-06-11 15:24:00 -04:00
|
|
|
* argument. The key for each child registers the name for the control.
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* @usageNotes
|
|
|
|
*
|
|
|
|
* ### Create a form group with 2 controls
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
|
|
|
* ```
|
|
|
|
* const form = new FormGroup({
|
|
|
|
* first: new FormControl('Nancy', Validators.minLength(2)),
|
|
|
|
* last: new FormControl('Drew'),
|
|
|
|
* });
|
|
|
|
*
|
|
|
|
* console.log(form.value); // {first: 'Nancy', last; 'Drew'}
|
|
|
|
* console.log(form.status); // 'VALID'
|
|
|
|
* ```
|
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Create a form group with a group-level validator
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* You include group-level validators as the second arg, or group-level async
|
2016-09-13 14:43:08 -04:00
|
|
|
* validators as the third arg. These come in handy when you want to perform validation
|
|
|
|
* that considers the value of more than one child control.
|
|
|
|
*
|
|
|
|
* ```
|
|
|
|
* const form = new FormGroup({
|
|
|
|
* password: new FormControl('', Validators.minLength(2)),
|
|
|
|
* passwordConfirm: new FormControl('', Validators.minLength(2)),
|
|
|
|
* }, passwordMatchValidator);
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
|
|
|
*
|
2016-09-13 14:43:08 -04:00
|
|
|
* function passwordMatchValidator(g: FormGroup) {
|
|
|
|
* return g.get('password').value === g.get('passwordConfirm').value
|
|
|
|
* ? null : {'mismatch': true};
|
|
|
|
* }
|
|
|
|
* ```
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* Like `FormControl` instances, you choose to pass in
|
2017-07-25 18:01:04 -04:00
|
|
|
* validators and async validators as part of an options object.
|
|
|
|
*
|
|
|
|
* ```
|
|
|
|
* const form = new FormGroup({
|
|
|
|
* password: new FormControl('')
|
|
|
|
* passwordConfirm: new FormControl('')
|
2018-06-01 08:35:06 -04:00
|
|
|
* }, { validators: passwordMatchValidator, asyncValidators: otherValidator });
|
2017-07-25 18:01:04 -04:00
|
|
|
* ```
|
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Set the updateOn property for all controls in a form group
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* The options object is used to set a default value for each child
|
2017-08-09 18:41:53 -04:00
|
|
|
* control's `updateOn` property. If you set `updateOn` to `'blur'` at the
|
2018-06-11 15:24:00 -04:00
|
|
|
* group level, all child controls default to 'blur', unless the child
|
2017-08-09 18:41:53 -04:00
|
|
|
* has explicitly specified a different `updateOn` value.
|
|
|
|
*
|
|
|
|
* ```ts
|
|
|
|
* const c = new FormGroup({
|
2018-06-01 08:35:06 -04:00
|
|
|
* one: new FormControl()
|
|
|
|
* }, { updateOn: 'blur' });
|
2017-08-09 18:41:53 -04:00
|
|
|
* ```
|
2018-10-19 12:36:24 -04:00
|
|
|
*
|
|
|
|
* @publicApi
|
2016-06-08 18:36:24 -04:00
|
|
|
*/
|
2016-06-10 14:15:59 -04:00
|
|
|
export class FormGroup extends AbstractControl {
|
2018-06-01 08:35:06 -04:00
|
|
|
/**
|
|
|
|
* Creates a new `FormGroup` instance.
|
|
|
|
*
|
2018-06-06 13:17:30 -04:00
|
|
|
* @param controls A collection of child controls. The key for each child is the name
|
2018-06-01 08:35:06 -04:00
|
|
|
* under which it is registered.
|
|
|
|
*
|
2018-06-07 14:37:01 -04:00
|
|
|
* @param validatorOrOpts A synchronous validator function, or an array of
|
|
|
|
* such functions, or an `AbstractControlOptions` object that contains validation functions
|
2018-06-06 13:17:30 -04:00
|
|
|
* and a validation trigger.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
|
|
|
* @param asyncValidator A single async validator or array of async validator functions
|
|
|
|
*
|
|
|
|
*/
|
2016-06-08 19:38:52 -04:00
|
|
|
constructor(
|
2017-07-25 18:01:04 -04:00
|
|
|
public controls: {[key: string]: AbstractControl},
|
|
|
|
validatorOrOpts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions|null,
|
|
|
|
asyncValidator?: AsyncValidatorFn|AsyncValidatorFn[]|null) {
|
|
|
|
super(
|
|
|
|
coerceToValidator(validatorOrOpts),
|
|
|
|
coerceToAsyncValidator(asyncValidator, validatorOrOpts));
|
2016-06-08 18:36:24 -04:00
|
|
|
this._initObservables();
|
2017-08-09 18:41:53 -04:00
|
|
|
this._setUpdateStrategy(validatorOrOpts);
|
2016-09-02 18:57:35 -04:00
|
|
|
this._setUpControls();
|
2016-06-08 18:36:24 -04:00
|
|
|
this.updateValueAndValidity({onlySelf: true, emitEvent: false});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-09-13 14:43:08 -04:00
|
|
|
* Registers a control with the group's list of controls.
|
|
|
|
*
|
2018-06-06 13:17:30 -04:00
|
|
|
* This method does not update the value or validity of the control.
|
|
|
|
* Use {@link FormGroup#addControl addControl} instead.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
|
|
|
* @param name The control name to register in the collection
|
|
|
|
* @param control Provides the control for the given name
|
2016-06-08 18:36:24 -04:00
|
|
|
*/
|
2016-06-15 18:15:41 -04:00
|
|
|
registerControl(name: string, control: AbstractControl): AbstractControl {
|
|
|
|
if (this.controls[name]) return this.controls[name];
|
2016-06-08 18:36:24 -04:00
|
|
|
this.controls[name] = control;
|
|
|
|
control.setParent(this);
|
2016-09-02 18:57:35 -04:00
|
|
|
control._registerOnCollectionChange(this._onCollectionChange);
|
2016-06-15 18:15:41 -04:00
|
|
|
return control;
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a control to this group.
|
2018-06-07 14:37:01 -04:00
|
|
|
*
|
2018-06-06 13:17:30 -04:00
|
|
|
* This method also updates the value and validity of the control.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
|
|
|
* @param name The control name to add to the collection
|
|
|
|
* @param control Provides the control for the given name
|
2016-06-08 18:36:24 -04:00
|
|
|
*/
|
|
|
|
addControl(name: string, control: AbstractControl): void {
|
|
|
|
this.registerControl(name, control);
|
|
|
|
this.updateValueAndValidity();
|
2016-09-02 18:57:35 -04:00
|
|
|
this._onCollectionChange();
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove a control from this group.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
|
|
|
* @param name The control name to remove from the collection
|
2016-06-08 18:36:24 -04:00
|
|
|
*/
|
|
|
|
removeControl(name: string): void {
|
2016-09-02 18:57:35 -04:00
|
|
|
if (this.controls[name]) this.controls[name]._registerOnCollectionChange(() => {});
|
2016-09-19 20:15:57 -04:00
|
|
|
delete (this.controls[name]);
|
2016-09-02 18:57:35 -04:00
|
|
|
this.updateValueAndValidity();
|
|
|
|
this._onCollectionChange();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replace an existing control.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
|
|
|
* @param name The control name to replace in the collection
|
|
|
|
* @param control Provides the control for the given name
|
2016-09-02 18:57:35 -04:00
|
|
|
*/
|
|
|
|
setControl(name: string, control: AbstractControl): void {
|
|
|
|
if (this.controls[name]) this.controls[name]._registerOnCollectionChange(() => {});
|
2016-09-19 20:15:57 -04:00
|
|
|
delete (this.controls[name]);
|
2016-09-02 18:57:35 -04:00
|
|
|
if (control) this.registerControl(name, control);
|
2016-06-08 18:36:24 -04:00
|
|
|
this.updateValueAndValidity();
|
2016-09-02 18:57:35 -04:00
|
|
|
this._onCollectionChange();
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-09-13 14:43:08 -04:00
|
|
|
* Check whether there is an enabled control with the given name in the group.
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* Reports false for disabled controls. If you'd like to check for existence in the group
|
2017-10-13 15:27:38 -04:00
|
|
|
* only, use {@link AbstractControl#get get} instead.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
|
|
|
* @param name The control name to check for existence in the collection
|
2018-06-11 15:24:00 -04:00
|
|
|
*
|
|
|
|
* @returns false for disabled controls, true otherwise.
|
2016-06-08 18:36:24 -04:00
|
|
|
*/
|
|
|
|
contains(controlName: string): boolean {
|
2016-09-12 18:15:50 -04:00
|
|
|
return this.controls.hasOwnProperty(controlName) && this.controls[controlName].enabled;
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
2016-09-13 14:43:08 -04:00
|
|
|
/**
|
2018-06-01 08:35:06 -04:00
|
|
|
* Sets the value of the `FormGroup`. It accepts an object that matches
|
|
|
|
* the structure of the group, with control names as keys.
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2018-09-20 10:18:14 -04:00
|
|
|
* @usageNotes
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Set the complete value for the form group
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ```
|
|
|
|
* const form = new FormGroup({
|
|
|
|
* first: new FormControl(),
|
|
|
|
* last: new FormControl()
|
|
|
|
* });
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* console.log(form.value); // {first: null, last: null}
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* form.setValue({first: 'Nancy', last: 'Drew'});
|
|
|
|
* console.log(form.value); // {first: 'Nancy', last: 'Drew'}
|
|
|
|
* ```
|
2018-09-20 10:18:14 -04:00
|
|
|
*
|
2018-06-07 14:37:01 -04:00
|
|
|
* @throws When strict checks fail, such as setting the value of a control
|
2018-06-06 13:17:30 -04:00
|
|
|
* that doesn't exist or if you excluding the value of a control.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
2018-06-06 13:17:30 -04:00
|
|
|
* @param value The new value for the control that matches the structure of the group.
|
2018-06-11 16:48:35 -04:00
|
|
|
* @param options Configuration options that determine how the control propagates changes
|
|
|
|
* and emits events after the value changes.
|
2018-06-07 14:37:01 -04:00
|
|
|
* The configuration options are passed to the {@link AbstractControl#updateValueAndValidity
|
|
|
|
* updateValueAndValidity} method.
|
2018-06-06 13:17:30 -04:00
|
|
|
*
|
2018-06-07 14:37:01 -04:00
|
|
|
* * `onlySelf`: When true, each change only affects this control, and not its parent. Default is
|
|
|
|
* false.
|
2018-06-11 16:48:35 -04:00
|
|
|
* * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
|
|
|
|
* `valueChanges`
|
|
|
|
* observables emit events with the latest status and value when the control value is updated.
|
|
|
|
* When false, no events are emitted.
|
2016-09-13 14:43:08 -04:00
|
|
|
*/
|
2017-04-27 16:39:21 -04:00
|
|
|
setValue(value: {[key: string]: any}, options: {onlySelf?: boolean, emitEvent?: boolean} = {}):
|
|
|
|
void {
|
2016-08-05 16:35:17 -04:00
|
|
|
this._checkAllValuesPresent(value);
|
2016-10-03 19:46:05 -04:00
|
|
|
Object.keys(value).forEach(name => {
|
2016-07-08 16:04:25 -04:00
|
|
|
this._throwIfControlMissing(name);
|
2017-04-27 16:39:21 -04:00
|
|
|
this.controls[name].setValue(value[name], {onlySelf: true, emitEvent: options.emitEvent});
|
2016-08-05 16:35:17 -04:00
|
|
|
});
|
2017-04-27 16:39:21 -04:00
|
|
|
this.updateValueAndValidity(options);
|
2016-08-05 16:35:17 -04:00
|
|
|
}
|
|
|
|
|
2016-09-13 14:43:08 -04:00
|
|
|
/**
|
2018-06-01 08:35:06 -04:00
|
|
|
* Patches the value of the `FormGroup`. It accepts an object with control
|
2018-06-11 15:24:00 -04:00
|
|
|
* names as keys, and does its best to match the values to the correct controls
|
2018-06-01 08:35:06 -04:00
|
|
|
* in the group.
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* It accepts both super-sets and sub-sets of the group without throwing an error.
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2018-09-20 10:18:14 -04:00
|
|
|
* @usageNotes
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Patch the value for a form group
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2018-09-20 10:18:14 -04:00
|
|
|
* ```
|
|
|
|
* const form = new FormGroup({
|
|
|
|
* first: new FormControl(),
|
|
|
|
* last: new FormControl()
|
|
|
|
* });
|
|
|
|
* console.log(form.value); // {first: null, last: null}
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2018-09-20 10:18:14 -04:00
|
|
|
* form.patchValue({first: 'Nancy'});
|
|
|
|
* console.log(form.value); // {first: 'Nancy', last: null}
|
|
|
|
* ```
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
2018-09-11 05:21:37 -04:00
|
|
|
* @param value The object that matches the structure of the group.
|
|
|
|
* @param options Configuration options that determine how the control propagates changes and
|
|
|
|
* emits events after the value is patched.
|
|
|
|
* * `onlySelf`: When true, each change only affects this control and not its parent. Default is
|
2018-06-11 16:48:35 -04:00
|
|
|
* true.
|
|
|
|
* * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
|
|
|
|
* `valueChanges`
|
|
|
|
* observables emit events with the latest status and value when the control value is updated.
|
|
|
|
* When false, no events are emitted.
|
2018-06-07 14:37:01 -04:00
|
|
|
* The configuration options are passed to the {@link AbstractControl#updateValueAndValidity
|
|
|
|
* updateValueAndValidity} method.
|
2016-09-13 14:43:08 -04:00
|
|
|
*/
|
2017-04-27 16:39:21 -04:00
|
|
|
patchValue(value: {[key: string]: any}, options: {onlySelf?: boolean, emitEvent?: boolean} = {}):
|
|
|
|
void {
|
2016-10-03 19:46:05 -04:00
|
|
|
Object.keys(value).forEach(name => {
|
2016-08-05 16:35:17 -04:00
|
|
|
if (this.controls[name]) {
|
2017-04-27 16:39:21 -04:00
|
|
|
this.controls[name].patchValue(value[name], {onlySelf: true, emitEvent: options.emitEvent});
|
2016-08-05 16:35:17 -04:00
|
|
|
}
|
2016-07-08 16:04:25 -04:00
|
|
|
});
|
2017-04-27 16:39:21 -04:00
|
|
|
this.updateValueAndValidity(options);
|
2016-07-08 16:04:25 -04:00
|
|
|
}
|
|
|
|
|
2016-09-13 14:43:08 -04:00
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* Resets the `FormGroup`, marks all descendants are marked `pristine` and `untouched`, and
|
|
|
|
* the value of all descendants to null.
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* You reset to a specific form state by passing in a map of states
|
2016-09-13 14:43:08 -04:00
|
|
|
* that matches the structure of your form, with control names as keys. The state
|
2018-06-11 15:24:00 -04:00
|
|
|
* is a standalone value or a form state object with both a value and a disabled
|
2016-09-13 14:43:08 -04:00
|
|
|
* status.
|
2018-06-07 14:37:01 -04:00
|
|
|
*
|
2018-06-27 15:33:16 -04:00
|
|
|
* @param formState Resets the control with an initial value,
|
|
|
|
* or an object that defines the initial value and disabled state.
|
2018-06-07 14:37:01 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* @param options Configuration options that determine how the control propagates changes
|
|
|
|
* and emits events when the group is reset.
|
2018-06-07 14:37:01 -04:00
|
|
|
* * `onlySelf`: When true, each change only affects this control, and not its parent. Default is
|
|
|
|
* false.
|
2018-06-11 16:48:35 -04:00
|
|
|
* * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
|
|
|
|
* `valueChanges`
|
|
|
|
* observables emit events with the latest status and value when the control is reset.
|
|
|
|
* When false, no events are emitted.
|
2018-06-07 14:37:01 -04:00
|
|
|
* The configuration options are passed to the {@link AbstractControl#updateValueAndValidity
|
|
|
|
* updateValueAndValidity} method.
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2018-06-07 14:31:25 -04:00
|
|
|
* @usageNotes
|
2018-06-07 14:37:01 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Reset the form group values
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
|
|
|
* ```ts
|
2018-06-01 08:35:06 -04:00
|
|
|
* const form = new FormGroup({
|
|
|
|
* first: new FormControl('first name'),
|
|
|
|
* last: new FormControl('last name')
|
|
|
|
* });
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* console.log(form.value); // {first: 'first name', last: 'last name'}
|
|
|
|
*
|
|
|
|
* form.reset({ first: 'name', last: 'last name' });
|
|
|
|
*
|
|
|
|
* console.log(form.value); // {first: 'name', last: 'last name'}
|
2016-09-13 14:43:08 -04:00
|
|
|
* ```
|
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Reset the form group values and disabled status
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
|
|
|
* ```
|
2018-06-01 08:35:06 -04:00
|
|
|
* const form = new FormGroup({
|
|
|
|
* first: new FormControl('first name'),
|
|
|
|
* last: new FormControl('last name')
|
|
|
|
* });
|
|
|
|
*
|
|
|
|
* form.reset({
|
2016-09-13 14:43:08 -04:00
|
|
|
* first: {value: 'name', disabled: true},
|
|
|
|
* last: 'last'
|
|
|
|
* });
|
|
|
|
*
|
|
|
|
* console.log(this.form.value); // {first: 'name', last: 'last name'}
|
|
|
|
* console.log(this.form.get('first').status); // 'DISABLED'
|
|
|
|
* ```
|
|
|
|
*/
|
2017-04-27 16:39:21 -04:00
|
|
|
reset(value: any = {}, options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
2016-07-12 18:02:25 -04:00
|
|
|
this._forEachChild((control: AbstractControl, name: string) => {
|
2017-04-27 16:39:21 -04:00
|
|
|
control.reset(value[name], {onlySelf: true, emitEvent: options.emitEvent});
|
2016-07-12 18:02:25 -04:00
|
|
|
});
|
2017-04-27 16:39:21 -04:00
|
|
|
this.updateValueAndValidity(options);
|
|
|
|
this._updatePristine(options);
|
|
|
|
this._updateTouched(options);
|
2016-07-12 18:02:25 -04:00
|
|
|
}
|
|
|
|
|
2016-09-13 14:43:08 -04:00
|
|
|
/**
|
2018-04-05 05:58:12 -04:00
|
|
|
* The aggregate value of the `FormGroup`, including any disabled controls.
|
2016-09-13 14:43:08 -04:00
|
|
|
*
|
2018-06-06 13:17:30 -04:00
|
|
|
* Retrieves all values regardless of disabled status.
|
2018-06-07 14:31:25 -04:00
|
|
|
* The `value` property is the best way to get the value of the group, because
|
|
|
|
* it excludes disabled controls in the `FormGroup`.
|
2016-09-13 14:43:08 -04:00
|
|
|
*/
|
2016-11-08 18:44:36 -05:00
|
|
|
getRawValue(): any {
|
2016-08-24 19:58:43 -04:00
|
|
|
return this._reduceChildren(
|
|
|
|
{}, (acc: {[k: string]: AbstractControl}, control: AbstractControl, name: string) => {
|
2016-11-18 06:44:05 -05:00
|
|
|
acc[name] = control instanceof FormControl ? control.value : (<any>control).getRawValue();
|
2016-08-24 19:58:43 -04:00
|
|
|
return acc;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-08-07 18:39:25 -04:00
|
|
|
/** @internal */
|
|
|
|
_syncPendingControls(): boolean {
|
|
|
|
let subtreeUpdated = this._reduceChildren(false, (updated: boolean, child: AbstractControl) => {
|
|
|
|
return child._syncPendingControls() ? true : updated;
|
|
|
|
});
|
|
|
|
if (subtreeUpdated) this.updateValueAndValidity({onlySelf: true});
|
|
|
|
return subtreeUpdated;
|
|
|
|
}
|
|
|
|
|
2016-07-08 16:04:25 -04:00
|
|
|
/** @internal */
|
|
|
|
_throwIfControlMissing(name: string): void {
|
2016-08-05 16:35:17 -04:00
|
|
|
if (!Object.keys(this.controls).length) {
|
2016-08-25 03:50:16 -04:00
|
|
|
throw new Error(`
|
2016-08-05 16:35:17 -04:00
|
|
|
There are no form controls registered with this group yet. If you're using ngModel,
|
|
|
|
you may want to check next tick (e.g. use setTimeout).
|
|
|
|
`);
|
|
|
|
}
|
2016-07-08 16:04:25 -04:00
|
|
|
if (!this.controls[name]) {
|
2016-08-25 03:50:16 -04:00
|
|
|
throw new Error(`Cannot find form control with name: ${name}.`);
|
2016-07-08 16:04:25 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-12 18:02:25 -04:00
|
|
|
/** @internal */
|
2016-07-15 19:26:19 -04:00
|
|
|
_forEachChild(cb: (v: any, k: string) => void): void {
|
2016-10-03 19:46:05 -04:00
|
|
|
Object.keys(this.controls).forEach(k => cb(this.controls[k], k));
|
2016-07-15 19:26:19 -04:00
|
|
|
}
|
2016-07-12 18:02:25 -04:00
|
|
|
|
2016-06-08 18:36:24 -04:00
|
|
|
/** @internal */
|
2016-10-19 12:55:50 -04:00
|
|
|
_setUpControls(): void {
|
2016-09-02 18:57:35 -04:00
|
|
|
this._forEachChild((control: AbstractControl) => {
|
|
|
|
control.setParent(this);
|
|
|
|
control._registerOnCollectionChange(this._onCollectionChange);
|
|
|
|
});
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal */
|
2017-09-13 17:00:10 -04:00
|
|
|
_updateValue(): void { (this as{value: any}).value = this._reduceValue(); }
|
2016-06-08 18:36:24 -04:00
|
|
|
|
|
|
|
/** @internal */
|
2016-07-12 18:02:25 -04:00
|
|
|
_anyControls(condition: Function): boolean {
|
2016-10-19 12:55:50 -04:00
|
|
|
let res = false;
|
2016-07-12 18:02:25 -04:00
|
|
|
this._forEachChild((control: AbstractControl, name: string) => {
|
|
|
|
res = res || (this.contains(name) && condition(control));
|
2016-06-08 18:36:24 -04:00
|
|
|
});
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
_reduceValue() {
|
|
|
|
return this._reduceChildren(
|
|
|
|
{}, (acc: {[k: string]: AbstractControl}, control: AbstractControl, name: string) => {
|
2016-08-24 19:58:43 -04:00
|
|
|
if (control.enabled || this.disabled) {
|
|
|
|
acc[name] = control.value;
|
|
|
|
}
|
2016-06-08 18:36:24 -04:00
|
|
|
return acc;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
_reduceChildren(initValue: any, fn: Function) {
|
2016-10-19 12:55:50 -04:00
|
|
|
let res = initValue;
|
2016-08-24 19:58:43 -04:00
|
|
|
this._forEachChild(
|
|
|
|
(control: AbstractControl, name: string) => { res = fn(res, control, name); });
|
2016-06-08 18:36:24 -04:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal */
|
2016-08-24 19:58:43 -04:00
|
|
|
_allControlsDisabled(): boolean {
|
2016-11-12 08:08:58 -05:00
|
|
|
for (const controlName of Object.keys(this.controls)) {
|
2016-08-24 19:58:43 -04:00
|
|
|
if (this.controls[controlName].enabled) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2016-09-08 15:21:48 -04:00
|
|
|
return Object.keys(this.controls).length > 0 || this.disabled;
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
2016-08-05 16:35:17 -04:00
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
_checkAllValuesPresent(value: any): void {
|
|
|
|
this._forEachChild((control: AbstractControl, name: string) => {
|
|
|
|
if (value[name] === undefined) {
|
2016-08-25 03:50:16 -04:00
|
|
|
throw new Error(`Must supply a value for form control with name: '${name}'.`);
|
2016-08-05 16:35:17 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-04-05 05:58:12 -04:00
|
|
|
* Tracks the value and validity state of an array of `FormControl`,
|
|
|
|
* `FormGroup` or `FormArray` instances.
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
2018-04-05 05:58:12 -04:00
|
|
|
* A `FormArray` aggregates the values of each child `FormControl` into an array.
|
2018-06-06 13:17:30 -04:00
|
|
|
* It calculates its status by reducing the status values of its children. For example, if one of
|
2016-09-13 16:07:40 -04:00
|
|
|
* the controls in a `FormArray` is invalid, the entire array becomes invalid.
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
2016-06-10 14:15:59 -04:00
|
|
|
* `FormArray` is one of the three fundamental building blocks used to define forms in Angular,
|
2018-04-05 05:58:12 -04:00
|
|
|
* along with `FormControl` and `FormGroup`.
|
2016-09-13 16:07:40 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* @usageNotes
|
2016-09-13 16:07:40 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Create an array of form controls
|
2016-09-13 16:07:40 -04:00
|
|
|
*
|
|
|
|
* ```
|
|
|
|
* const arr = new FormArray([
|
|
|
|
* new FormControl('Nancy', Validators.minLength(2)),
|
|
|
|
* new FormControl('Drew'),
|
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* console.log(arr.value); // ['Nancy', 'Drew']
|
|
|
|
* console.log(arr.status); // 'VALID'
|
|
|
|
* ```
|
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Create a form array with array-level validators
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* You include array-level validators and async validators. These come in handy
|
2017-07-25 18:01:04 -04:00
|
|
|
* when you want to perform validation that considers the value of more than one child
|
|
|
|
* control.
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* The two types of validators are passed in separately as the second and third arg
|
2017-07-25 18:01:04 -04:00
|
|
|
* respectively, or together as part of an options object.
|
|
|
|
*
|
|
|
|
* ```
|
|
|
|
* const arr = new FormArray([
|
|
|
|
* new FormControl('Nancy'),
|
|
|
|
* new FormControl('Drew')
|
|
|
|
* ], {validators: myValidator, asyncValidators: myAsyncValidator});
|
|
|
|
* ```
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
|
|
|
* ### Set the updateOn property for all controls in a form array
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* The options object is used to set a default value for each child
|
2017-08-09 18:41:53 -04:00
|
|
|
* control's `updateOn` property. If you set `updateOn` to `'blur'` at the
|
2018-06-11 15:24:00 -04:00
|
|
|
* array level, all child controls default to 'blur', unless the child
|
2017-08-09 18:41:53 -04:00
|
|
|
* has explicitly specified a different `updateOn` value.
|
|
|
|
*
|
|
|
|
* ```ts
|
2018-06-01 08:35:06 -04:00
|
|
|
* const arr = new FormArray([
|
2017-08-09 18:41:53 -04:00
|
|
|
* new FormControl()
|
|
|
|
* ], {updateOn: 'blur'});
|
|
|
|
* ```
|
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Adding or removing controls from a form array
|
2016-06-08 18:36:24 -04:00
|
|
|
*
|
|
|
|
* To change the controls in the array, use the `push`, `insert`, or `removeAt` methods
|
2016-06-10 14:15:59 -04:00
|
|
|
* in `FormArray` itself. These methods ensure the controls are properly tracked in the
|
2016-06-08 18:36:24 -04:00
|
|
|
* form's hierarchy. Do not modify the array of `AbstractControl`s used to instantiate
|
2018-06-11 15:24:00 -04:00
|
|
|
* the `FormArray` directly, as that result in strange and unexpected behavior such
|
2016-06-08 18:36:24 -04:00
|
|
|
* as broken change detection.
|
|
|
|
*
|
2018-10-19 12:36:24 -04:00
|
|
|
* @publicApi
|
2016-06-08 18:36:24 -04:00
|
|
|
*/
|
2016-06-10 14:15:59 -04:00
|
|
|
export class FormArray extends AbstractControl {
|
2018-06-01 08:35:06 -04:00
|
|
|
/**
|
|
|
|
* Creates a new `FormArray` instance.
|
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* @param controls An array of child controls. Each child control is given an index
|
2018-09-20 04:23:04 -04:00
|
|
|
* where it is registered.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
2018-06-07 14:37:01 -04:00
|
|
|
* @param validatorOrOpts A synchronous validator function, or an array of
|
|
|
|
* such functions, or an `AbstractControlOptions` object that contains validation functions
|
2018-06-06 13:17:30 -04:00
|
|
|
* and a validation trigger.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
|
|
|
* @param asyncValidator A single async validator or array of async validator functions
|
|
|
|
*
|
|
|
|
*/
|
2016-06-08 19:38:52 -04:00
|
|
|
constructor(
|
2017-07-25 18:01:04 -04:00
|
|
|
public controls: AbstractControl[],
|
|
|
|
validatorOrOpts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions|null,
|
|
|
|
asyncValidator?: AsyncValidatorFn|AsyncValidatorFn[]|null) {
|
|
|
|
super(
|
|
|
|
coerceToValidator(validatorOrOpts),
|
|
|
|
coerceToAsyncValidator(asyncValidator, validatorOrOpts));
|
2016-06-08 18:36:24 -04:00
|
|
|
this._initObservables();
|
2017-08-09 18:41:53 -04:00
|
|
|
this._setUpdateStrategy(validatorOrOpts);
|
2016-09-02 18:57:35 -04:00
|
|
|
this._setUpControls();
|
2016-06-08 18:36:24 -04:00
|
|
|
this.updateValueAndValidity({onlySelf: true, emitEvent: false});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-04-05 05:58:12 -04:00
|
|
|
* Get the `AbstractControl` at the given `index` in the array.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
|
|
|
* @param index Index in the array to retrieve the control
|
2016-06-08 18:36:24 -04:00
|
|
|
*/
|
|
|
|
at(index: number): AbstractControl { return this.controls[index]; }
|
|
|
|
|
|
|
|
/**
|
2018-04-05 05:58:12 -04:00
|
|
|
* Insert a new `AbstractControl` at the end of the array.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
|
|
|
* @param control Form control to be inserted
|
2016-06-08 18:36:24 -04:00
|
|
|
*/
|
|
|
|
push(control: AbstractControl): void {
|
|
|
|
this.controls.push(control);
|
2016-09-02 18:57:35 -04:00
|
|
|
this._registerControl(control);
|
2016-06-08 18:36:24 -04:00
|
|
|
this.updateValueAndValidity();
|
2016-09-02 18:57:35 -04:00
|
|
|
this._onCollectionChange();
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
2018-06-01 08:35:06 -04:00
|
|
|
/**
|
|
|
|
* Insert a new `AbstractControl` at the given `index` in the array.
|
|
|
|
*
|
|
|
|
* @param index Index in the array to insert the control
|
|
|
|
* @param control Form control to be inserted
|
|
|
|
*/
|
2016-06-08 18:36:24 -04:00
|
|
|
insert(index: number, control: AbstractControl): void {
|
2016-10-19 12:55:50 -04:00
|
|
|
this.controls.splice(index, 0, control);
|
|
|
|
|
2016-09-02 18:57:35 -04:00
|
|
|
this._registerControl(control);
|
2016-06-08 18:36:24 -04:00
|
|
|
this.updateValueAndValidity();
|
|
|
|
}
|
|
|
|
|
2018-06-01 08:35:06 -04:00
|
|
|
/**
|
|
|
|
* Remove the control at the given `index` in the array.
|
|
|
|
*
|
|
|
|
* @param index Index in the array to remove the control
|
|
|
|
*/
|
2016-06-08 18:36:24 -04:00
|
|
|
removeAt(index: number): void {
|
2016-09-02 18:57:35 -04:00
|
|
|
if (this.controls[index]) this.controls[index]._registerOnCollectionChange(() => {});
|
2016-10-19 12:55:50 -04:00
|
|
|
this.controls.splice(index, 1);
|
2016-06-08 18:36:24 -04:00
|
|
|
this.updateValueAndValidity();
|
2016-09-02 18:57:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replace an existing control.
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
|
|
|
* @param index Index in the array to replace the control
|
|
|
|
* @param control The `AbstractControl` control to replace the existing control
|
2016-09-02 18:57:35 -04:00
|
|
|
*/
|
|
|
|
setControl(index: number, control: AbstractControl): void {
|
|
|
|
if (this.controls[index]) this.controls[index]._registerOnCollectionChange(() => {});
|
2016-10-19 12:55:50 -04:00
|
|
|
this.controls.splice(index, 1);
|
2016-09-02 18:57:35 -04:00
|
|
|
|
|
|
|
if (control) {
|
2016-10-19 12:55:50 -04:00
|
|
|
this.controls.splice(index, 0, control);
|
2016-09-02 18:57:35 -04:00
|
|
|
this._registerControl(control);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.updateValueAndValidity();
|
|
|
|
this._onCollectionChange();
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Length of the control array.
|
|
|
|
*/
|
|
|
|
get length(): number { return this.controls.length; }
|
|
|
|
|
2016-09-13 16:07:40 -04:00
|
|
|
/**
|
2018-06-01 08:35:06 -04:00
|
|
|
* Sets the value of the `FormArray`. It accepts an array that matches
|
|
|
|
* the structure of the control.
|
2016-09-13 16:07:40 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* This method performs strict checks, and throws an error if you try
|
2016-09-13 16:07:40 -04:00
|
|
|
* to set the value of a control that doesn't exist or if you exclude the
|
|
|
|
* value of a control.
|
|
|
|
*
|
2018-09-20 10:18:14 -04:00
|
|
|
* @usageNotes
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Set the values for the controls in the form array
|
2016-09-13 16:07:40 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ```
|
|
|
|
* const arr = new FormArray([
|
|
|
|
* new FormControl(),
|
|
|
|
* new FormControl()
|
|
|
|
* ]);
|
|
|
|
* console.log(arr.value); // [null, null]
|
|
|
|
*
|
|
|
|
* arr.setValue(['Nancy', 'Drew']);
|
|
|
|
* console.log(arr.value); // ['Nancy', 'Drew']
|
|
|
|
* ```
|
|
|
|
*
|
2018-06-07 14:31:25 -04:00
|
|
|
* @param value Array of values for the controls
|
2018-06-11 16:48:35 -04:00
|
|
|
* @param options Configure options that determine how the control propagates changes and
|
|
|
|
* emits events after the value changes
|
|
|
|
*
|
|
|
|
* * `onlySelf`: When true, each change only affects this control, and not its parent. Default
|
|
|
|
* is false.
|
|
|
|
* * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
|
|
|
|
* `valueChanges`
|
|
|
|
* observables emit events with the latest status and value when the control value is updated.
|
|
|
|
* When false, no events are emitted.
|
2018-06-07 14:37:01 -04:00
|
|
|
* The configuration options are passed to the {@link AbstractControl#updateValueAndValidity
|
|
|
|
* updateValueAndValidity} method.
|
2016-09-13 16:07:40 -04:00
|
|
|
*/
|
2017-04-27 16:39:21 -04:00
|
|
|
setValue(value: any[], options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
2016-08-05 16:35:17 -04:00
|
|
|
this._checkAllValuesPresent(value);
|
2016-07-08 16:04:25 -04:00
|
|
|
value.forEach((newValue: any, index: number) => {
|
|
|
|
this._throwIfControlMissing(index);
|
2017-04-27 16:39:21 -04:00
|
|
|
this.at(index).setValue(newValue, {onlySelf: true, emitEvent: options.emitEvent});
|
2016-08-05 16:35:17 -04:00
|
|
|
});
|
2017-04-27 16:39:21 -04:00
|
|
|
this.updateValueAndValidity(options);
|
2016-08-05 16:35:17 -04:00
|
|
|
}
|
|
|
|
|
2016-09-13 16:07:40 -04:00
|
|
|
/**
|
2018-06-01 08:35:06 -04:00
|
|
|
* Patches the value of the `FormArray`. It accepts an array that matches the
|
2018-06-11 15:24:00 -04:00
|
|
|
* structure of the control, and does its best to match the values to the correct
|
2018-06-01 08:35:06 -04:00
|
|
|
* controls in the group.
|
2016-09-13 16:07:40 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* It accepts both super-sets and sub-sets of the array without throwing an error.
|
2016-09-13 16:07:40 -04:00
|
|
|
*
|
2018-09-20 10:18:14 -04:00
|
|
|
* @usageNotes
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Patch the values for controls in a form array
|
2016-09-13 16:07:40 -04:00
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ```
|
|
|
|
* const arr = new FormArray([
|
|
|
|
* new FormControl(),
|
|
|
|
* new FormControl()
|
|
|
|
* ]);
|
|
|
|
* console.log(arr.value); // [null, null]
|
|
|
|
*
|
|
|
|
* arr.patchValue(['Nancy']);
|
|
|
|
* console.log(arr.value); // ['Nancy', null]
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param value Array of latest values for the controls
|
2018-06-11 16:48:35 -04:00
|
|
|
* @param options Configure options that determine how the control propagates changes and
|
|
|
|
* emits events after the value changes
|
|
|
|
*
|
|
|
|
* * `onlySelf`: When true, each change only affects this control, and not its parent. Default
|
|
|
|
* is false.
|
|
|
|
* * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
|
|
|
|
* `valueChanges`
|
|
|
|
* observables emit events with the latest status and value when the control value is updated.
|
|
|
|
* When false, no events are emitted.
|
2018-06-07 14:37:01 -04:00
|
|
|
* The configuration options are passed to the {@link AbstractControl#updateValueAndValidity
|
|
|
|
* updateValueAndValidity} method.
|
2016-09-13 16:07:40 -04:00
|
|
|
*/
|
2017-04-27 16:39:21 -04:00
|
|
|
patchValue(value: any[], options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
2016-08-05 16:35:17 -04:00
|
|
|
value.forEach((newValue: any, index: number) => {
|
|
|
|
if (this.at(index)) {
|
2017-04-27 16:39:21 -04:00
|
|
|
this.at(index).patchValue(newValue, {onlySelf: true, emitEvent: options.emitEvent});
|
2016-08-05 16:35:17 -04:00
|
|
|
}
|
2016-07-08 16:04:25 -04:00
|
|
|
});
|
2017-04-27 16:39:21 -04:00
|
|
|
this.updateValueAndValidity(options);
|
2016-07-08 16:04:25 -04:00
|
|
|
}
|
|
|
|
|
2016-09-13 16:07:40 -04:00
|
|
|
/**
|
2018-06-11 15:24:00 -04:00
|
|
|
* Resets the `FormArray` and all descendants are marked `pristine` and `untouched`, and the
|
|
|
|
* value of all descendants to null or null maps.
|
2016-09-13 16:07:40 -04:00
|
|
|
*
|
2018-06-11 15:24:00 -04:00
|
|
|
* You reset to a specific form state by passing in an array of states
|
|
|
|
* that matches the structure of the control. The state is a standalone value
|
2016-09-13 16:07:40 -04:00
|
|
|
* or a form state object with both a value and a disabled status.
|
|
|
|
*
|
2018-09-20 10:18:14 -04:00
|
|
|
* @usageNotes
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Reset the values in a form array
|
2016-09-13 16:07:40 -04:00
|
|
|
*
|
|
|
|
* ```ts
|
2018-06-01 08:35:06 -04:00
|
|
|
* const arr = new FormArray([
|
|
|
|
* new FormControl(),
|
|
|
|
* new FormControl()
|
|
|
|
* ]);
|
|
|
|
* arr.reset(['name', 'last name']);
|
2016-09-13 16:07:40 -04:00
|
|
|
*
|
|
|
|
* console.log(this.arr.value); // ['name', 'last name']
|
|
|
|
* ```
|
|
|
|
*
|
2018-06-01 08:35:06 -04:00
|
|
|
* ### Reset the values in a form array and the disabled status for the first control
|
2016-09-13 16:07:40 -04:00
|
|
|
*
|
|
|
|
* ```
|
|
|
|
* this.arr.reset([
|
|
|
|
* {value: 'name', disabled: true},
|
|
|
|
* 'last'
|
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* console.log(this.arr.value); // ['name', 'last name']
|
|
|
|
* console.log(this.arr.get(0).status); // 'DISABLED'
|
|
|
|
* ```
|
2018-06-01 08:35:06 -04:00
|
|
|
*
|
2018-06-07 14:31:25 -04:00
|
|
|
* @param value Array of values for the controls
|
2018-06-11 16:48:35 -04:00
|
|
|
* @param options Configure options that determine how the control propagates changes and
|
|
|
|
* emits events after the value changes
|
|
|
|
*
|
|
|
|
* * `onlySelf`: When true, each change only affects this control, and not its parent. Default
|
|
|
|
* is false.
|
|
|
|
* * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
|
|
|
|
* `valueChanges`
|
|
|
|
* observables emit events with the latest status and value when the control is reset.
|
|
|
|
* When false, no events are emitted.
|
2018-06-07 14:37:01 -04:00
|
|
|
* The configuration options are passed to the {@link AbstractControl#updateValueAndValidity
|
|
|
|
* updateValueAndValidity} method.
|
2016-09-13 16:07:40 -04:00
|
|
|
*/
|
2017-04-27 16:39:21 -04:00
|
|
|
reset(value: any = [], options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
2016-07-12 18:02:25 -04:00
|
|
|
this._forEachChild((control: AbstractControl, index: number) => {
|
2017-04-27 16:39:21 -04:00
|
|
|
control.reset(value[index], {onlySelf: true, emitEvent: options.emitEvent});
|
2016-07-12 18:02:25 -04:00
|
|
|
});
|
2017-04-27 16:39:21 -04:00
|
|
|
this.updateValueAndValidity(options);
|
|
|
|
this._updatePristine(options);
|
|
|
|
this._updateTouched(options);
|
2016-07-12 18:02:25 -04:00
|
|
|
}
|
|
|
|
|
2016-09-13 16:07:40 -04:00
|
|
|
/**
|
|
|
|
* The aggregate value of the array, including any disabled controls.
|
|
|
|
*
|
2018-06-07 14:31:25 -04:00
|
|
|
* Reports all values regardless of disabled status.
|
|
|
|
* For enabled controls only, the `value` property is the best way to get the value of the array.
|
2016-09-13 16:07:40 -04:00
|
|
|
*/
|
2016-11-18 06:44:05 -05:00
|
|
|
getRawValue(): any[] {
|
|
|
|
return this.controls.map((control: AbstractControl) => {
|
|
|
|
return control instanceof FormControl ? control.value : (<any>control).getRawValue();
|
|
|
|
});
|
|
|
|
}
|
2016-08-24 19:58:43 -04:00
|
|
|
|
2017-08-07 18:39:25 -04:00
|
|
|
/** @internal */
|
|
|
|
_syncPendingControls(): boolean {
|
|
|
|
let subtreeUpdated = this.controls.reduce((updated: boolean, child: AbstractControl) => {
|
|
|
|
return child._syncPendingControls() ? true : updated;
|
|
|
|
}, false);
|
|
|
|
if (subtreeUpdated) this.updateValueAndValidity({onlySelf: true});
|
|
|
|
return subtreeUpdated;
|
|
|
|
}
|
|
|
|
|
2016-07-08 16:04:25 -04:00
|
|
|
/** @internal */
|
|
|
|
_throwIfControlMissing(index: number): void {
|
2016-08-05 16:35:17 -04:00
|
|
|
if (!this.controls.length) {
|
2016-08-25 03:50:16 -04:00
|
|
|
throw new Error(`
|
2016-08-05 16:35:17 -04:00
|
|
|
There are no form controls registered with this array yet. If you're using ngModel,
|
|
|
|
you may want to check next tick (e.g. use setTimeout).
|
|
|
|
`);
|
|
|
|
}
|
2016-07-08 16:04:25 -04:00
|
|
|
if (!this.at(index)) {
|
2016-08-25 03:50:16 -04:00
|
|
|
throw new Error(`Cannot find form control at index ${index}`);
|
2016-07-08 16:04:25 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-12 18:02:25 -04:00
|
|
|
/** @internal */
|
|
|
|
_forEachChild(cb: Function): void {
|
|
|
|
this.controls.forEach((control: AbstractControl, index: number) => { cb(control, index); });
|
|
|
|
}
|
|
|
|
|
2016-06-08 18:36:24 -04:00
|
|
|
/** @internal */
|
2016-08-24 19:58:43 -04:00
|
|
|
_updateValue(): void {
|
2017-09-13 17:00:10 -04:00
|
|
|
(this as{value: any}).value =
|
|
|
|
this.controls.filter((control) => control.enabled || this.disabled)
|
|
|
|
.map((control) => control.value);
|
2016-08-24 19:58:43 -04:00
|
|
|
}
|
2016-06-08 18:36:24 -04:00
|
|
|
|
|
|
|
/** @internal */
|
2016-07-12 18:02:25 -04:00
|
|
|
_anyControls(condition: Function): boolean {
|
2016-08-24 19:58:43 -04:00
|
|
|
return this.controls.some((control: AbstractControl) => control.enabled && condition(control));
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal */
|
2016-09-02 18:57:35 -04:00
|
|
|
_setUpControls(): void {
|
|
|
|
this._forEachChild((control: AbstractControl) => this._registerControl(control));
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
2016-08-05 16:35:17 -04:00
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
_checkAllValuesPresent(value: any): void {
|
|
|
|
this._forEachChild((control: AbstractControl, i: number) => {
|
|
|
|
if (value[i] === undefined) {
|
2016-08-25 03:50:16 -04:00
|
|
|
throw new Error(`Must supply a value for form control at index: ${i}.`);
|
2016-08-05 16:35:17 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2016-08-24 19:58:43 -04:00
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
_allControlsDisabled(): boolean {
|
2016-11-12 08:08:58 -05:00
|
|
|
for (const control of this.controls) {
|
2016-08-24 19:58:43 -04:00
|
|
|
if (control.enabled) return false;
|
|
|
|
}
|
2016-09-08 15:21:48 -04:00
|
|
|
return this.controls.length > 0 || this.disabled;
|
2016-08-24 19:58:43 -04:00
|
|
|
}
|
2016-09-02 18:57:35 -04:00
|
|
|
|
|
|
|
private _registerControl(control: AbstractControl) {
|
|
|
|
control.setParent(this);
|
|
|
|
control._registerOnCollectionChange(this._onCollectionChange);
|
|
|
|
}
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|