2016-06-23 09:47:54 -07: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
*/
2016-08-30 12:13:38 -07:00
import { fromPromise } from 'rxjs/observable/fromPromise' ;
2016-08-02 15:53:34 -07:00
2016-06-13 11:27:04 -07:00
import { composeAsyncValidators , composeValidators } from './directives/shared' ;
2016-06-08 16:38:52 -07:00
import { AsyncValidatorFn , ValidatorFn } from './directives/validators' ;
2016-08-02 15:53:34 -07:00
import { EventEmitter , Observable } from './facade/async' ;
2016-09-18 15:55:08 -07:00
import { isPromise } from './private_import_core' ;
2016-06-13 11:27:04 -07:00
2016-07-08 13:04:25 -07:00
2016-06-08 15:36:24 -07:00
/**
2016-06-10 11:15:59 -07:00
* Indicates that a FormControl is valid, i.e. that no errors exist in the input value.
2016-06-08 15:36:24 -07:00
*/
2016-06-08 16:38:52 -07:00
export const VALID = 'VALID' ;
2016-06-08 15:36:24 -07:00
/**
2016-06-10 11:15:59 -07:00
* Indicates that a FormControl is invalid, i.e. that an error exists in the input value.
2016-06-08 15:36:24 -07:00
*/
2016-06-08 16:38:52 -07:00
export const INVALID = 'INVALID' ;
2016-06-08 15:36:24 -07:00
/**
2016-06-10 11:15:59 -07:00
* Indicates that a FormControl is pending, i.e. that async validation is occurring and
2016-06-08 15:36:24 -07:00
* errors are not yet available for the input value.
*/
2016-06-08 16:38:52 -07:00
export const PENDING = 'PENDING' ;
2016-06-08 15:36:24 -07:00
2016-08-24 16:58:43 -07:00
/**
* Indicates that a FormControl is disabled, i.e. that the control is exempt from ancestor
* calculations of validity or value.
*/
export const DISABLED = 'DISABLED' ;
2016-08-01 14:22:50 -07:00
function _find ( control : AbstractControl , path : Array < string | number > | string , delimiter : string ) {
2016-10-21 15:14:44 -07:00
if ( path == null ) return null ;
2016-06-08 15:36:24 -07:00
if ( ! ( path instanceof Array ) ) {
2016-08-01 14:22:50 -07:00
path = ( < string > path ) . split ( delimiter ) ;
2016-06-08 15:36:24 -07:00
}
2016-10-19 19:55:50 +03:00
if ( path instanceof Array && ( path . length === 0 ) ) return null ;
2016-06-08 15:36:24 -07:00
2016-06-08 16:38:52 -07:00
return ( < Array < string | number > > path ) . reduce ( ( v , name ) = > {
2016-06-10 11:15:59 -07:00
if ( v instanceof FormGroup ) {
2016-10-07 18:11:37 -07:00
return v . controls [ name ] || null ;
2016-06-08 16:38:52 -07:00
}
2016-10-07 18:11:37 -07:00
if ( v instanceof FormArray ) {
return v . at ( < number > name ) || null ;
}
return null ;
2016-06-08 16:38:52 -07:00
} , control ) ;
2016-06-08 15:36:24 -07:00
}
function toObservable ( r : any ) : Observable < any > {
2016-08-30 12:13:38 -07:00
return isPromise ( r ) ? fromPromise ( r ) : r ;
2016-06-08 15:36:24 -07:00
}
2016-06-13 11:27:04 -07:00
function coerceToValidator ( validator : ValidatorFn | ValidatorFn [ ] ) : ValidatorFn {
return Array . isArray ( validator ) ? composeValidators ( validator ) : validator ;
}
function coerceToAsyncValidator ( asyncValidator : AsyncValidatorFn | AsyncValidatorFn [ ] ) :
AsyncValidatorFn {
return Array . isArray ( asyncValidator ) ? composeAsyncValidators ( asyncValidator ) : asyncValidator ;
}
2016-06-08 15:36:24 -07:00
/**
2016-09-12 20:31:13 -07:00
* @whatItDoes This is the base class for {@link FormControl}, {@link FormGroup}, and
* {@link FormArray}.
*
* It provides some of the shared behavior that all controls and groups of controls have, like
* running validators, calculating status, and resetting state. It also defines the properties
* that are shared between all sub-classes, like `value`, `valid`, and `dirty`. It shouldn't be
* instantiated directly.
*
2016-08-17 07:44:39 -07:00
* @stable
2016-06-08 15:36:24 -07:00
*/
export abstract class AbstractControl {
/** @internal */
_value : any ;
2016-09-02 15:57:35 -07:00
/** @internal */
_onCollectionChange = ( ) = > { } ;
2016-06-08 15:36:24 -07:00
private _valueChanges : EventEmitter < any > ;
private _statusChanges : EventEmitter < any > ;
private _status : string ;
private _errors : { [ key : string ] : any } ;
private _pristine : boolean = true ;
private _touched : boolean = false ;
2016-06-10 11:15:59 -07:00
private _parent : FormGroup | FormArray ;
2016-06-08 15:36:24 -07:00
private _asyncValidationSubscription : any ;
constructor ( public validator : ValidatorFn , public asyncValidator : AsyncValidatorFn ) { }
2016-09-12 20:31:13 -07:00
/**
* The value of the control.
*/
2016-06-08 15:36:24 -07:00
get value ( ) : any { return this . _value ; }
2016-10-19 19:55:50 +03:00
/**
* The parent control.
*/
get parent ( ) : FormGroup | FormArray { return this . _parent ; }
2016-09-12 20:31:13 -07:00
/**
* The validation status of the control. There are four possible
* validation statuses:
*
* * **VALID**: control has passed all validation checks
* * **INVALID**: control has failed at least one validation check
* * **PENDING**: control is in the midst of conducting a validation check
* * **DISABLED**: control is exempt from validation checks
*
* These statuses are mutually exclusive, so a control cannot be
* both valid AND invalid or invalid AND disabled.
*/
2016-06-08 15:36:24 -07:00
get status ( ) : string { return this . _status ; }
2016-09-12 20:31:13 -07:00
/**
* A control is `valid` when its `status === VALID`.
*
* In order to have this status, the control must have passed all its
* validation checks.
*/
2016-06-08 15:36:24 -07:00
get valid ( ) : boolean { return this . _status === VALID ; }
2016-09-12 20:31:13 -07:00
/**
* A control is `invalid` when its `status === INVALID`.
*
* In order to have this status, the control must have failed
* at least one of its validation checks.
*/
2016-08-01 18:41:25 -07:00
get invalid ( ) : boolean { return this . _status === INVALID ; }
2016-06-08 15:36:24 -07:00
/**
2016-09-12 20:31:13 -07:00
* A control is `pending` when its `status === PENDING`.
*
* In order to have this status, the control must be in the
* middle of conducting a validation check.
*/
get pending ( ) : boolean { return this . _status == PENDING ; }
/**
* A control is `disabled` when its `status === DISABLED`.
*
* Disabled controls are exempt from validation checks and
* are not included in the aggregate value of their ancestor
* controls.
*/
get disabled ( ) : boolean { return this . _status === DISABLED ; }
/**
* A control is `enabled` as long as its `status !== DISABLED`.
*
* In other words, it has a status of `VALID`, `INVALID`, or
* `PENDING`.
*/
get enabled ( ) : boolean { return this . _status !== DISABLED ; }
/**
* Returns any errors generated by failing validation. If there
* are no errors, it will return null.
2016-06-08 15:36:24 -07:00
*/
get errors ( ) : { [ key : string ] : any } { return this . _errors ; }
2016-09-12 20:31:13 -07:00
/**
* A control is `pristine` if the user has not yet changed
* the value in the UI.
*
* Note that programmatic changes to a control's value will
* *not* mark it dirty.
*/
2016-06-08 15:36:24 -07:00
get pristine ( ) : boolean { return this . _pristine ; }
2016-09-12 20:31:13 -07:00
/**
* A control is `dirty` if the user has changed the value
* in the UI.
*
* Note that programmatic changes to a control's value will
* *not* mark it dirty.
*/
2016-06-08 15:36:24 -07:00
get dirty ( ) : boolean { return ! this . pristine ; }
2016-09-12 20:31:13 -07:00
/**
* A control is marked `touched` once the user has triggered
* a `blur` event on it.
*/
2016-06-08 15:36:24 -07:00
get touched ( ) : boolean { return this . _touched ; }
2016-09-12 20:31:13 -07:00
/**
* A control is `untouched` if the user has not yet triggered
* a `blur` event on it.
*/
2016-06-08 15:36:24 -07:00
get untouched ( ) : boolean { return ! this . _touched ; }
2016-09-12 20:31:13 -07:00
/**
* Emits an event every time the value of the control changes, in
* the UI or programmatically.
*/
2016-06-08 15:36:24 -07:00
get valueChanges ( ) : Observable < any > { return this . _valueChanges ; }
2016-09-12 20:31:13 -07:00
/**
* Emits an event every time the validation status of the control
* is re-calculated.
*/
2016-06-08 15:36:24 -07:00
get statusChanges ( ) : Observable < any > { return this . _statusChanges ; }
2016-09-12 20:31:13 -07:00
/**
* Sets the synchronous validators that are active on this control. Calling
* this will overwrite any existing sync validators.
*/
setValidators ( newValidator : ValidatorFn | ValidatorFn [ ] ) : void {
this . validator = coerceToValidator ( newValidator ) ;
}
2016-08-24 16:58:43 -07:00
2016-09-12 20:31:13 -07:00
/**
* Sets the async validators that are active on this control. Calling this
* will overwrite any existing async validators.
*/
2016-06-23 11:18:07 -04:00
setAsyncValidators ( newValidator : AsyncValidatorFn | AsyncValidatorFn [ ] ) : void {
this . asyncValidator = coerceToAsyncValidator ( newValidator ) ;
}
2016-09-12 20:31:13 -07:00
/**
* Empties out the sync validator list.
*/
2016-06-23 11:18:07 -04:00
clearValidators ( ) : void { this . validator = null ; }
2016-09-12 20:31:13 -07:00
/**
* Empties out the async validator list.
*/
clearAsyncValidators ( ) : void { this . asyncValidator = null ; }
/**
* Marks the control as `touched`.
*
* This will also mark all direct ancestors as `touched` to maintain
* the model.
*/
2016-07-01 15:36:04 -07:00
markAsTouched ( { onlySelf } : { onlySelf? : boolean } = { } ) : void {
this . _touched = true ;
2016-10-21 15:14:44 -07:00
if ( this . _parent && ! onlySelf ) {
2016-10-19 18:54:54 +02:00
this . _parent . markAsTouched ( { onlySelf } ) ;
2016-07-01 15:36:04 -07:00
}
}
2016-06-08 15:36:24 -07:00
2016-09-12 20:31:13 -07:00
/**
* Marks the control as `untouched`.
*
* If the control has any children, it will also mark all children as `untouched`
* to maintain the model, and re-calculate the `touched` status of all parent
* controls.
*/
markAsUntouched ( { onlySelf } : { onlySelf? : boolean } = { } ) : void {
this . _touched = false ;
this . _forEachChild (
( control : AbstractControl ) = > { control . markAsUntouched ( { onlySelf : true } ) ; } ) ;
2016-10-21 15:14:44 -07:00
if ( this . _parent && ! onlySelf ) {
2016-10-19 18:54:54 +02:00
this . _parent . _updateTouched ( { onlySelf } ) ;
2016-09-12 20:31:13 -07:00
}
}
/**
* Marks the control as `dirty`.
*
* This will also mark all direct ancestors as `dirty` to maintain
* the model.
*/
2016-06-08 15:36:24 -07:00
markAsDirty ( { onlySelf } : { onlySelf? : boolean } = { } ) : void {
this . _pristine = false ;
2016-10-21 15:14:44 -07:00
if ( this . _parent && ! onlySelf ) {
2016-10-19 18:54:54 +02:00
this . _parent . markAsDirty ( { onlySelf } ) ;
2016-06-08 15:36:24 -07:00
}
}
2016-09-12 20:31:13 -07:00
/**
* Marks the control as `pristine`.
*
* If the control has any children, it will also mark all children as `pristine`
* to maintain the model, and re-calculate the `pristine` status of all parent
* controls.
*/
2016-07-12 15:02:25 -07:00
markAsPristine ( { onlySelf } : { onlySelf? : boolean } = { } ) : void {
this . _pristine = true ;
this . _forEachChild ( ( control : AbstractControl ) = > { control . markAsPristine ( { onlySelf : true } ) ; } ) ;
2016-10-21 15:14:44 -07:00
if ( this . _parent && ! onlySelf ) {
2016-10-19 18:54:54 +02:00
this . _parent . _updatePristine ( { onlySelf } ) ;
2016-07-12 15:02:25 -07:00
}
}
2016-09-12 20:31:13 -07:00
/**
* Marks the control as `pending`.
*/
2016-06-08 15:36:24 -07:00
markAsPending ( { onlySelf } : { onlySelf? : boolean } = { } ) : void {
this . _status = PENDING ;
2016-10-21 15:14:44 -07:00
if ( this . _parent && ! onlySelf ) {
2016-10-19 18:54:54 +02:00
this . _parent . markAsPending ( { onlySelf } ) ;
2016-06-08 15:36:24 -07:00
}
}
2016-09-12 20:31:13 -07:00
/**
* Disables the control. This means the control will be exempt from validation checks and
* excluded from the aggregate value of any parent. Its status is `DISABLED`.
*
* If the control has children, all children will be disabled to maintain the model.
*/
2016-08-24 16:58:43 -07:00
disable ( { onlySelf , emitEvent } : { onlySelf? : boolean , emitEvent? : boolean } = { } ) : void {
this . _status = DISABLED ;
2016-09-09 12:00:38 -07:00
this . _errors = null ;
2016-08-24 16:58:43 -07:00
this . _forEachChild ( ( control : AbstractControl ) = > { control . disable ( { onlySelf : true } ) ; } ) ;
this . _updateValue ( ) ;
2016-10-21 15:14:44 -07:00
if ( emitEvent !== false ) {
2016-08-24 16:58:43 -07:00
this . _valueChanges . emit ( this . _value ) ;
this . _statusChanges . emit ( this . _status ) ;
}
this . _updateAncestors ( onlySelf ) ;
2016-09-20 09:08:12 -07:00
this . _onDisabledChange . forEach ( ( changeFn ) = > changeFn ( true ) ) ;
2016-08-24 16:58:43 -07:00
}
2016-09-12 20:31:13 -07:00
/**
* Enables the control. This means the control will be included in validation checks and
* the aggregate value of its parent. Its status is re-calculated based on its value and
* its validators.
*
* If the control has children, all children will be enabled.
*/
2016-08-24 16:58:43 -07:00
enable ( { onlySelf , emitEvent } : { onlySelf? : boolean , emitEvent? : boolean } = { } ) : void {
this . _status = VALID ;
this . _forEachChild ( ( control : AbstractControl ) = > { control . enable ( { onlySelf : true } ) ; } ) ;
2016-10-19 18:54:54 +02:00
this . updateValueAndValidity ( { onlySelf : true , emitEvent } ) ;
2016-08-24 16:58:43 -07:00
this . _updateAncestors ( onlySelf ) ;
2016-09-20 09:08:12 -07:00
this . _onDisabledChange . forEach ( ( changeFn ) = > changeFn ( false ) ) ;
2016-08-24 16:58:43 -07:00
}
private _updateAncestors ( onlySelf : boolean ) {
2016-10-21 15:14:44 -07:00
if ( this . _parent && ! onlySelf ) {
2016-08-24 16:58:43 -07:00
this . _parent . updateValueAndValidity ( ) ;
this . _parent . _updatePristine ( ) ;
this . _parent . _updateTouched ( ) ;
}
}
2016-06-10 11:15:59 -07:00
setParent ( parent : FormGroup | FormArray ) : void { this . _parent = parent ; }
2016-06-08 15:36:24 -07:00
2016-09-12 20:31:13 -07:00
/**
* Sets the value of the control. Abstract method (implemented in sub-classes).
*/
2016-08-05 13:35:17 -07:00
abstract setValue ( value : any , options? : Object ) : void ;
2016-09-12 20:31:13 -07:00
/**
* Patches the value of the control. Abstract method (implemented in sub-classes).
*/
2016-08-05 13:35:17 -07:00
abstract patchValue ( value : any , options? : Object ) : void ;
2016-07-08 13:04:25 -07:00
2016-09-12 20:31:13 -07:00
/**
* Resets the control. Abstract method (implemented in sub-classes).
*/
2016-07-12 15:02:25 -07:00
abstract reset ( value? : any , options? : Object ) : void ;
2016-09-12 20:31:13 -07:00
/**
* Re-calculates the value and validation status of the control.
*
* By default, it will also update the value and validity of its ancestors.
*/
2016-06-08 16:38:52 -07:00
updateValueAndValidity ( { onlySelf , emitEvent } : { onlySelf? : boolean , emitEvent? : boolean } = { } ) :
void {
2016-09-09 12:00:38 -07:00
this . _setInitialStatus ( ) ;
2016-06-08 15:36:24 -07:00
this . _updateValue ( ) ;
2016-09-09 12:00:38 -07:00
if ( this . enabled ) {
2016-12-04 01:17:09 +03:00
this . _cancelExistingSubscription ( ) ;
2016-09-09 12:00:38 -07:00
this . _errors = this . _runValidator ( ) ;
this . _status = this . _calculateStatus ( ) ;
2016-06-08 15:36:24 -07:00
2016-09-09 12:00:38 -07:00
if ( this . _status === VALID || this . _status === PENDING ) {
this . _runAsyncValidator ( emitEvent ) ;
}
2016-08-24 16:58:43 -07:00
}
2016-10-21 15:14:44 -07:00
if ( emitEvent !== false ) {
2016-08-02 15:53:34 -07:00
this . _valueChanges . emit ( this . _value ) ;
this . _statusChanges . emit ( this . _status ) ;
2016-06-08 15:36:24 -07:00
}
2016-10-21 15:14:44 -07:00
if ( this . _parent && ! onlySelf ) {
2016-10-19 18:54:54 +02:00
this . _parent . updateValueAndValidity ( { onlySelf , emitEvent } ) ;
2016-06-08 15:36:24 -07:00
}
}
2016-08-29 11:33:49 -07:00
/** @internal */
_updateTreeValidity ( { emitEvent } : { emitEvent? : boolean } = { emitEvent : true } ) {
this . _forEachChild ( ( ctrl : AbstractControl ) = > ctrl . _updateTreeValidity ( { emitEvent } ) ) ;
this . updateValueAndValidity ( { onlySelf : true , emitEvent } ) ;
}
2016-09-09 12:00:38 -07:00
private _setInitialStatus() { this . _status = this . _allControlsDisabled ( ) ? DISABLED : VALID ; }
2016-06-08 15:36:24 -07:00
private _runValidator ( ) : { [ key : string ] : any } {
2016-10-21 15:14:44 -07:00
return this . validator ? this . validator ( this ) : null ;
2016-06-08 15:36:24 -07:00
}
private _runAsyncValidator ( emitEvent : boolean ) : void {
2016-10-21 15:14:44 -07:00
if ( this . asyncValidator ) {
2016-06-08 15:36:24 -07:00
this . _status = PENDING ;
2016-10-21 15:14:44 -07:00
const obs = toObservable ( this . asyncValidator ( this ) ) ;
2017-01-22 21:37:22 +13:00
if ( ! ( obs instanceof Observable ) ) {
throw new Error (
` expected the following validator to return Promise or Observable: ${ this . asyncValidator } . If you are using FormBuilder; did you forget to brace your validators in an array? ` ) ;
}
2016-10-19 18:54:54 +02:00
this . _asyncValidationSubscription =
obs . subscribe ( { next : ( res : { [ key : string ] : any } ) = > this . setErrors ( res , { emitEvent } ) } ) ;
2016-06-08 15:36:24 -07:00
}
}
private _cancelExistingSubscription ( ) : void {
2016-10-21 15:14:44 -07:00
if ( this . _asyncValidationSubscription ) {
2016-08-02 15:53:34 -07:00
this . _asyncValidationSubscription . unsubscribe ( ) ;
2016-06-08 15:36:24 -07:00
}
}
/**
2016-06-10 11:15:59 -07:00
* Sets errors on a form control.
2016-06-08 15:36:24 -07:00
*
2016-09-12 20:31:13 -07:00
* This is used when validations are run manually by the user, rather than automatically.
2016-06-08 15:36:24 -07:00
*
* Calling `setErrors` will also update the validity of the parent control.
*
2016-09-12 20:31:13 -07:00
* ### Example
2016-06-08 15:36:24 -07:00
*
* ```
2016-09-12 20:31:13 -07:00
* const login = new FormControl("someLogin");
2016-06-08 15:36:24 -07:00
* login.setErrors({
* "notUnique": true
* });
*
* expect(login.valid).toEqual(false);
* expect(login.errors).toEqual({"notUnique": true});
*
2016-09-12 20:31:13 -07:00
* login.setValue("someOtherLogin");
2016-06-08 15:36:24 -07:00
*
* expect(login.valid).toEqual(true);
* ```
*/
setErrors ( errors : { [ key : string ] : any } , { emitEvent } : { emitEvent? : boolean } = { } ) : void {
this . _errors = errors ;
2016-10-21 15:14:44 -07:00
this . _updateControlsErrors ( emitEvent !== false ) ;
2016-06-08 15:36:24 -07:00
}
2016-09-12 20:31:13 -07:00
/**
* Retrieves a child control given the control's name or path.
*
* Paths can be passed in as an array or a string delimited by a dot.
*
* To get a control nested within a `person` sub-group:
*
* * `this.form.get('person.name');`
*
* -OR-
*
* * `this.form.get(['person', 'name']);`
*/
2016-08-01 14:22:50 -07:00
get ( path : Array < string | number > | string ) : AbstractControl { return _find ( this , path , '.' ) ; }
2016-06-08 15:36:24 -07:00
2016-09-12 20:31:13 -07:00
/**
* Returns true if the control with the given path has the error specified. Otherwise
* returns null or undefined.
*
* If no path is given, it checks for the error on the present control.
*/
2016-06-08 15:36:24 -07:00
getError ( errorCode : string , path : string [ ] = null ) : any {
2016-10-21 15:14:44 -07:00
const control = path ? this . get ( path ) : this ;
return control && control . _errors ? control . _errors [ errorCode ] : null ;
2016-06-08 15:36:24 -07:00
}
2016-09-12 20:31:13 -07:00
/**
* Returns true if the control with the given path has the error specified. Otherwise
* returns false.
*
* If no path is given, it checks for the error on the present control.
*/
2016-06-08 15:36:24 -07:00
hasError ( errorCode : string , path : string [ ] = null ) : boolean {
2016-10-21 15:14:44 -07:00
return ! ! this . getError ( errorCode , path ) ;
2016-06-08 15:36:24 -07:00
}
2016-09-12 20:31:13 -07:00
/**
* Retrieves the top-level ancestor of this control.
*/
2016-06-08 15:36:24 -07:00
get root ( ) : AbstractControl {
let x : AbstractControl = this ;
2016-10-21 15:14:44 -07:00
while ( x . _parent ) {
2016-06-08 15:36:24 -07:00
x = x . _parent ;
}
return x ;
}
/** @internal */
2016-06-27 21:01:24 -06:00
_updateControlsErrors ( emitEvent : boolean ) : void {
2016-06-08 15:36:24 -07:00
this . _status = this . _calculateStatus ( ) ;
2016-06-27 21:01:24 -06:00
if ( emitEvent ) {
2016-08-02 15:53:34 -07:00
this . _statusChanges . emit ( this . _status ) ;
2016-06-27 21:01:24 -06:00
}
2016-10-21 15:14:44 -07:00
if ( this . _parent ) {
2016-06-27 21:01:24 -06:00
this . _parent . _updateControlsErrors ( emitEvent ) ;
2016-06-08 15:36:24 -07:00
}
}
/** @internal */
_initObservables() {
this . _valueChanges = new EventEmitter ( ) ;
this . _statusChanges = new EventEmitter ( ) ;
}
private _calculateStatus ( ) : string {
2016-09-01 16:51:42 -07:00
if ( this . _allControlsDisabled ( ) ) return DISABLED ;
2016-10-21 15:14:44 -07:00
if ( this . _errors ) return INVALID ;
2016-06-08 15:36:24 -07:00
if ( this . _anyControlsHaveStatus ( PENDING ) ) return PENDING ;
if ( this . _anyControlsHaveStatus ( INVALID ) ) return INVALID ;
return VALID ;
}
/** @internal */
abstract _updateValue ( ) : void ;
/** @internal */
2016-07-12 15:02:25 -07:00
abstract _forEachChild ( cb : Function ) : void ;
/** @internal */
abstract _anyControls ( condition : Function ) : boolean ;
2016-08-24 16:58:43 -07:00
/** @internal */
abstract _allControlsDisabled ( ) : boolean ;
2016-07-12 15:02:25 -07:00
/** @internal */
_anyControlsHaveStatus ( status : string ) : boolean {
2016-10-19 19:55:50 +03:00
return this . _anyControls ( ( control : AbstractControl ) = > control . status === status ) ;
2016-07-12 15:02:25 -07:00
}
/** @internal */
_anyControlsDirty ( ) : boolean {
return this . _anyControls ( ( control : AbstractControl ) = > control . dirty ) ;
}
/** @internal */
_anyControlsTouched ( ) : boolean {
return this . _anyControls ( ( control : AbstractControl ) = > control . touched ) ;
}
/** @internal */
_updatePristine ( { onlySelf } : { onlySelf? : boolean } = { } ) : void {
this . _pristine = ! this . _anyControlsDirty ( ) ;
2016-10-21 15:14:44 -07:00
if ( this . _parent && ! onlySelf ) {
2016-10-19 18:54:54 +02:00
this . _parent . _updatePristine ( { onlySelf } ) ;
2016-07-12 15:02:25 -07:00
}
}
/** @internal */
_updateTouched ( { onlySelf } : { onlySelf? : boolean } = { } ) : void {
this . _touched = this . _anyControlsTouched ( ) ;
2016-10-21 15:14:44 -07:00
if ( this . _parent && ! onlySelf ) {
2016-10-19 18:54:54 +02:00
this . _parent . _updateTouched ( { onlySelf } ) ;
2016-07-12 15:02:25 -07:00
}
}
2016-08-24 16:58:43 -07:00
/** @internal */
2016-09-20 09:08:12 -07:00
_onDisabledChange : Function [ ] = [ ] ;
2016-08-24 16:58:43 -07:00
/** @internal */
_isBoxedValue ( formState : any ) : boolean {
2016-10-19 13:42:39 -07:00
return typeof formState === 'object' && formState !== null &&
Object . keys ( formState ) . length === 2 && 'value' in formState && 'disabled' in formState ;
2016-08-24 16:58:43 -07:00
}
2016-09-02 15:57:35 -07:00
/** @internal */
_registerOnCollectionChange ( fn : ( ) = > void ) : void { this . _onCollectionChange = fn ; }
2016-06-08 15:36:24 -07:00
}
/**
2016-09-13 00:14:07 -07:00
* @whatItDoes Tracks the value and validation status of an individual form control.
2016-06-08 15:36:24 -07:00
*
2016-09-13 00:14:07 -07:00
* It is one of the three fundamental building blocks of Angular forms, along with
* {@link FormGroup} and {@link FormArray}.
2016-06-08 15:36:24 -07:00
*
2016-09-13 00:14:07 -07:00
* @howToUse
2016-06-08 15:36:24 -07:00
*
2016-09-13 00:14:07 -07:00
* When instantiating a {@link FormControl}, you can pass in an initial value as the
* first argument. Example:
*
* ```ts
* const ctrl = new FormControl('some value');
* console.log(ctrl.value); // 'some value'
*```
*
* You can also initialize the control with a form state object on instantiation,
* which includes both the value and whether or not the control is disabled.
2016-09-28 22:59:08 +02:00
* You can't use the value key without the disabled key; both are required
* to use this way of initialization.
2016-09-13 00:14:07 -07:00
*
* ```ts
* const ctrl = new FormControl({value: 'n/a', disabled: true});
* console.log(ctrl.value); // 'n/a'
* console.log(ctrl.status); // 'DISABLED'
* ```
*
* To include a sync validator (or an array of sync validators) with the control,
* pass it in as the second argument. Async validators are also supported, but
* have to be passed in separately as the third arg.
*
* ```ts
* const ctrl = new FormControl('', Validators.required);
* console.log(ctrl.value); // ''
* console.log(ctrl.status); // 'INVALID'
* ```
*
* See its superclass, {@link AbstractControl}, for more properties and methods.
*
* * **npm package**: `@angular/forms`
2016-06-08 15:36:24 -07:00
*
2016-08-17 07:44:39 -07:00
* @stable
2016-06-08 15:36:24 -07:00
*/
2016-06-10 11:15:59 -07:00
export class FormControl extends AbstractControl {
2016-06-08 15:36:24 -07:00
/** @internal */
2016-06-15 15:15:41 -07:00
_onChange : Function [ ] = [ ] ;
2016-06-08 15:36:24 -07:00
2016-06-08 16:38:52 -07:00
constructor (
2016-08-24 16:58:43 -07:00
formState : any = null , validator : ValidatorFn | ValidatorFn [ ] = null ,
2016-06-13 11:27:04 -07:00
asyncValidator : AsyncValidatorFn | AsyncValidatorFn [ ] = null ) {
super ( coerceToValidator ( validator ) , coerceToAsyncValidator ( asyncValidator ) ) ;
2016-08-24 16:58:43 -07:00
this . _applyFormState ( formState ) ;
2016-06-08 15:36:24 -07:00
this . updateValueAndValidity ( { onlySelf : true , emitEvent : false } ) ;
this . _initObservables ( ) ;
}
/**
2016-06-10 11:15:59 -07:00
* Set the value of the form control to `value`.
2016-06-08 15:36:24 -07:00
*
2016-06-10 11:15:59 -07:00
* If `onlySelf` is `true`, this change will only affect the validation of this `FormControl`
2016-09-13 11:43:08 -07:00
* and not its parent component. This defaults to false.
*
* If `emitEvent` is `true`, this
2016-09-13 00:14:07 -07:00
* change will cause a `valueChanges` event on the `FormControl` to be emitted. This defaults
* to true (as it falls through to `updateValueAndValidity`).
2016-06-08 15:36:24 -07:00
*
* If `emitModelToViewChange` is `true`, the view will be notified about the new value
* via an `onChange` event. This is the default behavior if `emitModelToViewChange` is not
* specified.
2016-07-12 15:02:25 -07:00
*
* If `emitViewToModelChange` is `true`, an ngModelChange event will be fired to update the
* model. This is the default behavior if `emitViewToModelChange` is not specified.
2016-06-08 15:36:24 -07:00
*/
2016-08-05 13:35:17 -07:00
setValue ( value : any , { onlySelf , emitEvent , emitModelToViewChange , emitViewToModelChange } : {
2016-06-08 15:36:24 -07:00
onlySelf? : boolean ,
emitEvent? : boolean ,
2016-07-12 15:02:25 -07:00
emitModelToViewChange? : boolean ,
emitViewToModelChange? : boolean
2016-06-08 15:36:24 -07:00
} = { } ) : void {
this . _value = value ;
2016-10-21 15:14:44 -07:00
if ( this . _onChange . length && emitModelToViewChange !== false ) {
this . _onChange . forEach ( ( changeFn ) = > changeFn ( this . _value , emitViewToModelChange !== false ) ) ;
2016-06-15 15:15:41 -07:00
}
2016-10-19 18:54:54 +02:00
this . updateValueAndValidity ( { onlySelf , emitEvent } ) ;
2016-06-08 15:36:24 -07:00
}
2016-08-05 13:35:17 -07:00
/**
2016-09-13 00:14:07 -07:00
* Patches the value of a control.
*
* This function is functionally the same as {@link FormControl.setValue} at this level.
* It exists for symmetry with {@link FormGroup.patchValue} on `FormGroups` and `FormArrays`,
* where it does behave differently.
2016-08-05 13:35:17 -07:00
*/
patchValue ( value : any , options : {
onlySelf? : boolean ,
emitEvent? : boolean ,
emitModelToViewChange? : boolean ,
emitViewToModelChange? : boolean
} = { } ) : void {
this . setValue ( value , options ) ;
}
2016-09-13 00:14:07 -07:00
/**
* Resets the form control. This means by default:
*
* * it is marked as `pristine`
* * it is marked as `untouched`
* * value is set to null
*
* You can also reset to a specific form state by passing through a standalone
* 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).
*
* Ex:
*
* ```ts
* this.control.reset('Nancy');
*
* console.log(this.control.value); // 'Nancy'
* ```
*
* OR
*
* ```
* this.control.reset({value: 'Nancy', disabled: true});
2016-09-13 11:43:08 -07:00
*
2016-09-13 00:14:07 -07:00
* console.log(this.control.value); // 'Nancy'
* console.log(this.control.status); // 'DISABLED'
* ```
*/
2016-10-19 18:54:54 +02:00
reset ( formState : any = null , { onlySelf , emitEvent } : { onlySelf? : boolean ,
emitEvent? : boolean } = { } ) : void {
2016-08-24 16:58:43 -07:00
this . _applyFormState ( formState ) ;
this . markAsPristine ( { onlySelf } ) ;
this . markAsUntouched ( { onlySelf } ) ;
2016-10-19 18:54:54 +02:00
this . setValue ( this . _value , { onlySelf , emitEvent } ) ;
2016-07-12 15:02:25 -07:00
}
2016-06-08 15:36:24 -07:00
/**
* @internal
*/
_updateValue() { }
/**
* @internal
*/
2016-07-12 15:02:25 -07:00
_anyControls ( condition : Function ) : boolean { return false ; }
2016-06-08 15:36:24 -07:00
2016-08-24 16:58:43 -07:00
/**
* @internal
*/
_allControlsDisabled ( ) : boolean { return this . disabled ; }
2016-06-08 15:36:24 -07:00
/**
* Register a listener for change events.
*/
2016-06-15 15:15:41 -07:00
registerOnChange ( fn : Function ) : void { this . _onChange . push ( fn ) ; }
2016-07-12 15:02:25 -07:00
2016-08-25 14:37:57 -07:00
/**
* @internal
*/
_clearChangeFns ( ) : void {
this . _onChange = [ ] ;
2016-09-20 09:08:12 -07:00
this . _onDisabledChange = [ ] ;
2016-09-02 15:57:35 -07:00
this . _onCollectionChange = ( ) = > { } ;
2016-08-25 14:37:57 -07:00
}
2016-08-24 16:58:43 -07:00
/**
* Register a listener for disabled events.
*/
2016-09-20 09:08:12 -07:00
registerOnDisabledChange ( fn : ( isDisabled : boolean ) = > void ) : void {
this . _onDisabledChange . push ( fn ) ;
}
2016-08-24 16:58:43 -07:00
2016-07-12 15:02:25 -07:00
/**
* @internal
*/
_forEachChild ( cb : Function ) : void { }
2016-08-24 16:58:43 -07:00
private _applyFormState ( formState : any ) {
if ( this . _isBoxedValue ( formState ) ) {
this . _value = formState . value ;
formState . disabled ? this . disable ( { onlySelf : true , emitEvent : false } ) :
this . enable ( { onlySelf : true , emitEvent : false } ) ;
} else {
this . _value = formState ;
}
}
2016-06-08 15:36:24 -07:00
}
/**
2016-09-13 11:43:08 -07:00
* @whatItDoes Tracks the value and validity state of a group of {@link FormControl}
* instances.
2016-06-08 15:36:24 -07:00
*
2016-09-13 11:43:08 -07:00
* A `FormGroup` aggregates the values of each child {@link FormControl} into one object,
* with each control name as the key. It calculates its status by reducing the statuses
* of its children. For example, if one of the controls in a group is invalid, the entire
* group becomes invalid.
2016-06-08 15:36:24 -07:00
*
2016-06-10 11:15:59 -07:00
* `FormGroup` is one of the three fundamental building blocks used to define forms in Angular,
2016-09-13 11:43:08 -07:00
* along with {@link FormControl} and {@link FormArray}.
*
* @howToUse
*
* When instantiating a {@link FormGroup}, pass in a collection of child controls as the first
* argument. The key for each child will be the name under which it is registered.
*
* ### Example
*
* ```
* 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'
* ```
*
* You can also include group-level validators as the second arg, or group-level async
* 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.
*
* ### Example
*
* ```
* const form = new FormGroup({
* password: new FormControl('', Validators.minLength(2)),
* passwordConfirm: new FormControl('', Validators.minLength(2)),
* }, passwordMatchValidator);
2016-06-08 15:36:24 -07:00
*
*
2016-09-13 11:43:08 -07:00
* function passwordMatchValidator(g: FormGroup) {
* return g.get('password').value === g.get('passwordConfirm').value
* ? null : {'mismatch': true};
* }
* ```
*
* * **npm package**: `@angular/forms`
*
2016-08-17 07:44:39 -07:00
* @stable
2016-06-08 15:36:24 -07:00
*/
2016-06-10 11:15:59 -07:00
export class FormGroup extends AbstractControl {
2016-06-08 16:38:52 -07:00
constructor (
2016-08-24 16:58:43 -07:00
public controls : { [ key : string ] : AbstractControl } , validator : ValidatorFn = null ,
2016-08-17 07:44:39 -07:00
asyncValidator : AsyncValidatorFn = null ) {
2016-06-08 15:36:24 -07:00
super ( validator , asyncValidator ) ;
this . _initObservables ( ) ;
2016-09-02 15:57:35 -07:00
this . _setUpControls ( ) ;
2016-06-08 15:36:24 -07:00
this . updateValueAndValidity ( { onlySelf : true , emitEvent : false } ) ;
}
/**
2016-09-13 11:43:08 -07:00
* Registers a control with the group's list of controls.
*
* This method does not update value or validity of the control, so for
* most cases you'll want to use {@link FormGroup.addControl} instead.
2016-06-08 15:36:24 -07:00
*/
2016-06-15 15:15:41 -07:00
registerControl ( name : string , control : AbstractControl ) : AbstractControl {
if ( this . controls [ name ] ) return this . controls [ name ] ;
2016-06-08 15:36:24 -07:00
this . controls [ name ] = control ;
control . setParent ( this ) ;
2016-09-02 15:57:35 -07:00
control . _registerOnCollectionChange ( this . _onCollectionChange ) ;
2016-06-15 15:15:41 -07:00
return control ;
2016-06-08 15:36:24 -07:00
}
/**
* Add a control to this group.
*/
addControl ( name : string , control : AbstractControl ) : void {
this . registerControl ( name , control ) ;
this . updateValueAndValidity ( ) ;
2016-09-02 15:57:35 -07:00
this . _onCollectionChange ( ) ;
2016-06-08 15:36:24 -07:00
}
/**
* Remove a control from this group.
*/
removeControl ( name : string ) : void {
2016-09-02 15:57:35 -07:00
if ( this . controls [ name ] ) this . controls [ name ] . _registerOnCollectionChange ( ( ) = > { } ) ;
2016-09-19 17:15:57 -07:00
delete ( this . controls [ name ] ) ;
2016-09-02 15:57:35 -07:00
this . updateValueAndValidity ( ) ;
this . _onCollectionChange ( ) ;
}
/**
* Replace an existing control.
*/
setControl ( name : string , control : AbstractControl ) : void {
if ( this . controls [ name ] ) this . controls [ name ] . _registerOnCollectionChange ( ( ) = > { } ) ;
2016-09-19 17:15:57 -07:00
delete ( this . controls [ name ] ) ;
2016-09-02 15:57:35 -07:00
if ( control ) this . registerControl ( name , control ) ;
2016-06-08 15:36:24 -07:00
this . updateValueAndValidity ( ) ;
2016-09-02 15:57:35 -07:00
this . _onCollectionChange ( ) ;
2016-06-08 15:36:24 -07:00
}
/**
2016-09-13 11:43:08 -07:00
* Check whether there is an enabled control with the given name in the group.
*
* It will return false for disabled controls. If you'd like to check for
* existence in the group only, use {@link AbstractControl.get} instead.
2016-06-08 15:36:24 -07:00
*/
contains ( controlName : string ) : boolean {
2016-09-12 15:15:50 -07:00
return this . controls . hasOwnProperty ( controlName ) && this . controls [ controlName ] . enabled ;
2016-06-08 15:36:24 -07:00
}
2016-09-13 11:43:08 -07:00
/**
* Sets the value of the {@link FormGroup}. It accepts an object that matches
* the structure of the group, with control names as keys.
*
* This method performs strict checks, so it will throw an error if you try
* to set the value of a control that doesn't exist or if you exclude the
* value of a control.
*
* ### Example
*
* ```
* const form = new FormGroup({
* first: new FormControl(),
* last: new FormControl()
* });
* console.log(form.value); // {first: null, last: null}
*
* form.setValue({first: 'Nancy', last: 'Drew'});
* console.log(form.value); // {first: 'Nancy', last: 'Drew'}
*
* ```
*/
2016-10-19 18:54:54 +02:00
setValue (
value : { [ key : string ] : any } ,
{ onlySelf , emitEvent } : { onlySelf? : boolean , emitEvent? : boolean } = { } ) : void {
2016-08-05 13:35:17 -07:00
this . _checkAllValuesPresent ( value ) ;
2016-10-03 16:46:05 -07:00
Object . keys ( value ) . forEach ( name = > {
2016-07-08 13:04:25 -07:00
this . _throwIfControlMissing ( name ) ;
2016-10-19 18:54:54 +02:00
this . controls [ name ] . setValue ( value [ name ] , { onlySelf : true , emitEvent } ) ;
2016-08-05 13:35:17 -07:00
} ) ;
2016-10-19 18:54:54 +02:00
this . updateValueAndValidity ( { onlySelf , emitEvent } ) ;
2016-08-05 13:35:17 -07:00
}
2016-09-13 11:43:08 -07:00
/**
* Patches the value of the {@link FormGroup}. It accepts an object with control
* names as keys, and will do its best to match the values to the correct controls
* in the group.
*
* It accepts both super-sets and sub-sets of the group without throwing an error.
*
* ### Example
*
* ```
* const form = new FormGroup({
* first: new FormControl(),
* last: new FormControl()
* });
* console.log(form.value); // {first: null, last: null}
*
* form.patchValue({first: 'Nancy'});
* console.log(form.value); // {first: 'Nancy', last: null}
*
* ```
*/
2016-10-19 18:54:54 +02:00
patchValue (
value : { [ key : string ] : any } ,
{ onlySelf , emitEvent } : { onlySelf? : boolean , emitEvent? : boolean } = { } ) : void {
2016-10-03 16:46:05 -07:00
Object . keys ( value ) . forEach ( name = > {
2016-08-05 13:35:17 -07:00
if ( this . controls [ name ] ) {
2016-10-19 18:54:54 +02:00
this . controls [ name ] . patchValue ( value [ name ] , { onlySelf : true , emitEvent } ) ;
2016-08-05 13:35:17 -07:00
}
2016-07-08 13:04:25 -07:00
} ) ;
2016-10-19 18:54:54 +02:00
this . updateValueAndValidity ( { onlySelf , emitEvent } ) ;
2016-07-08 13:04:25 -07:00
}
2016-09-13 11:43:08 -07:00
/**
* Resets the {@link FormGroup}. This means by default:
*
* * The group and all descendants are marked `pristine`
* * The group and all descendants are marked `untouched`
* * The value of all descendants will be null or null maps
*
* You can also reset to a specific form state by passing in a map of states
* that matches the structure of your form, with control names as keys. The state
* can be a standalone value or a form state object with both a value and a disabled
* status.
*
* ### Example
*
* ```ts
2016-10-25 15:44:07 -07:00
* this.form.reset({first: 'name', last: 'last name'});
2016-09-13 11:43:08 -07:00
*
* console.log(this.form.value); // {first: 'name', last: 'last name'}
* ```
*
* - OR -
*
* ```
* this.form.reset({
* 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'
* ```
*/
2016-10-19 18:54:54 +02:00
reset ( value : any = { } , { onlySelf , emitEvent } : { onlySelf? : boolean , emitEvent? : boolean } = { } ) :
void {
2016-07-12 15:02:25 -07:00
this . _forEachChild ( ( control : AbstractControl , name : string ) = > {
2016-10-19 18:54:54 +02:00
control . reset ( value [ name ] , { onlySelf : true , emitEvent } ) ;
2016-07-12 15:02:25 -07:00
} ) ;
2016-10-19 18:54:54 +02:00
this . updateValueAndValidity ( { onlySelf , emitEvent } ) ;
this . _updatePristine ( { onlySelf } ) ;
this . _updateTouched ( { onlySelf } ) ;
2016-07-12 15:02:25 -07:00
}
2016-09-13 11:43:08 -07:00
/**
2016-09-13 13:07:40 -07:00
* The aggregate value of the {@link FormGroup}, including any disabled controls.
2016-09-13 11:43:08 -07:00
*
* If you'd like to include all values regardless of disabled status, use this method.
2016-09-13 13:07:40 -07:00
* Otherwise, the `value` property is the best way to get the value of the group.
2016-09-13 11:43:08 -07:00
*/
2016-11-09 02:44:36 +03:00
getRawValue ( ) : any {
2016-08-24 16:58:43 -07:00
return this . _reduceChildren (
{ } , ( acc : { [ k : string ] : AbstractControl } , control : AbstractControl , name : string ) = > {
acc [ name ] = control . value ;
return acc ;
} ) ;
}
2016-07-08 13:04:25 -07:00
/** @internal */
_throwIfControlMissing ( name : string ) : void {
2016-08-05 13:35:17 -07:00
if ( ! Object . keys ( this . controls ) . length ) {
2016-08-25 00:50:16 -07:00
throw new Error ( `
2016-08-05 13:35:17 -07: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 13:04:25 -07:00
if ( ! this . controls [ name ] ) {
2016-08-25 00:50:16 -07:00
throw new Error ( ` Cannot find form control with name: ${ name } . ` ) ;
2016-07-08 13:04:25 -07:00
}
}
2016-07-12 15:02:25 -07:00
/** @internal */
2016-07-15 16:26:19 -07:00
_forEachChild ( cb : ( v : any , k : string ) = > void ) : void {
2016-10-03 16:46:05 -07:00
Object . keys ( this . controls ) . forEach ( k = > cb ( this . controls [ k ] , k ) ) ;
2016-07-15 16:26:19 -07:00
}
2016-07-12 15:02:25 -07:00
2016-06-08 15:36:24 -07:00
/** @internal */
2016-10-19 19:55:50 +03:00
_setUpControls ( ) : void {
2016-09-02 15:57:35 -07:00
this . _forEachChild ( ( control : AbstractControl ) = > {
control . setParent ( this ) ;
control . _registerOnCollectionChange ( this . _onCollectionChange ) ;
} ) ;
2016-06-08 15:36:24 -07:00
}
/** @internal */
2016-10-19 19:55:50 +03:00
_updateValue ( ) : void { this . _value = this . _reduceValue ( ) ; }
2016-06-08 15:36:24 -07:00
/** @internal */
2016-07-12 15:02:25 -07:00
_anyControls ( condition : Function ) : boolean {
2016-10-19 19:55:50 +03:00
let res = false ;
2016-07-12 15:02:25 -07:00
this . _forEachChild ( ( control : AbstractControl , name : string ) = > {
res = res || ( this . contains ( name ) && condition ( control ) ) ;
2016-06-08 15:36:24 -07:00
} ) ;
return res ;
}
/** @internal */
_reduceValue() {
return this . _reduceChildren (
{ } , ( acc : { [ k : string ] : AbstractControl } , control : AbstractControl , name : string ) = > {
2016-08-24 16:58:43 -07:00
if ( control . enabled || this . disabled ) {
acc [ name ] = control . value ;
}
2016-06-08 15:36:24 -07:00
return acc ;
} ) ;
}
/** @internal */
_reduceChildren ( initValue : any , fn : Function ) {
2016-10-19 19:55:50 +03:00
let res = initValue ;
2016-08-24 16:58:43 -07:00
this . _forEachChild (
( control : AbstractControl , name : string ) = > { res = fn ( res , control , name ) ; } ) ;
2016-06-08 15:36:24 -07:00
return res ;
}
/** @internal */
2016-08-24 16:58:43 -07:00
_allControlsDisabled ( ) : boolean {
2016-11-12 14:08:58 +01:00
for ( const controlName of Object . keys ( this . controls ) ) {
2016-08-24 16:58:43 -07:00
if ( this . controls [ controlName ] . enabled ) {
return false ;
}
}
2016-09-08 12:21:48 -07:00
return Object . keys ( this . controls ) . length > 0 || this . disabled ;
2016-06-08 15:36:24 -07:00
}
2016-08-05 13:35:17 -07:00
/** @internal */
_checkAllValuesPresent ( value : any ) : void {
this . _forEachChild ( ( control : AbstractControl , name : string ) = > {
if ( value [ name ] === undefined ) {
2016-08-25 00:50:16 -07:00
throw new Error ( ` Must supply a value for form control with name: ' ${ name } '. ` ) ;
2016-08-05 13:35:17 -07:00
}
} ) ;
}
2016-06-08 15:36:24 -07:00
}
/**
2017-01-25 10:52:41 +03:00
* @whatItDoes Tracks the value and validity state of an array of {@link FormControl},
* {@link FormGroup} or {@link FormArray} instances.
2016-06-08 15:36:24 -07:00
*
2016-09-13 13:07:40 -07:00
* A `FormArray` aggregates the values of each child {@link FormControl} into an array.
* It calculates its status by reducing the statuses of its children. For example, if one of
* the controls in a `FormArray` is invalid, the entire array becomes invalid.
2016-06-08 15:36:24 -07:00
*
2016-06-10 11:15:59 -07:00
* `FormArray` is one of the three fundamental building blocks used to define forms in Angular,
2016-09-13 13:07:40 -07:00
* along with {@link FormControl} and {@link FormGroup}.
*
* @howToUse
*
* When instantiating a {@link FormArray}, pass in an array of child controls as the first
* argument.
*
* ### Example
*
* ```
* const arr = new FormArray([
* new FormControl('Nancy', Validators.minLength(2)),
* new FormControl('Drew'),
* ]);
*
* console.log(arr.value); // ['Nancy', 'Drew']
* console.log(arr.status); // 'VALID'
* ```
*
* You can also include array-level validators as the second arg, or array-level async
* 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.
2016-06-08 15:36:24 -07:00
*
2016-09-13 13:07:40 -07:00
* ### Adding or removing controls
2016-06-08 15:36:24 -07:00
*
* To change the controls in the array, use the `push`, `insert`, or `removeAt` methods
2016-06-10 11:15:59 -07:00
* in `FormArray` itself. These methods ensure the controls are properly tracked in the
2016-06-08 15:36:24 -07:00
* form's hierarchy. Do not modify the array of `AbstractControl`s used to instantiate
2016-06-10 11:15:59 -07:00
* the `FormArray` directly, as that will result in strange and unexpected behavior such
2016-06-08 15:36:24 -07:00
* as broken change detection.
*
2016-09-13 13:07:40 -07:00
* * **npm package**: `@angular/forms`
2016-06-08 15:36:24 -07:00
*
2016-08-17 07:44:39 -07:00
* @stable
2016-06-08 15:36:24 -07:00
*/
2016-06-10 11:15:59 -07:00
export class FormArray extends AbstractControl {
2016-06-08 16:38:52 -07:00
constructor (
public controls : AbstractControl [ ] , validator : ValidatorFn = null ,
asyncValidator : AsyncValidatorFn = null ) {
2016-06-08 15:36:24 -07:00
super ( validator , asyncValidator ) ;
this . _initObservables ( ) ;
2016-09-02 15:57:35 -07:00
this . _setUpControls ( ) ;
2016-06-08 15:36:24 -07:00
this . updateValueAndValidity ( { onlySelf : true , emitEvent : false } ) ;
}
/**
* Get the {@link AbstractControl} at the given `index` in the array.
*/
at ( index : number ) : AbstractControl { return this . controls [ index ] ; }
/**
* Insert a new {@link AbstractControl} at the end of the array.
*/
push ( control : AbstractControl ) : void {
this . controls . push ( control ) ;
2016-09-02 15:57:35 -07:00
this . _registerControl ( control ) ;
2016-06-08 15:36:24 -07:00
this . updateValueAndValidity ( ) ;
2016-09-02 15:57:35 -07:00
this . _onCollectionChange ( ) ;
2016-06-08 15:36:24 -07:00
}
/**
* Insert a new {@link AbstractControl} at the given `index` in the array.
*/
insert ( index : number , control : AbstractControl ) : void {
2016-10-19 19:55:50 +03:00
this . controls . splice ( index , 0 , control ) ;
2016-09-02 15:57:35 -07:00
this . _registerControl ( control ) ;
2016-06-08 15:36:24 -07:00
this . updateValueAndValidity ( ) ;
2016-09-02 15:57:35 -07:00
this . _onCollectionChange ( ) ;
2016-06-08 15:36:24 -07:00
}
/**
* Remove the control at the given `index` in the array.
*/
removeAt ( index : number ) : void {
2016-09-02 15:57:35 -07:00
if ( this . controls [ index ] ) this . controls [ index ] . _registerOnCollectionChange ( ( ) = > { } ) ;
2016-10-19 19:55:50 +03:00
this . controls . splice ( index , 1 ) ;
2016-06-08 15:36:24 -07:00
this . updateValueAndValidity ( ) ;
2016-09-02 15:57:35 -07:00
this . _onCollectionChange ( ) ;
}
/**
* Replace an existing control.
*/
setControl ( index : number , control : AbstractControl ) : void {
if ( this . controls [ index ] ) this . controls [ index ] . _registerOnCollectionChange ( ( ) = > { } ) ;
2016-10-19 19:55:50 +03:00
this . controls . splice ( index , 1 ) ;
2016-09-02 15:57:35 -07:00
if ( control ) {
2016-10-19 19:55:50 +03:00
this . controls . splice ( index , 0 , control ) ;
2016-09-02 15:57:35 -07:00
this . _registerControl ( control ) ;
}
this . updateValueAndValidity ( ) ;
this . _onCollectionChange ( ) ;
2016-06-08 15:36:24 -07:00
}
/**
* Length of the control array.
*/
get length ( ) : number { return this . controls . length ; }
2016-09-13 13:07:40 -07:00
/**
* Sets the value of the {@link FormArray}. It accepts an array that matches
* the structure of the control.
*
* This method performs strict checks, so it will throw an error if you try
* to set the value of a control that doesn't exist or if you exclude the
* value of a control.
*
* ### Example
*
* ```
* const arr = new FormArray([
* new FormControl(),
* new FormControl()
* ]);
* console.log(arr.value); // [null, null]
*
* arr.setValue(['Nancy', 'Drew']);
* console.log(arr.value); // ['Nancy', 'Drew']
* ```
*/
2016-10-19 18:54:54 +02:00
setValue ( value : any [ ] , { onlySelf , emitEvent } : { onlySelf? : boolean , emitEvent? : boolean } = { } ) :
void {
2016-08-05 13:35:17 -07:00
this . _checkAllValuesPresent ( value ) ;
2016-07-08 13:04:25 -07:00
value . forEach ( ( newValue : any , index : number ) = > {
this . _throwIfControlMissing ( index ) ;
2016-10-19 18:54:54 +02:00
this . at ( index ) . setValue ( newValue , { onlySelf : true , emitEvent } ) ;
2016-08-05 13:35:17 -07:00
} ) ;
2016-10-19 18:54:54 +02:00
this . updateValueAndValidity ( { onlySelf , emitEvent } ) ;
2016-08-05 13:35:17 -07:00
}
2016-09-13 13:07:40 -07:00
/**
* Patches the value of the {@link FormArray}. It accepts an array that matches the
* structure of the control, and will do its best to match the values to the correct
* controls in the group.
*
* It accepts both super-sets and sub-sets of the array without throwing an error.
*
* ### Example
*
* ```
* const arr = new FormArray([
* new FormControl(),
* new FormControl()
* ]);
* console.log(arr.value); // [null, null]
*
* arr.patchValue(['Nancy']);
* console.log(arr.value); // ['Nancy', null]
* ```
*/
2016-10-19 18:54:54 +02:00
patchValue ( value : any [ ] , { onlySelf , emitEvent } : { onlySelf? : boolean , emitEvent? : boolean } = { } ) :
void {
2016-08-05 13:35:17 -07:00
value . forEach ( ( newValue : any , index : number ) = > {
if ( this . at ( index ) ) {
2016-10-19 18:54:54 +02:00
this . at ( index ) . patchValue ( newValue , { onlySelf : true , emitEvent } ) ;
2016-08-05 13:35:17 -07:00
}
2016-07-08 13:04:25 -07:00
} ) ;
2016-10-19 18:54:54 +02:00
this . updateValueAndValidity ( { onlySelf , emitEvent } ) ;
2016-07-08 13:04:25 -07:00
}
2016-09-13 13:07:40 -07:00
/**
* Resets the {@link FormArray}. This means by default:
*
* * The array and all descendants are marked `pristine`
* * The array and all descendants are marked `untouched`
* * The value of all descendants will be null or null maps
*
* You can also reset to a specific form state by passing in an array of states
* that matches the structure of the control. The state can be a standalone value
* or a form state object with both a value and a disabled status.
*
* ### Example
*
* ```ts
* this.arr.reset(['name', 'last name']);
*
* console.log(this.arr.value); // ['name', 'last name']
* ```
*
* - OR -
*
* ```
* this.arr.reset([
* {value: 'name', disabled: true},
* 'last'
* ]);
*
* console.log(this.arr.value); // ['name', 'last name']
* console.log(this.arr.get(0).status); // 'DISABLED'
* ```
*/
2016-10-19 18:54:54 +02:00
reset ( value : any = [ ] , { onlySelf , emitEvent } : { onlySelf? : boolean , emitEvent? : boolean } = { } ) :
void {
2016-07-12 15:02:25 -07:00
this . _forEachChild ( ( control : AbstractControl , index : number ) = > {
2016-10-19 18:54:54 +02:00
control . reset ( value [ index ] , { onlySelf : true , emitEvent } ) ;
2016-07-12 15:02:25 -07:00
} ) ;
2016-10-19 18:54:54 +02:00
this . updateValueAndValidity ( { onlySelf , emitEvent } ) ;
this . _updatePristine ( { onlySelf } ) ;
this . _updateTouched ( { onlySelf } ) ;
2016-07-12 15:02:25 -07:00
}
2016-09-13 13:07:40 -07:00
/**
* The aggregate value of the array, including any disabled controls.
*
* If you'd like to include all values regardless of disabled status, use this method.
* Otherwise, the `value` property is the best way to get the value of the array.
*/
2016-08-24 16:58:43 -07:00
getRawValue ( ) : any [ ] { return this . controls . map ( ( control ) = > control . value ) ; }
2016-07-08 13:04:25 -07:00
/** @internal */
_throwIfControlMissing ( index : number ) : void {
2016-08-05 13:35:17 -07:00
if ( ! this . controls . length ) {
2016-08-25 00:50:16 -07:00
throw new Error ( `
2016-08-05 13:35:17 -07: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 13:04:25 -07:00
if ( ! this . at ( index ) ) {
2016-08-25 00:50:16 -07:00
throw new Error ( ` Cannot find form control at index ${ index } ` ) ;
2016-07-08 13:04:25 -07:00
}
}
2016-07-12 15:02:25 -07:00
/** @internal */
_forEachChild ( cb : Function ) : void {
this . controls . forEach ( ( control : AbstractControl , index : number ) = > { cb ( control , index ) ; } ) ;
}
2016-06-08 15:36:24 -07:00
/** @internal */
2016-08-24 16:58:43 -07:00
_updateValue ( ) : void {
this . _value = this . controls . filter ( ( control ) = > control . enabled || this . disabled )
. map ( ( control ) = > control . value ) ;
}
2016-06-08 15:36:24 -07:00
/** @internal */
2016-07-12 15:02:25 -07:00
_anyControls ( condition : Function ) : boolean {
2016-08-24 16:58:43 -07:00
return this . controls . some ( ( control : AbstractControl ) = > control . enabled && condition ( control ) ) ;
2016-06-08 15:36:24 -07:00
}
/** @internal */
2016-09-02 15:57:35 -07:00
_setUpControls ( ) : void {
this . _forEachChild ( ( control : AbstractControl ) = > this . _registerControl ( control ) ) ;
2016-06-08 15:36:24 -07:00
}
2016-08-05 13:35:17 -07:00
/** @internal */
_checkAllValuesPresent ( value : any ) : void {
this . _forEachChild ( ( control : AbstractControl , i : number ) = > {
if ( value [ i ] === undefined ) {
2016-08-25 00:50:16 -07:00
throw new Error ( ` Must supply a value for form control at index: ${ i } . ` ) ;
2016-08-05 13:35:17 -07:00
}
} ) ;
}
2016-08-24 16:58:43 -07:00
/** @internal */
_allControlsDisabled ( ) : boolean {
2016-11-12 14:08:58 +01:00
for ( const control of this . controls ) {
2016-08-24 16:58:43 -07:00
if ( control . enabled ) return false ;
}
2016-09-08 12:21:48 -07:00
return this . controls . length > 0 || this . disabled ;
2016-08-24 16:58:43 -07:00
}
2016-09-02 15:57:35 -07:00
private _registerControl ( control : AbstractControl ) {
control . setParent ( this ) ;
control . _registerOnCollectionChange ( this . _onCollectionChange ) ;
}
2016-06-08 15:36:24 -07:00
}