refactor: move angular source to /packages rather than modules/@angular
This commit is contained in:
@ -0,0 +1,120 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
import {Directive, EventEmitter, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core';
|
||||
|
||||
import {FormControl} from '../../model';
|
||||
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators';
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '../control_value_accessor';
|
||||
import {NgControl} from '../ng_control';
|
||||
import {ReactiveErrors} from '../reactive_errors';
|
||||
import {composeAsyncValidators, composeValidators, isPropertyUpdated, selectValueAccessor, setUpControl} from '../shared';
|
||||
import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from '../validators';
|
||||
|
||||
export const formControlBinding: any = {
|
||||
provide: NgControl,
|
||||
useExisting: forwardRef(() => FormControlDirective)
|
||||
};
|
||||
|
||||
/**
|
||||
* @whatItDoes Syncs a standalone {@link FormControl} instance to a form control element.
|
||||
*
|
||||
* In other words, this directive ensures that any values written to the {@link FormControl}
|
||||
* instance programmatically will be written to the DOM element (model -> view). Conversely,
|
||||
* any values written to the DOM element through user input will be reflected in the
|
||||
* {@link FormControl} instance (view -> model).
|
||||
*
|
||||
* @howToUse
|
||||
*
|
||||
* Use this directive if you'd like to create and manage a {@link FormControl} instance directly.
|
||||
* Simply create a {@link FormControl}, save it to your component class, and pass it into the
|
||||
* {@link FormControlDirective}.
|
||||
*
|
||||
* This directive is designed to be used as a standalone control. Unlike {@link FormControlName},
|
||||
* it does not require that your {@link FormControl} instance be part of any parent
|
||||
* {@link FormGroup}, and it won't be registered to any {@link FormGroupDirective} that
|
||||
* exists above it.
|
||||
*
|
||||
* **Get the value**: the `value` property is always synced and available on the
|
||||
* {@link FormControl} instance. See a full list of available properties in
|
||||
* {@link AbstractControl}.
|
||||
*
|
||||
* **Set the value**: You can pass in an initial value when instantiating the {@link FormControl},
|
||||
* or you can set it programmatically later using {@link AbstractControl.setValue} or
|
||||
* {@link AbstractControl.patchValue}.
|
||||
*
|
||||
* **Listen to value**: If you want to listen to changes in the value of the control, you can
|
||||
* subscribe to the {@link AbstractControl.valueChanges} event. You can also listen to
|
||||
* {@link AbstractControl.statusChanges} to be notified when the validation status is
|
||||
* re-calculated.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example forms/ts/simpleFormControl/simple_form_control_example.ts region='Component'}
|
||||
*
|
||||
* * **npm package**: `@angular/forms`
|
||||
*
|
||||
* * **NgModule**: `ReactiveFormsModule`
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[formControl]', providers: [formControlBinding], exportAs: 'ngForm'})
|
||||
|
||||
export class FormControlDirective extends NgControl implements OnChanges {
|
||||
viewModel: any;
|
||||
|
||||
@Input('formControl') form: FormControl;
|
||||
@Input('ngModel') model: any;
|
||||
@Output('ngModelChange') update = new EventEmitter();
|
||||
|
||||
@Input('disabled')
|
||||
set isDisabled(isDisabled: boolean) { ReactiveErrors.disabledAttrWarning(); }
|
||||
|
||||
constructor(@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
|
||||
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<AsyncValidator|AsyncValidatorFn>,
|
||||
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
|
||||
valueAccessors: ControlValueAccessor[]) {
|
||||
super();
|
||||
this._rawValidators = validators || [];
|
||||
this._rawAsyncValidators = asyncValidators || [];
|
||||
this.valueAccessor = selectValueAccessor(this, valueAccessors);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (this._isControlChanged(changes)) {
|
||||
setUpControl(this.form, this);
|
||||
if (this.control.disabled && this.valueAccessor.setDisabledState) {
|
||||
this.valueAccessor.setDisabledState(true);
|
||||
}
|
||||
this.form.updateValueAndValidity({emitEvent: false});
|
||||
}
|
||||
if (isPropertyUpdated(changes, this.viewModel)) {
|
||||
this.form.setValue(this.model);
|
||||
this.viewModel = this.model;
|
||||
}
|
||||
}
|
||||
|
||||
get path(): string[] { return []; }
|
||||
|
||||
get validator(): ValidatorFn { return composeValidators(this._rawValidators); }
|
||||
|
||||
get asyncValidator(): AsyncValidatorFn {
|
||||
return composeAsyncValidators(this._rawAsyncValidators);
|
||||
}
|
||||
|
||||
get control(): FormControl { return this.form; }
|
||||
|
||||
viewToModelUpdate(newValue: any): void {
|
||||
this.viewModel = newValue;
|
||||
this.update.emit(newValue);
|
||||
}
|
||||
|
||||
private _isControlChanged(changes: {[key: string]: any}): boolean {
|
||||
return changes.hasOwnProperty('form');
|
||||
}
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
import {Directive, EventEmitter, Host, Inject, Input, OnChanges, OnDestroy, Optional, Output, Self, SimpleChanges, SkipSelf, forwardRef} from '@angular/core';
|
||||
|
||||
import {FormControl} from '../../model';
|
||||
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators';
|
||||
import {AbstractFormGroupDirective} from '../abstract_form_group_directive';
|
||||
import {ControlContainer} from '../control_container';
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '../control_value_accessor';
|
||||
import {NgControl} from '../ng_control';
|
||||
import {ReactiveErrors} from '../reactive_errors';
|
||||
import {composeAsyncValidators, composeValidators, controlPath, isPropertyUpdated, selectValueAccessor} from '../shared';
|
||||
import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from '../validators';
|
||||
|
||||
import {FormGroupDirective} from './form_group_directive';
|
||||
import {FormArrayName, FormGroupName} from './form_group_name';
|
||||
|
||||
export const controlNameBinding: any = {
|
||||
provide: NgControl,
|
||||
useExisting: forwardRef(() => FormControlName)
|
||||
};
|
||||
|
||||
/**
|
||||
* @whatItDoes Syncs a {@link FormControl} in an existing {@link FormGroup} to a form control
|
||||
* element by name.
|
||||
*
|
||||
* In other words, this directive ensures that any values written to the {@link FormControl}
|
||||
* instance programmatically will be written to the DOM element (model -> view). Conversely,
|
||||
* any values written to the DOM element through user input will be reflected in the
|
||||
* {@link FormControl} instance (view -> model).
|
||||
*
|
||||
* @howToUse
|
||||
*
|
||||
* This directive is designed to be used with a parent {@link FormGroupDirective} (selector:
|
||||
* `[formGroup]`).
|
||||
*
|
||||
* It accepts the string name of the {@link FormControl} instance you want to
|
||||
* link, and will look for a {@link FormControl} registered with that name in the
|
||||
* closest {@link FormGroup} or {@link FormArray} above it.
|
||||
*
|
||||
* **Access the control**: You can access the {@link FormControl} associated with
|
||||
* this directive by using the {@link AbstractControl.get} method.
|
||||
* Ex: `this.form.get('first');`
|
||||
*
|
||||
* **Get value**: the `value` property is always synced and available on the {@link FormControl}.
|
||||
* See a full list of available properties in {@link AbstractControl}.
|
||||
*
|
||||
* **Set value**: You can set an initial value for the control when instantiating the
|
||||
* {@link FormControl}, or you can set it programmatically later using
|
||||
* {@link AbstractControl.setValue} or {@link AbstractControl.patchValue}.
|
||||
*
|
||||
* **Listen to value**: If you want to listen to changes in the value of the control, you can
|
||||
* subscribe to the {@link AbstractControl.valueChanges} event. You can also listen to
|
||||
* {@link AbstractControl.statusChanges} to be notified when the validation status is
|
||||
* re-calculated.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* In this example, we create form controls for first name and last name.
|
||||
*
|
||||
* {@example forms/ts/simpleFormGroup/simple_form_group_example.ts region='Component'}
|
||||
*
|
||||
* To see `formControlName` examples with different form control types, see:
|
||||
*
|
||||
* * Radio buttons: {@link RadioControlValueAccessor}
|
||||
* * Selects: {@link SelectControlValueAccessor}
|
||||
*
|
||||
* **npm package**: `@angular/forms`
|
||||
*
|
||||
* **NgModule**: {@link ReactiveFormsModule}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[formControlName]', providers: [controlNameBinding]})
|
||||
export class FormControlName extends NgControl implements OnChanges, OnDestroy {
|
||||
private _added = false;
|
||||
/** @internal */
|
||||
viewModel: any;
|
||||
/** @internal */
|
||||
_control: FormControl;
|
||||
|
||||
@Input('formControlName') name: string;
|
||||
|
||||
// TODO(kara): Replace ngModel with reactive API
|
||||
@Input('ngModel') model: any;
|
||||
@Output('ngModelChange') update = new EventEmitter();
|
||||
@Input('disabled')
|
||||
set isDisabled(isDisabled: boolean) { ReactiveErrors.disabledAttrWarning(); }
|
||||
|
||||
constructor(
|
||||
@Optional() @Host() @SkipSelf() parent: ControlContainer,
|
||||
@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
|
||||
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators:
|
||||
Array<AsyncValidator|AsyncValidatorFn>,
|
||||
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) {
|
||||
super();
|
||||
this._parent = parent;
|
||||
this._rawValidators = validators || [];
|
||||
this._rawAsyncValidators = asyncValidators || [];
|
||||
this.valueAccessor = selectValueAccessor(this, valueAccessors);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (!this._added) this._setUpControl();
|
||||
if (isPropertyUpdated(changes, this.viewModel)) {
|
||||
this.viewModel = this.model;
|
||||
this.formDirective.updateModel(this, this.model);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.formDirective) {
|
||||
this.formDirective.removeControl(this);
|
||||
}
|
||||
}
|
||||
|
||||
viewToModelUpdate(newValue: any): void {
|
||||
this.viewModel = newValue;
|
||||
this.update.emit(newValue);
|
||||
}
|
||||
|
||||
get path(): string[] { return controlPath(this.name, this._parent); }
|
||||
|
||||
get formDirective(): any { return this._parent ? this._parent.formDirective : null; }
|
||||
|
||||
get validator(): ValidatorFn { return composeValidators(this._rawValidators); }
|
||||
|
||||
get asyncValidator(): AsyncValidatorFn {
|
||||
return composeAsyncValidators(this._rawAsyncValidators);
|
||||
}
|
||||
|
||||
get control(): FormControl { return this._control; }
|
||||
|
||||
private _checkParentType(): void {
|
||||
if (!(this._parent instanceof FormGroupName) &&
|
||||
this._parent instanceof AbstractFormGroupDirective) {
|
||||
ReactiveErrors.ngModelGroupException();
|
||||
} else if (
|
||||
!(this._parent instanceof FormGroupName) && !(this._parent instanceof FormGroupDirective) &&
|
||||
!(this._parent instanceof FormArrayName)) {
|
||||
ReactiveErrors.controlParentException();
|
||||
}
|
||||
}
|
||||
|
||||
private _setUpControl() {
|
||||
this._checkParentType();
|
||||
this._control = this.formDirective.addControl(this);
|
||||
if (this.control.disabled && this.valueAccessor.setDisabledState) {
|
||||
this.valueAccessor.setDisabledState(true);
|
||||
}
|
||||
this._added = true;
|
||||
}
|
||||
}
|
@ -0,0 +1,188 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
import {Directive, EventEmitter, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core';
|
||||
import {FormArray, FormControl, FormGroup} from '../../model';
|
||||
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from '../../validators';
|
||||
import {ControlContainer} from '../control_container';
|
||||
import {Form} from '../form_interface';
|
||||
import {ReactiveErrors} from '../reactive_errors';
|
||||
import {cleanUpControl, composeAsyncValidators, composeValidators, setUpControl, setUpFormContainer} from '../shared';
|
||||
|
||||
import {FormControlName} from './form_control_name';
|
||||
import {FormArrayName, FormGroupName} from './form_group_name';
|
||||
|
||||
export const formDirectiveProvider: any = {
|
||||
provide: ControlContainer,
|
||||
useExisting: forwardRef(() => FormGroupDirective)
|
||||
};
|
||||
|
||||
/**
|
||||
* @whatItDoes Binds an existing {@link FormGroup} to a DOM element.
|
||||
*
|
||||
* @howToUse
|
||||
*
|
||||
* This directive accepts an existing {@link FormGroup} instance. It will then use this
|
||||
* {@link FormGroup} instance to match any child {@link FormControl}, {@link FormGroup},
|
||||
* and {@link FormArray} instances to child {@link FormControlName}, {@link FormGroupName},
|
||||
* and {@link FormArrayName} directives.
|
||||
*
|
||||
* **Set value**: You can set the form's initial value when instantiating the
|
||||
* {@link FormGroup}, or you can set it programmatically later using the {@link FormGroup}'s
|
||||
* {@link AbstractControl.setValue} or {@link AbstractControl.patchValue} methods.
|
||||
*
|
||||
* **Listen to value**: If you want to listen to changes in the value of the form, you can subscribe
|
||||
* to the {@link FormGroup}'s {@link AbstractControl.valueChanges} event. You can also listen to
|
||||
* its {@link AbstractControl.statusChanges} event to be notified when the validation status is
|
||||
* re-calculated.
|
||||
*
|
||||
* Furthermore, you can listen to the directive's `ngSubmit` event to be notified when the user has
|
||||
* triggered a form submission. The `ngSubmit` event will be emitted with the original form
|
||||
* submission event.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* In this example, we create form controls for first name and last name.
|
||||
*
|
||||
* {@example forms/ts/simpleFormGroup/simple_form_group_example.ts region='Component'}
|
||||
*
|
||||
* **npm package**: `@angular/forms`
|
||||
*
|
||||
* **NgModule**: {@link ReactiveFormsModule}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[formGroup]',
|
||||
providers: [formDirectiveProvider],
|
||||
host: {'(submit)': 'onSubmit($event)', '(reset)': 'onReset()'},
|
||||
exportAs: 'ngForm'
|
||||
})
|
||||
export class FormGroupDirective extends ControlContainer implements Form,
|
||||
OnChanges {
|
||||
private _submitted: boolean = false;
|
||||
private _oldForm: FormGroup;
|
||||
directives: FormControlName[] = [];
|
||||
|
||||
@Input('formGroup') form: FormGroup = null;
|
||||
@Output() ngSubmit = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
@Optional() @Self() @Inject(NG_VALIDATORS) private _validators: any[],
|
||||
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators: any[]) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
this._checkFormPresent();
|
||||
if (changes.hasOwnProperty('form')) {
|
||||
this._updateValidators();
|
||||
this._updateDomValue();
|
||||
this._updateRegistrations();
|
||||
}
|
||||
}
|
||||
|
||||
get submitted(): boolean { return this._submitted; }
|
||||
|
||||
get formDirective(): Form { return this; }
|
||||
|
||||
get control(): FormGroup { return this.form; }
|
||||
|
||||
get path(): string[] { return []; }
|
||||
|
||||
addControl(dir: FormControlName): FormControl {
|
||||
const ctrl: any = this.form.get(dir.path);
|
||||
setUpControl(ctrl, dir);
|
||||
ctrl.updateValueAndValidity({emitEvent: false});
|
||||
this.directives.push(dir);
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
getControl(dir: FormControlName): FormControl { return <FormControl>this.form.get(dir.path); }
|
||||
|
||||
removeControl(dir: FormControlName): void { remove(this.directives, dir); }
|
||||
|
||||
addFormGroup(dir: FormGroupName): void {
|
||||
const ctrl: any = this.form.get(dir.path);
|
||||
setUpFormContainer(ctrl, dir);
|
||||
ctrl.updateValueAndValidity({emitEvent: false});
|
||||
}
|
||||
|
||||
removeFormGroup(dir: FormGroupName): void {}
|
||||
|
||||
getFormGroup(dir: FormGroupName): FormGroup { return <FormGroup>this.form.get(dir.path); }
|
||||
|
||||
addFormArray(dir: FormArrayName): void {
|
||||
const ctrl: any = this.form.get(dir.path);
|
||||
setUpFormContainer(ctrl, dir);
|
||||
ctrl.updateValueAndValidity({emitEvent: false});
|
||||
}
|
||||
|
||||
removeFormArray(dir: FormArrayName): void {}
|
||||
|
||||
getFormArray(dir: FormArrayName): FormArray { return <FormArray>this.form.get(dir.path); }
|
||||
|
||||
updateModel(dir: FormControlName, value: any): void {
|
||||
const ctrl = <FormControl>this.form.get(dir.path);
|
||||
ctrl.setValue(value);
|
||||
}
|
||||
|
||||
onSubmit($event: Event): boolean {
|
||||
this._submitted = true;
|
||||
this.ngSubmit.emit($event);
|
||||
return false;
|
||||
}
|
||||
|
||||
onReset(): void { this.resetForm(); }
|
||||
|
||||
resetForm(value: any = undefined): void {
|
||||
this.form.reset(value);
|
||||
this._submitted = false;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_updateDomValue() {
|
||||
this.directives.forEach(dir => {
|
||||
const newCtrl: any = this.form.get(dir.path);
|
||||
if (dir._control !== newCtrl) {
|
||||
cleanUpControl(dir._control, dir);
|
||||
if (newCtrl) setUpControl(newCtrl, dir);
|
||||
dir._control = newCtrl;
|
||||
}
|
||||
});
|
||||
|
||||
this.form._updateTreeValidity({emitEvent: false});
|
||||
}
|
||||
|
||||
private _updateRegistrations() {
|
||||
this.form._registerOnCollectionChange(() => this._updateDomValue());
|
||||
if (this._oldForm) this._oldForm._registerOnCollectionChange(() => {});
|
||||
this._oldForm = this.form;
|
||||
}
|
||||
|
||||
private _updateValidators() {
|
||||
const sync = composeValidators(this._validators);
|
||||
this.form.validator = Validators.compose([this.form.validator, sync]);
|
||||
|
||||
const async = composeAsyncValidators(this._asyncValidators);
|
||||
this.form.asyncValidator = Validators.composeAsync([this.form.asyncValidator, async]);
|
||||
}
|
||||
|
||||
private _checkFormPresent() {
|
||||
if (!this.form) {
|
||||
ReactiveErrors.missingFormException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function remove<T>(list: T[], el: T): void {
|
||||
const index = list.indexOf(el);
|
||||
if (index > -1) {
|
||||
list.splice(index, 1);
|
||||
}
|
||||
}
|
@ -0,0 +1,200 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
import {Directive, Host, Inject, Input, OnDestroy, OnInit, Optional, Self, SkipSelf, forwardRef} from '@angular/core';
|
||||
|
||||
import {FormArray} from '../../model';
|
||||
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators';
|
||||
import {AbstractFormGroupDirective} from '../abstract_form_group_directive';
|
||||
import {ControlContainer} from '../control_container';
|
||||
import {ReactiveErrors} from '../reactive_errors';
|
||||
import {composeAsyncValidators, composeValidators, controlPath} from '../shared';
|
||||
import {AsyncValidatorFn, ValidatorFn} from '../validators';
|
||||
|
||||
import {FormGroupDirective} from './form_group_directive';
|
||||
|
||||
export const formGroupNameProvider: any = {
|
||||
provide: ControlContainer,
|
||||
useExisting: forwardRef(() => FormGroupName)
|
||||
};
|
||||
|
||||
/**
|
||||
* @whatItDoes Syncs a nested {@link FormGroup} to a DOM element.
|
||||
*
|
||||
* @howToUse
|
||||
*
|
||||
* This directive can only be used with a parent {@link FormGroupDirective} (selector:
|
||||
* `[formGroup]`).
|
||||
*
|
||||
* It accepts the string name of the nested {@link FormGroup} you want to link, and
|
||||
* will look for a {@link FormGroup} registered with that name in the parent
|
||||
* {@link FormGroup} instance you passed into {@link FormGroupDirective}.
|
||||
*
|
||||
* Nested form groups can come in handy when you want to validate a sub-group of a
|
||||
* form separately from the rest or when you'd like to group the values of certain
|
||||
* controls into their own nested object.
|
||||
*
|
||||
* **Access the group**: You can access the associated {@link FormGroup} using the
|
||||
* {@link AbstractControl.get} method. Ex: `this.form.get('name')`.
|
||||
*
|
||||
* You can also access individual controls within the group using dot syntax.
|
||||
* Ex: `this.form.get('name.first')`
|
||||
*
|
||||
* **Get the value**: the `value` property is always synced and available on the
|
||||
* {@link FormGroup}. See a full list of available properties in {@link AbstractControl}.
|
||||
*
|
||||
* **Set the value**: You can set an initial value for each child control when instantiating
|
||||
* the {@link FormGroup}, or you can set it programmatically later using
|
||||
* {@link AbstractControl.setValue} or {@link AbstractControl.patchValue}.
|
||||
*
|
||||
* **Listen to value**: If you want to listen to changes in the value of the group, you can
|
||||
* subscribe to the {@link AbstractControl.valueChanges} event. You can also listen to
|
||||
* {@link AbstractControl.statusChanges} to be notified when the validation status is
|
||||
* re-calculated.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example forms/ts/nestedFormGroup/nested_form_group_example.ts region='Component'}
|
||||
*
|
||||
* * **npm package**: `@angular/forms`
|
||||
*
|
||||
* * **NgModule**: `ReactiveFormsModule`
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[formGroupName]', providers: [formGroupNameProvider]})
|
||||
export class FormGroupName extends AbstractFormGroupDirective implements OnInit, OnDestroy {
|
||||
@Input('formGroupName') name: string;
|
||||
|
||||
constructor(
|
||||
@Optional() @Host() @SkipSelf() parent: ControlContainer,
|
||||
@Optional() @Self() @Inject(NG_VALIDATORS) validators: any[],
|
||||
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: any[]) {
|
||||
super();
|
||||
this._parent = parent;
|
||||
this._validators = validators;
|
||||
this._asyncValidators = asyncValidators;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_checkParentType(): void {
|
||||
if (_hasInvalidParent(this._parent)) {
|
||||
ReactiveErrors.groupParentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const formArrayNameProvider: any = {
|
||||
provide: ControlContainer,
|
||||
useExisting: forwardRef(() => FormArrayName)
|
||||
};
|
||||
|
||||
/**
|
||||
* @whatItDoes Syncs a nested {@link FormArray} to a DOM element.
|
||||
*
|
||||
* @howToUse
|
||||
*
|
||||
* This directive is designed to be used with a parent {@link FormGroupDirective} (selector:
|
||||
* `[formGroup]`).
|
||||
*
|
||||
* It accepts the string name of the nested {@link FormArray} you want to link, and
|
||||
* will look for a {@link FormArray} registered with that name in the parent
|
||||
* {@link FormGroup} instance you passed into {@link FormGroupDirective}.
|
||||
*
|
||||
* Nested form arrays can come in handy when you have a group of form controls but
|
||||
* you're not sure how many there will be. Form arrays allow you to create new
|
||||
* form controls dynamically.
|
||||
*
|
||||
* **Access the array**: You can access the associated {@link FormArray} using the
|
||||
* {@link AbstractControl.get} method on the parent {@link FormGroup}.
|
||||
* Ex: `this.form.get('cities')`.
|
||||
*
|
||||
* **Get the value**: the `value` property is always synced and available on the
|
||||
* {@link FormArray}. See a full list of available properties in {@link AbstractControl}.
|
||||
*
|
||||
* **Set the value**: You can set an initial value for each child control when instantiating
|
||||
* the {@link FormArray}, or you can set the value programmatically later using the
|
||||
* {@link FormArray}'s {@link AbstractControl.setValue} or {@link AbstractControl.patchValue}
|
||||
* methods.
|
||||
*
|
||||
* **Listen to value**: If you want to listen to changes in the value of the array, you can
|
||||
* subscribe to the {@link FormArray}'s {@link AbstractControl.valueChanges} event. You can also
|
||||
* listen to its {@link AbstractControl.statusChanges} event to be notified when the validation
|
||||
* status is re-calculated.
|
||||
*
|
||||
* **Add new controls**: You can add new controls to the {@link FormArray} dynamically by
|
||||
* calling its {@link FormArray.push} method.
|
||||
* Ex: `this.form.get('cities').push(new FormControl());`
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example forms/ts/nestedFormArray/nested_form_array_example.ts region='Component'}
|
||||
*
|
||||
* * **npm package**: `@angular/forms`
|
||||
*
|
||||
* * **NgModule**: `ReactiveFormsModule`
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[formArrayName]', providers: [formArrayNameProvider]})
|
||||
export class FormArrayName extends ControlContainer implements OnInit, OnDestroy {
|
||||
/** @internal */
|
||||
_parent: ControlContainer;
|
||||
|
||||
/** @internal */
|
||||
_validators: any[];
|
||||
|
||||
/** @internal */
|
||||
_asyncValidators: any[];
|
||||
|
||||
@Input('formArrayName') name: string;
|
||||
|
||||
constructor(
|
||||
@Optional() @Host() @SkipSelf() parent: ControlContainer,
|
||||
@Optional() @Self() @Inject(NG_VALIDATORS) validators: any[],
|
||||
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: any[]) {
|
||||
super();
|
||||
this._parent = parent;
|
||||
this._validators = validators;
|
||||
this._asyncValidators = asyncValidators;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._checkParentType();
|
||||
this.formDirective.addFormArray(this);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.formDirective) {
|
||||
this.formDirective.removeFormArray(this);
|
||||
}
|
||||
}
|
||||
|
||||
get control(): FormArray { return this.formDirective.getFormArray(this); }
|
||||
|
||||
get formDirective(): FormGroupDirective {
|
||||
return this._parent ? <FormGroupDirective>this._parent.formDirective : null;
|
||||
}
|
||||
|
||||
get path(): string[] { return controlPath(this.name, this._parent); }
|
||||
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
|
||||
|
||||
private _checkParentType(): void {
|
||||
if (_hasInvalidParent(this._parent)) {
|
||||
ReactiveErrors.arrayParentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _hasInvalidParent(parent: ControlContainer): boolean {
|
||||
return !(parent instanceof FormGroupName) && !(parent instanceof FormGroupDirective) &&
|
||||
!(parent instanceof FormArrayName);
|
||||
}
|
Reference in New Issue
Block a user