parent
17dcbf66b9
commit
c03e1f2f59
|
@ -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
|
||||
];
|
|
@ -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});
|
||||
});
|
||||
|
|
|
@ -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); }
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -12,7 +12,7 @@ import {fakeAsync, flushMicrotasks, tick,} from '@angular/core/testing';
|
|||
|
||||
import {SpyNgControl, SpyValueAccessor} from './spies';
|
||||
|
||||
import {FormGroup, FormControl, FormControlName, FormGroupName, NgModelGroup, FormGroupDirective, ControlValueAccessor, Validators, NgForm, NgModel, FormControlDirective, NgControl, DefaultValueAccessor, CheckboxControlValueAccessor, SelectControlValueAccessor, Validator} from '@angular/forms';
|
||||
import {FormGroup, FormControl, FormArray, FormArrayName, FormControlName, FormGroupName, NgModelGroup, FormGroupDirective, ControlValueAccessor, Validators, NgForm, NgModel, FormControlDirective, NgControl, DefaultValueAccessor, CheckboxControlValueAccessor, SelectControlValueAccessor, Validator} from '@angular/forms';
|
||||
|
||||
import {selectValueAccessor, composeValidators} from '@angular/forms/src/directives/shared';
|
||||
import {TimerWrapper} from '../src/facade/async';
|
||||
|
@ -376,6 +376,30 @@ export function main() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('FormArrayName', () => {
|
||||
var formModel: FormArray;
|
||||
var formArrayDir: FormArrayName;
|
||||
|
||||
beforeEach(() => {
|
||||
const parent = new FormGroupDirective([], []);
|
||||
formModel = new FormArray([new FormControl('')]);
|
||||
parent.form = new FormGroup({'array': formModel});
|
||||
formArrayDir = new FormArrayName(parent, [], []);
|
||||
formArrayDir.name = 'array';
|
||||
});
|
||||
|
||||
it('should reexport control properties', () => {
|
||||
expect(formArrayDir.control).toBe(formModel);
|
||||
expect(formArrayDir.value).toBe(formModel.value);
|
||||
expect(formArrayDir.valid).toBe(formModel.valid);
|
||||
expect(formArrayDir.errors).toBe(formModel.errors);
|
||||
expect(formArrayDir.pristine).toBe(formModel.pristine);
|
||||
expect(formArrayDir.dirty).toBe(formModel.dirty);
|
||||
expect(formArrayDir.touched).toBe(formModel.touched);
|
||||
expect(formArrayDir.untouched).toBe(formModel.untouched);
|
||||
});
|
||||
});
|
||||
|
||||
describe('FormControlDirective', () => {
|
||||
var controlDir: any /** TODO #9100 */;
|
||||
var control: any /** TODO #9100 */;
|
||||
|
|
|
@ -14,7 +14,7 @@ import {ComponentFixture} from '@angular/core/testing';
|
|||
import {fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {ControlValueAccessor, FORM_DIRECTIVES, FORM_PROVIDERS, FormControl, FormGroup, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NgControl, NgForm, NgModel, REACTIVE_FORM_DIRECTIVES, Validator, Validators, disableDeprecatedForms, provideForms} from '@angular/forms';
|
||||
import {ControlValueAccessor, FORM_DIRECTIVES, FORM_PROVIDERS, FormArray, FormControl, FormGroup, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NgControl, NgForm, NgModel, REACTIVE_FORM_DIRECTIVES, Validator, Validators, disableDeprecatedForms, provideForms} from '@angular/forms';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {dispatchEvent} from '@angular/platform-browser/testing/browser_util';
|
||||
|
@ -68,7 +68,7 @@ export function main() {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should update the control group values on DOM change',
|
||||
it('should update the form group values on DOM change',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
|
@ -221,7 +221,7 @@ export function main() {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should update DOM elements when rebinding the control group',
|
||||
it('should update DOM elements when rebinding the form group',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
|
@ -304,6 +304,77 @@ export function main() {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should support form arrays',
|
||||
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
const cityArray = new FormArray([new FormControl('SF'), new FormControl('NY')]);
|
||||
const form = new FormGroup({cities: cityArray});
|
||||
|
||||
const t = `<div [formGroup]="form">
|
||||
<div formArrayName="cities">
|
||||
<div *ngFor="let city of cityArray.controls; let i=index">
|
||||
<input [formControlName]="i">
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
tcb.overrideTemplate(MyComp8, t)
|
||||
.overrideProviders(MyComp8, providerArr)
|
||||
.createAsync(MyComp8)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.form = form;
|
||||
fixture.debugElement.componentInstance.cityArray = cityArray;
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
const inputs = fixture.debugElement.queryAll(By.css('input'));
|
||||
expect(inputs[0].nativeElement.value).toEqual('SF');
|
||||
expect(inputs[1].nativeElement.value).toEqual('NY');
|
||||
expect(fixture.componentInstance.form.value).toEqual({cities: ['SF', 'NY']});
|
||||
|
||||
inputs[0].nativeElement.value = 'LA';
|
||||
dispatchEvent(inputs[0].nativeElement, 'input');
|
||||
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(fixture.componentInstance.form.value).toEqual({cities: ['LA', 'NY']});
|
||||
|
||||
});
|
||||
})));
|
||||
|
||||
it('should support pushing new controls to form arrays',
|
||||
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
const cityArray = new FormArray([new FormControl('SF'), new FormControl('NY')]);
|
||||
const form = new FormGroup({cities: cityArray});
|
||||
|
||||
const t = `<div [formGroup]="form">
|
||||
<div formArrayName="cities">
|
||||
<div *ngFor="let city of cityArray.controls; let i=index">
|
||||
<input [formControlName]="i">
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
tcb.overrideTemplate(MyComp8, t)
|
||||
.overrideProviders(MyComp8, providerArr)
|
||||
.createAsync(MyComp8)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.form = form;
|
||||
fixture.debugElement.componentInstance.cityArray = cityArray;
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
cityArray.push(new FormControl('LA'));
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
const inputs = fixture.debugElement.queryAll(By.css('input'));
|
||||
expect(inputs[2].nativeElement.value).toEqual('LA');
|
||||
expect(fixture.componentInstance.form.value).toEqual({cities: ['SF', 'NY', 'LA']});
|
||||
|
||||
});
|
||||
})));
|
||||
|
||||
describe('different control types', () => {
|
||||
it('should support <input type=text>',
|
||||
inject(
|
||||
|
|
|
@ -118,6 +118,18 @@ export declare class FormArray extends AbstractControl {
|
|||
removeAt(index: number): void;
|
||||
}
|
||||
|
||||
export declare class FormArrayName extends ControlContainer implements OnInit, OnDestroy {
|
||||
asyncValidator: AsyncValidatorFn;
|
||||
control: FormArray;
|
||||
formDirective: FormGroupDirective;
|
||||
name: string;
|
||||
path: string[];
|
||||
validator: ValidatorFn;
|
||||
constructor(parent: ControlContainer, validators: any[], asyncValidators: any[]);
|
||||
ngOnDestroy(): void;
|
||||
ngOnInit(): void;
|
||||
}
|
||||
|
||||
export declare class FormBuilder {
|
||||
array(controlsConfig: any[], validator?: ValidatorFn, asyncValidator?: AsyncValidatorFn): FormArray;
|
||||
control(value: Object, validator?: ValidatorFn | ValidatorFn[], asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[]): FormControl;
|
||||
|
@ -194,12 +206,15 @@ export declare class FormGroupDirective extends ControlContainer implements Form
|
|||
submitted: boolean;
|
||||
constructor(_validators: any[], _asyncValidators: any[]);
|
||||
addControl(dir: NgControl): void;
|
||||
addFormArray(dir: FormArrayName): void;
|
||||
addFormGroup(dir: FormGroupName): void;
|
||||
getControl(dir: NgControl): FormControl;
|
||||
getFormArray(dir: FormArrayName): FormArray;
|
||||
getFormGroup(dir: FormGroupName): FormGroup;
|
||||
ngOnChanges(changes: SimpleChanges): void;
|
||||
onSubmit(): boolean;
|
||||
removeControl(dir: NgControl): void;
|
||||
removeFormArray(dir: FormArrayName): void;
|
||||
removeFormGroup(dir: FormGroupName): void;
|
||||
updateModel(dir: NgControl, value: any): void;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue