feat(forms): add support for formArrayName

Closes #9251
This commit is contained in:
Kara Erickson
2016-06-25 13:27:29 -07:00
parent 17dcbf66b9
commit c03e1f2f59
9 changed files with 238 additions and 14 deletions

View File

@ -16,6 +16,7 @@ import {NgModel} from './directives/ng_model';
import {NgModelGroup} from './directives/ng_model_group';
import {NumberValueAccessor} from './directives/number_value_accessor';
import {RadioControlValueAccessor} from './directives/radio_control_value_accessor';
import {FormArrayName} from './directives/reactive_directives/form_array_name';
import {FormControlDirective} from './directives/reactive_directives/form_control_directive';
import {FormControlName} from './directives/reactive_directives/form_control_name';
import {FormGroupDirective} from './directives/reactive_directives/form_group_directive';
@ -34,6 +35,7 @@ export {NgModel} from './directives/ng_model';
export {NgModelGroup} from './directives/ng_model_group';
export {NumberValueAccessor} from './directives/number_value_accessor';
export {RadioControlValueAccessor} from './directives/radio_control_value_accessor';
export {FormArrayName} from './directives/reactive_directives/form_array_name';
export {FormControlDirective} from './directives/reactive_directives/form_control_directive';
export {FormControlName} from './directives/reactive_directives/form_control_name';
export {FormGroupDirective} from './directives/reactive_directives/form_group_directive';
@ -72,4 +74,6 @@ export const FORM_DIRECTIVES: Type[] = /*@ts2dart_const*/[
];
export const REACTIVE_FORM_DIRECTIVES: Type[] =
/*@ts2dart_const*/[FormControlDirective, FormGroupDirective, FormControlName, FormGroupName];
/*@ts2dart_const*/[
FormControlDirective, FormGroupDirective, FormControlName, FormGroupName, FormArrayName
];

View File

@ -19,7 +19,7 @@ import {Form} from './form_interface';
import {NgControl} from './ng_control';
import {NgModel} from './ng_model';
import {NgModelGroup} from './ng_model_group';
import {composeAsyncValidators, composeValidators, setUpControl, setUpFormGroup} from './shared';
import {composeAsyncValidators, composeValidators, setUpControl, setUpFormContainer} from './shared';
export const formDirectiveProvider: any =
/*@ts2dart_const*/ {provide: ControlContainer, useExisting: forwardRef(() => NgForm)};
@ -140,7 +140,7 @@ export class NgForm extends ControlContainer implements Form {
PromiseWrapper.scheduleMicrotask(() => {
var container = this._findContainer(dir.path);
var group = new FormGroup({});
setUpFormGroup(group, dir);
setUpFormContainer(group, dir);
container.registerControl(dir.name, group);
group.updateValueAndValidity({emitEvent: false});
});

View File

@ -0,0 +1,96 @@
/**
* @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 {ControlContainer} from '../control_container';
import {composeAsyncValidators, composeValidators, controlPath} from '../shared';
import {AsyncValidatorFn, ValidatorFn} from '../validators';
import {FormGroupDirective} from './form_group_directive';
export const formArrayNameProvider: any =
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
provide: ControlContainer,
useExisting: forwardRef(() => FormArrayName)
};
/**
* Syncs an existing form array to a DOM element.
*
* This directive can only be used as a child of {@link FormGroupDirective}.
*
* ```typescript
* @Component({
* selector: 'my-app',
* template: `
* <div>
* <h2>Angular FormArray Example</h2>
* <form [formGroup]="myForm">
* <div formArrayName="cities">
* <div *ngFor="let city of cityArray.controls; let i=index">
* <input [formControlName]="i">
* </div>
* </div>
* </form>
* {{ myForm.value | json }} // {cities: ['SF', 'NY']}
* </div>
* `
* })
* export class App {
* cityArray = new FormArray([
* new FormControl('SF'),
* new FormControl('NY')
* ]);
* myForm = new FormGroup({
* cities: this.cityArray
* });
* }
* ```
*
* @experimental
*/
@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(
@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.formDirective.addFormArray(this); }
ngOnDestroy(): void { this.formDirective.removeFormArray(this); }
get control(): FormArray { return this.formDirective.getFormArray(this); }
get formDirective(): FormGroupDirective { return <FormGroupDirective>this._parent.formDirective; }
get path(): string[] { return controlPath(this.name, this._parent); }
get validator(): ValidatorFn { return composeValidators(this._validators); }
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
}

View File

@ -12,13 +12,14 @@ import {EventEmitter, ObservableWrapper} from '../../facade/async';
import {ListWrapper, StringMapWrapper} from '../../facade/collection';
import {BaseException} from '../../facade/exceptions';
import {isBlank} from '../../facade/lang';
import {FormControl, FormGroup} from '../../model';
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 {NgControl} from '../ng_control';
import {composeAsyncValidators, composeValidators, setUpControl, setUpFormGroup} from '../shared';
import {composeAsyncValidators, composeValidators, setUpControl, setUpFormContainer} from '../shared';
import {FormArrayName} from './form_array_name';
import {FormGroupName} from './form_group_name';
export const formDirectiveProvider: any =
@ -155,16 +156,26 @@ export class FormGroupDirective extends ControlContainer implements Form,
removeControl(dir: NgControl): void { ListWrapper.remove(this.directives, dir); }
addFormGroup(dir: FormGroupName) {
addFormGroup(dir: FormGroupName): void {
var ctrl: any = this.form.find(dir.path);
setUpFormGroup(ctrl, dir);
setUpFormContainer(ctrl, dir);
ctrl.updateValueAndValidity({emitEvent: false});
}
removeFormGroup(dir: FormGroupName) {}
removeFormGroup(dir: FormGroupName): void {}
getFormGroup(dir: FormGroupName): FormGroup { return <FormGroup>this.form.find(dir.path); }
addFormArray(dir: FormArrayName): void {
var ctrl: any = this.form.find(dir.path);
setUpFormContainer(ctrl, dir);
ctrl.updateValueAndValidity({emitEvent: false});
}
removeFormArray(dir: FormArrayName): void {}
getFormArray(dir: FormArrayName): FormArray { return <FormArray>this.form.find(dir.path); }
updateModel(dir: NgControl, value: any): void {
var ctrl  = <FormControl>this.form.find(dir.path);
ctrl.updateValue(value);

View File

@ -9,7 +9,7 @@
import {ListWrapper, StringMapWrapper} from '../facade/collection';
import {BaseException} from '../facade/exceptions';
import {hasConstructor, isBlank, isPresent, looseIdentical} from '../facade/lang';
import {FormControl, FormGroup} from '../model';
import {FormArray, FormControl, FormGroup} from '../model';
import {Validators} from '../validators';
import {AbstractControlDirective} from './abstract_control_directive';
@ -22,6 +22,7 @@ import {NgControl} from './ng_control';
import {normalizeAsyncValidator, normalizeValidator} from './normalize_validator';
import {NumberValueAccessor} from './number_value_accessor';
import {RadioControlValueAccessor} from './radio_control_value_accessor';
import {FormArrayName} from './reactive_directives/form_array_name';
import {SelectControlValueAccessor} from './select_control_value_accessor';
import {AsyncValidatorFn, ValidatorFn} from './validators';
@ -54,7 +55,8 @@ export function setUpControl(control: FormControl, dir: NgControl): void {
dir.valueAccessor.registerOnTouched(() => control.markAsTouched());
}
export function setUpFormGroup(control: FormGroup, dir: AbstractFormGroupDirective) {
export function setUpFormContainer(
control: FormGroup | FormArray, dir: AbstractFormGroupDirective | FormArrayName) {
if (isBlank(control)) _throwError(dir, 'Cannot find control');
control.validator = Validators.compose([control.validator, dir.validator]);
control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]);

View File

@ -33,6 +33,7 @@ export {NgControlStatus} from './directives/ng_control_status';
export {NgForm} from './directives/ng_form';
export {NgModel} from './directives/ng_model';
export {NgModelGroup} from './directives/ng_model_group';
export {FormArrayName} from './directives/reactive_directives/form_array_name';
export {FormControlDirective} from './directives/reactive_directives/form_control_directive';
export {FormControlName} from './directives/reactive_directives/form_control_name';
export {FormGroupDirective} from './directives/reactive_directives/form_group_directive';