fix(forms): separate ngModelGroup from formGroupName

This commit is contained in:
Kara Erickson 2016-06-12 16:37:42 -07:00
parent bc888bf3a1
commit 5c0cfdee48
14 changed files with 236 additions and 154 deletions

View File

@ -22,15 +22,16 @@ export {CheckboxControlValueAccessor} from './forms/directives/checkbox_value_ac
export {ControlContainer} from './forms/directives/control_container'; export {ControlContainer} from './forms/directives/control_container';
export {ControlValueAccessor, NG_VALUE_ACCESSOR} from './forms/directives/control_value_accessor'; export {ControlValueAccessor, NG_VALUE_ACCESSOR} from './forms/directives/control_value_accessor';
export {DefaultValueAccessor} from './forms/directives/default_value_accessor'; export {DefaultValueAccessor} from './forms/directives/default_value_accessor';
export {FormControlName} from './forms/directives/form_control_name';
export {Form} from './forms/directives/form_interface'; export {Form} from './forms/directives/form_interface';
export {NgControl} from './forms/directives/ng_control'; export {NgControl} from './forms/directives/ng_control';
export {NgControlGroup} from './forms/directives/ng_control_group';
export {NgControlStatus} from './forms/directives/ng_control_status'; export {NgControlStatus} from './forms/directives/ng_control_status';
export {NgForm} from './forms/directives/ng_form'; export {NgForm} from './forms/directives/ng_form';
export {NgModel} from './forms/directives/ng_model'; export {NgModel} from './forms/directives/ng_model';
export {NgModelGroup} from './forms/directives/ng_model_group';
export {FormControlDirective} from './forms/directives/reactive_directives/form_control_directive'; export {FormControlDirective} from './forms/directives/reactive_directives/form_control_directive';
export {FormControlName} from './forms/directives/reactive_directives/form_control_name';
export {FormGroupDirective} from './forms/directives/reactive_directives/form_group_directive'; export {FormGroupDirective} from './forms/directives/reactive_directives/form_group_directive';
export {FormGroupName} from './forms/directives/reactive_directives/form_group_name';
export {NgSelectOption, SelectControlValueAccessor} from './forms/directives/select_control_value_accessor'; export {NgSelectOption, SelectControlValueAccessor} from './forms/directives/select_control_value_accessor';
export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator, Validator} from './forms/directives/validators'; export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator, Validator} from './forms/directives/validators';
export {FormBuilder} from './forms/form_builder'; export {FormBuilder} from './forms/form_builder';

View File

@ -2,15 +2,16 @@ import {Type} from '@angular/core';
import {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor'; import {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
import {DefaultValueAccessor} from './directives/default_value_accessor'; import {DefaultValueAccessor} from './directives/default_value_accessor';
import {FormControlName} from './directives/form_control_name';
import {NgControlGroup} from './directives/ng_control_group';
import {NgControlStatus} from './directives/ng_control_status'; import {NgControlStatus} from './directives/ng_control_status';
import {NgForm} from './directives/ng_form'; import {NgForm} from './directives/ng_form';
import {NgModel} from './directives/ng_model'; import {NgModel} from './directives/ng_model';
import {NgModelGroup} from './directives/ng_model_group';
import {NumberValueAccessor} from './directives/number_value_accessor'; import {NumberValueAccessor} from './directives/number_value_accessor';
import {RadioControlValueAccessor} from './directives/radio_control_value_accessor'; import {RadioControlValueAccessor} from './directives/radio_control_value_accessor';
import {FormControlDirective} from './directives/reactive_directives/form_control_directive'; 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'; import {FormGroupDirective} from './directives/reactive_directives/form_group_directive';
import {FormGroupName} from './directives/reactive_directives/form_group_name';
import {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor'; import {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor';
import {NgSelectMultipleOption, SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor'; import {NgSelectMultipleOption, SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor';
import {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator} from './directives/validators'; import {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator} from './directives/validators';
@ -18,16 +19,17 @@ import {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValida
export {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor'; export {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
export {ControlValueAccessor} from './directives/control_value_accessor'; export {ControlValueAccessor} from './directives/control_value_accessor';
export {DefaultValueAccessor} from './directives/default_value_accessor'; export {DefaultValueAccessor} from './directives/default_value_accessor';
export {FormControlName} from './directives/form_control_name';
export {NgControl} from './directives/ng_control'; export {NgControl} from './directives/ng_control';
export {NgControlGroup} from './directives/ng_control_group';
export {NgControlStatus} from './directives/ng_control_status'; export {NgControlStatus} from './directives/ng_control_status';
export {NgForm} from './directives/ng_form'; export {NgForm} from './directives/ng_form';
export {NgModel} from './directives/ng_model'; export {NgModel} from './directives/ng_model';
export {NgModelGroup} from './directives/ng_model_group';
export {NumberValueAccessor} from './directives/number_value_accessor'; export {NumberValueAccessor} from './directives/number_value_accessor';
export {RadioButtonState, RadioControlValueAccessor} from './directives/radio_control_value_accessor'; export {RadioButtonState, RadioControlValueAccessor} from './directives/radio_control_value_accessor';
export {FormControlDirective} from './directives/reactive_directives/form_control_directive'; 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'; export {FormGroupDirective} from './directives/reactive_directives/form_group_directive';
export {FormGroupName} from './directives/reactive_directives/form_group_name';
export {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor'; export {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor';
export {NgSelectMultipleOption, SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor'; export {NgSelectMultipleOption, SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor';
export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator} from './directives/validators'; export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator} from './directives/validators';
@ -52,9 +54,7 @@ export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValida
* @experimental * @experimental
*/ */
export const FORM_DIRECTIVES: Type[] = /*@ts2dart_const*/[ export const FORM_DIRECTIVES: Type[] = /*@ts2dart_const*/[
NgControlGroup, NgModel, NgModelGroup, NgForm,
NgModel, NgForm,
NgSelectOption, NgSelectMultipleOption, DefaultValueAccessor, NumberValueAccessor, NgSelectOption, NgSelectMultipleOption, DefaultValueAccessor, NumberValueAccessor,
CheckboxControlValueAccessor, SelectControlValueAccessor, SelectMultipleControlValueAccessor, CheckboxControlValueAccessor, SelectControlValueAccessor, SelectMultipleControlValueAccessor,
@ -64,4 +64,4 @@ export const FORM_DIRECTIVES: Type[] = /*@ts2dart_const*/[
]; ];
export const REACTIVE_FORM_DIRECTIVES: Type[] = export const REACTIVE_FORM_DIRECTIVES: Type[] =
/*@ts2dart_const*/[FormControlDirective, FormGroupDirective, FormControlName]; /*@ts2dart_const*/[FormControlDirective, FormGroupDirective, FormControlName, FormGroupName];

View File

@ -0,0 +1,46 @@
import {OnDestroy, OnInit} from '@angular/core';
import {FormGroup} from '../model';
import {ControlContainer} from './control_container';
import {Form} from './form_interface';
import {composeAsyncValidators, composeValidators, controlPath} from './shared';
import {AsyncValidatorFn, ValidatorFn} from './validators';
/**
This is a base class for code shared between {@link NgModelGroup} and {@link FormGroupName}.
*/
export class AbstractFormGroupDirective extends ControlContainer implements OnInit, OnDestroy {
/** @internal */
_parent: ControlContainer;
/** @internal */
_validators: any[];
/** @internal */
_asyncValidators: any[];
ngOnInit(): void { this.formDirective.addFormGroup(this); }
ngOnDestroy(): void { this.formDirective.removeFormGroup(this); }
/**
* Get the {@link FormGroup} backing this binding.
*/
get control(): FormGroup { return this.formDirective.getFormGroup(this); }
/**
* Get the path to this control group.
*/
get path(): string[] { return controlPath(this.name, this._parent); }
/**
* Get the {@link Form} to which this group belongs.
*/
get formDirective(): Form { return this._parent.formDirective; }
get validator(): ValidatorFn { return composeValidators(this._validators); }
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
}

View File

@ -1,7 +1,8 @@
import {FormControl, FormGroup} from '../model'; import {FormControl, FormGroup} from '../model';
import {AbstractFormGroupDirective} from './abstract_form_group_directive';
import {NgControl} from './ng_control'; import {NgControl} from './ng_control';
import {NgControlGroup} from './ng_control_group';
/** /**
@ -30,17 +31,17 @@ export interface Form {
/** /**
* Add a group of controls to this form. * Add a group of controls to this form.
*/ */
addFormGroup(dir: NgControlGroup): void; addFormGroup(dir: AbstractFormGroupDirective): void;
/** /**
* Remove a group of controls from this form. * Remove a group of controls from this form.
*/ */
removeFormGroup(dir: NgControlGroup): void; removeFormGroup(dir: AbstractFormGroupDirective): void;
/** /**
* Look up the {@link FormGroup} associated with a particular {@link NgControlGroup}. * Look up the {@link FormGroup} associated with a particular {@link AbstractFormGroupDirective}.
*/ */
getFormGroup(dir: NgControlGroup): FormGroup; getFormGroup(dir: AbstractFormGroupDirective): FormGroup;
/** /**
* Update the model for a particular control with a new value. * Update the model for a particular control with a new value.

View File

@ -1,102 +0,0 @@
import {Directive, Host, Inject, OnDestroy, OnInit, Optional, Self, SkipSelf, forwardRef} from '@angular/core';
import {FormGroup} from '../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
import {ControlContainer} from './control_container';
import {Form} from './form_interface';
import {composeAsyncValidators, composeValidators, controlPath} from './shared';
import {AsyncValidatorFn, ValidatorFn} from './validators';
export const controlGroupProvider: any =
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
provide: ControlContainer,
useExisting: forwardRef(() => NgControlGroup)
};
/**
* Creates and binds a control group to a DOM element.
*
* This directive can only be used as a child of {@link NgForm} or {@link FormGroupDirective}.
*
* ```typescript
* @Component({
* selector: 'my-app',
* template: `
* <div>
* <h2>Angular FormControl &amp; FormGroup Example</h2>
* <form #f="ngForm">
* <div ngControlGroup="name" #cgName="ngForm">
* <h3>Enter your name:</h3>
* <p>First: <input name="first" ngModel required></p>
* <p>Middle: <input name="middle" ngModel></p>
* <p>Last: <input name="last" ngModel required></p>
* </div>
* <h3>Name value:</h3>
* <pre>{{valueOf(cgName)}}</pre>
* <p>Name is {{cgName?.control?.valid ? "valid" : "invalid"}}</p>
* <h3>What's your favorite food?</h3>
* <p><input name="food" ngModel></p>
* <h3>Form value</h3>
* <pre>{{valueOf(f)}}</pre>
* </form>
* </div>
* `
* })
* export class App {
* valueOf(cg: NgControlGroup): string {
* if (cg.control == null) {
* return null;
* }
* return JSON.stringify(cg.control.value, null, 2);
* }
* }
* ```
*
* This example declares a control group for a user's name. The value and validation state of
* this group can be accessed separately from the overall form.
*
* @experimental
*/
@Directive({
selector: '[ngControlGroup]',
providers: [controlGroupProvider],
inputs: ['name: ngControlGroup'],
exportAs: 'ngForm'
})
export class NgControlGroup extends ControlContainer implements OnInit,
OnDestroy {
/** @internal */
_parent: ControlContainer;
constructor(
@Host() @SkipSelf() parent: ControlContainer,
@Optional() @Self() @Inject(NG_VALIDATORS) private _validators: any[],
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators: any[]) {
super();
this._parent = parent;
}
ngOnInit(): void { this.formDirective.addFormGroup(this); }
ngOnDestroy(): void { this.formDirective.removeFormGroup(this); }
/**
* Get the {@link FormGroup} backing this binding.
*/
get control(): FormGroup { return this.formDirective.getFormGroup(this); }
/**
* Get the path to this control group.
*/
get path(): string[] { return controlPath(this.name, this._parent); }
/**
* Get the {@link Form} to which this group belongs.
*/
get formDirective(): Form { return this._parent.formDirective; }
get validator(): ValidatorFn { return composeValidators(this._validators); }
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
}

View File

@ -9,7 +9,7 @@ import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
import {ControlContainer} from './control_container'; import {ControlContainer} from './control_container';
import {Form} from './form_interface'; import {Form} from './form_interface';
import {NgControl} from './ng_control'; import {NgControl} from './ng_control';
import {NgControlGroup} from './ng_control_group'; import {NgModelGroup} from './ng_model_group';
import {composeAsyncValidators, composeValidators, setUpControl, setUpFormGroup} from './shared'; import {composeAsyncValidators, composeValidators, setUpControl, setUpFormGroup} from './shared';
export const formDirectiveProvider: any = export const formDirectiveProvider: any =
@ -44,12 +44,12 @@ export const formDirectiveProvider: any =
* <h2>NgForm demo</h2> * <h2>NgForm demo</h2>
* <form #f="ngForm" (ngSubmit)="onSubmit(f.value)"> * <form #f="ngForm" (ngSubmit)="onSubmit(f.value)">
* <h3>Control group: credentials</h3> * <h3>Control group: credentials</h3>
* <div ngControlGroup="credentials"> * <div ngModelGroup="credentials">
* <p>Login: <input type="text" name="login" ngModel></p> * <p>Login: <input type="text" name="login" ngModel></p>
* <p>Password: <input type="password" name="password" ngModel></p> * <p>Password: <input type="password" name="password" ngModel></p>
* </div> * </div>
* <h3>Control group: person</h3> * <h3>Control group: person</h3>
* <div ngControlGroup="person"> * <div ngModelGroup="person">
* <p>First name: <input type="text" name="firstName" ngModel></p> * <p>First name: <input type="text" name="firstName" ngModel></p>
* <p>Last name: <input type="text" name="lastName" ngModel></p> * <p>Last name: <input type="text" name="lastName" ngModel></p>
* </div> * </div>
@ -129,7 +129,7 @@ export class NgForm extends ControlContainer implements Form {
}); });
} }
addFormGroup(dir: NgControlGroup): void { addFormGroup(dir: NgModelGroup): void {
PromiseWrapper.scheduleMicrotask(() => { PromiseWrapper.scheduleMicrotask(() => {
var container = this._findContainer(dir.path); var container = this._findContainer(dir.path);
var group = new FormGroup({}); var group = new FormGroup({});
@ -139,7 +139,7 @@ export class NgForm extends ControlContainer implements Form {
}); });
} }
removeFormGroup(dir: NgControlGroup): void { removeFormGroup(dir: NgModelGroup): void {
PromiseWrapper.scheduleMicrotask(() => { PromiseWrapper.scheduleMicrotask(() => {
var container = this._findContainer(dir.path); var container = this._findContainer(dir.path);
if (isPresent(container)) { if (isPresent(container)) {
@ -148,7 +148,7 @@ export class NgForm extends ControlContainer implements Form {
}); });
} }
getFormGroup(dir: NgControlGroup): FormGroup { return <FormGroup>this.form.find(dir.path); } getFormGroup(dir: NgModelGroup): FormGroup { return <FormGroup>this.form.find(dir.path); }
updateModel(dir: NgControl, value: any): void { updateModel(dir: NgControl, value: any): void {
PromiseWrapper.scheduleMicrotask(() => { PromiseWrapper.scheduleMicrotask(() => {

View File

@ -0,0 +1,64 @@
import {Directive, Host, Inject, Input, OnDestroy, OnInit, Optional, Self, SkipSelf, forwardRef} from '@angular/core';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
import {AbstractFormGroupDirective} from './abstract_form_group_directive';
import {ControlContainer} from './control_container';
export const modelGroupProvider: any =
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
provide: ControlContainer,
useExisting: forwardRef(() => NgModelGroup)
};
/**
* Creates and binds a model group to a DOM element.
*
* This directive can only be used as a child of {@link NgForm}.
*
* ```typescript
* @Component({
* selector: 'my-app',
* template: `
* <div>
* <h2>Angular forms Example</h2>
* <form #f="ngForm">
* <div ngModelGroup="name" #mgName="ngModelGroup">
* <h3>Enter your name:</h3>
* <p>First: <input name="first" ngModel required></p>
* <p>Middle: <input name="middle" ngModel></p>
* <p>Last: <input name="last" ngModel required></p>
* </div>
* <h3>Name value:</h3>
* <pre>{{ mgName | json }}</pre>
* <p>Name is {{mgName?.valid ? "valid" : "invalid"}}</p>
* <h3>What's your favorite food?</h3>
* <p><input name="food" ngModel></p>
* <h3>Form value</h3>
* <pre>{{ f | json }}</pre>
* </form>
* </div>
* `
* })
* export class App {}
* ```
*
* This example declares a model group for a user's name. The value and validation state of
* this group can be accessed separately from the overall form.
*
* @experimental
*/
@Directive({selector: '[ngModelGroup]', providers: [modelGroupProvider], exportAs: 'ngModelGroup'})
export class NgModelGroup extends AbstractFormGroupDirective implements OnInit, OnDestroy {
@Input('ngModelGroup') 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;
}
}

View File

@ -1,14 +1,14 @@
import {Directive, Host, Inject, Input, OnChanges, OnDestroy, Optional, Output, Self, SimpleChanges, SkipSelf, forwardRef} from '@angular/core'; import {Directive, Host, Inject, Input, OnChanges, OnDestroy, Optional, Output, Self, SimpleChanges, SkipSelf, forwardRef} from '@angular/core';
import {EventEmitter, ObservableWrapper} from '../../facade/async'; import {EventEmitter, ObservableWrapper} from '../../../facade/async';
import {FormControl} from '../model'; import {FormControl} from '../../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators';
import {ControlContainer} from './control_container'; import {ControlContainer} from '../control_container';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '../control_value_accessor';
import {NgControl} from './ng_control'; import {NgControl} from '../ng_control';
import {composeAsyncValidators, composeValidators, controlPath, isPropertyUpdated, selectValueAccessor} from './shared'; import {composeAsyncValidators, composeValidators, controlPath, isPropertyUpdated, selectValueAccessor} from '../shared';
import {AsyncValidatorFn, ValidatorFn} from './validators'; import {AsyncValidatorFn, ValidatorFn} from '../validators';
export const controlNameBinding: any = export const controlNameBinding: any =
@ -18,9 +18,9 @@ export const controlNameBinding: any =
}; };
/** /**
* Creates and binds a form control with a specified name to a DOM element. * Syncs an existing form control with the specified name to a DOM element.
* *
* This directive can only be used as a child of {@link NgForm} or {@link FormGroupDirective}. * This directive can only be used as a child of {@link FormGroupDirective}.
* ### Example * ### Example
* *

View File

@ -6,13 +6,13 @@ import {BaseException} from '../../../facade/exceptions';
import {isBlank} from '../../../facade/lang'; import {isBlank} from '../../../facade/lang';
import {FormControl, FormGroup} from '../../model'; import {FormControl, FormGroup} from '../../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from '../../validators'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from '../../validators';
import {ControlContainer} from '../control_container'; import {ControlContainer} from '../control_container';
import {Form} from '../form_interface'; import {Form} from '../form_interface';
import {NgControl} from '../ng_control'; import {NgControl} from '../ng_control';
import {NgControlGroup} from '../ng_control_group';
import {composeAsyncValidators, composeValidators, setUpControl, setUpFormGroup} from '../shared'; import {composeAsyncValidators, composeValidators, setUpControl, setUpFormGroup} from '../shared';
import {FormGroupName} from './form_group_name';
export const formDirectiveProvider: any = export const formDirectiveProvider: any =
/*@ts2dart_const*/ /* @ts2dart_Provider */ { /*@ts2dart_const*/ /* @ts2dart_Provider */ {
provide: ControlContainer, provide: ControlContainer,
@ -148,15 +148,15 @@ export class FormGroupDirective extends ControlContainer implements Form,
removeControl(dir: NgControl): void { ListWrapper.remove(this.directives, dir); } removeControl(dir: NgControl): void { ListWrapper.remove(this.directives, dir); }
addFormGroup(dir: NgControlGroup) { addFormGroup(dir: FormGroupName) {
var ctrl: any = this.form.find(dir.path); var ctrl: any = this.form.find(dir.path);
setUpFormGroup(ctrl, dir); setUpFormGroup(ctrl, dir);
ctrl.updateValueAndValidity({emitEvent: false}); ctrl.updateValueAndValidity({emitEvent: false});
} }
removeFormGroup(dir: NgControlGroup) {} removeFormGroup(dir: FormGroupName) {}
getFormGroup(dir: NgControlGroup): FormGroup { return <FormGroup>this.form.find(dir.path); } getFormGroup(dir: FormGroupName): FormGroup { return <FormGroup>this.form.find(dir.path); }
updateModel(dir: NgControl, value: any): void { updateModel(dir: NgControl, value: any): void {
var ctrl  = <FormControl>this.form.find(dir.path); var ctrl  = <FormControl>this.form.find(dir.path);

View File

@ -0,0 +1,73 @@
import {Directive, Host, Inject, Input, OnDestroy, OnInit, Optional, Self, SkipSelf, forwardRef} from '@angular/core';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators';
import {AbstractFormGroupDirective} from '../abstract_form_group_directive';
import {ControlContainer} from '../control_container';
export const formGroupNameProvider: any =
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
provide: ControlContainer,
useExisting: forwardRef(() => FormGroupName)
};
/**
* Syncs an existing form group 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 FormGroup Example</h2>
* <form [formGroup]="myForm">
* <div formGroupName="name">
* <h3>Enter your name:</h3>
* <p>First: <input formControlName="first"></p>
* <p>Middle: <input formControlName="middle"></p>
* <p>Last: <input formControlName="last"></p>
* </div>
* <h3>Name value:</h3>
* <pre>{{ nameGroup | json }}</pre>
* <p>Name is {{nameGroup?.valid ? "valid" : "invalid"}}</p>
* <h3>What's your favorite food?</h3>
* <p><input formControlName="food"></p>
* <h3>Form value</h3>
* <pre> {{ myForm | json }} </pre>
* </form>
* </div>
* `
* })
* export class App {
* nameGroup = new FormGroup({
* first: new FormControl('', Validators.required),
* middle: new FormControl(''),
* last: new FormControl('', Validators.required)
* });
*
* myForm = new FormGroup({
* name: this.nameGroup,
* food: new FormControl()
* });
* }
* ```
*
* This example syncs the form group for the user's name. The value and validation state of
* this group can be accessed separately from the overall form.
*
* @experimental
*/
@Directive({selector: '[formGroupName]', providers: [formGroupNameProvider]})
export class FormGroupName extends AbstractFormGroupDirective implements OnInit, OnDestroy {
@Input('formGroupName') 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;
}
}

View File

@ -5,12 +5,12 @@ import {FormControl, FormGroup} from '../model';
import {Validators} from '../validators'; import {Validators} from '../validators';
import {AbstractControlDirective} from './abstract_control_directive'; import {AbstractControlDirective} from './abstract_control_directive';
import {AbstractFormGroupDirective} from './abstract_form_group_directive';
import {CheckboxControlValueAccessor} from './checkbox_value_accessor'; import {CheckboxControlValueAccessor} from './checkbox_value_accessor';
import {ControlContainer} from './control_container'; import {ControlContainer} from './control_container';
import {ControlValueAccessor} from './control_value_accessor'; import {ControlValueAccessor} from './control_value_accessor';
import {DefaultValueAccessor} from './default_value_accessor'; import {DefaultValueAccessor} from './default_value_accessor';
import {NgControl} from './ng_control'; import {NgControl} from './ng_control';
import {NgControlGroup} from './ng_control_group';
import {normalizeAsyncValidator, normalizeValidator} from './normalize_validator'; import {normalizeAsyncValidator, normalizeValidator} from './normalize_validator';
import {NumberValueAccessor} from './number_value_accessor'; import {NumberValueAccessor} from './number_value_accessor';
import {RadioControlValueAccessor} from './radio_control_value_accessor'; import {RadioControlValueAccessor} from './radio_control_value_accessor';
@ -46,7 +46,7 @@ export function setUpControl(control: FormControl, dir: NgControl): void {
dir.valueAccessor.registerOnTouched(() => control.markAsTouched()); dir.valueAccessor.registerOnTouched(() => control.markAsTouched());
} }
export function setUpFormGroup(control: FormGroup, dir: NgControlGroup) { export function setUpFormGroup(control: FormGroup, dir: AbstractFormGroupDirective) {
if (isBlank(control)) _throwError(dir, 'Cannot find control'); if (isBlank(control)) _throwError(dir, 'Cannot find control');
control.validator = Validators.compose([control.validator, dir.validator]); control.validator = Validators.compose([control.validator, dir.validator]);
control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]); control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]);

View File

@ -17,7 +17,7 @@ import * as modelModule from './model';
* template: ` * template: `
* <form [formGroup]="loginForm"> * <form [formGroup]="loginForm">
* <p>Login <input formControlName="login"></p> * <p>Login <input formControlName="login"></p>
* <div ngControlGroup="passwordRetry"> * <div formGroupName="passwordRetry">
* <p>Password <input type="password" formControlName="password"></p> * <p>Password <input type="password" formControlName="password"></p>
* <p>Confirm password <input type="password" formControlName="passwordConfirmation"></p> * <p>Confirm password <input type="password" formControlName="passwordConfirmation"></p>
* </div> * </div>

View File

@ -4,8 +4,7 @@ import {fakeAsync, flushMicrotasks, Log, tick,} from '@angular/core/testing';
import {SpyNgControl, SpyValueAccessor} from '../spies'; import {SpyNgControl, SpyValueAccessor} from '../spies';
import {FormGroup, FormControl, FormControlName, NgControlGroup, FormGroupDirective, ControlValueAccessor, Validators, NgForm, NgModel, FormControlDirective, NgControl, DefaultValueAccessor, CheckboxControlValueAccessor, SelectControlValueAccessor, Validator} from '@angular/common/src/forms'; import {FormGroup, FormControl, FormControlName, FormGroupName, NgModelGroup, FormGroupDirective, ControlValueAccessor, Validators, NgForm, NgModel, FormControlDirective, NgControl, DefaultValueAccessor, CheckboxControlValueAccessor, SelectControlValueAccessor, Validator} from '@angular/common/src/forms';
import {selectValueAccessor, composeValidators} from '@angular/common/src/forms/directives/shared'; import {selectValueAccessor, composeValidators} from '@angular/common/src/forms/directives/shared';
import {TimerWrapper} from '../../src/facade/async'; import {TimerWrapper} from '../../src/facade/async';
@ -195,7 +194,7 @@ export function main() {
}; };
it('should set up validator', fakeAsync(() => { it('should set up validator', fakeAsync(() => {
var group = new NgControlGroup( var group = new FormGroupName(
form, [matchingPasswordsValidator], [asyncValidator('expected')]); form, [matchingPasswordsValidator], [asyncValidator('expected')]);
group.name = 'passwords'; group.name = 'passwords';
form.addFormGroup(group); form.addFormGroup(group);
@ -271,7 +270,7 @@ export function main() {
form = new NgForm([], []); form = new NgForm([], []);
formModel = form.form; formModel = form.form;
personControlGroupDir = new NgControlGroup(form, [], []); personControlGroupDir = new NgModelGroup(form, [], []);
personControlGroupDir.name = 'person'; personControlGroupDir.name = 'person';
loginControlDir = new FormControlName(personControlGroupDir, null, null, [defaultAccessor]); loginControlDir = new FormControlName(personControlGroupDir, null, null, [defaultAccessor]);
@ -338,7 +337,7 @@ export function main() {
})); }));
}); });
describe('NgControlGroup', () => { describe('FormGroupName', () => {
var formModel: any /** TODO #9100 */; var formModel: any /** TODO #9100 */;
var controlGroupDir: any /** TODO #9100 */; var controlGroupDir: any /** TODO #9100 */;
@ -347,7 +346,7 @@ export function main() {
var parent = new FormGroupDirective([], []); var parent = new FormGroupDirective([], []);
parent.form = new FormGroup({'group': formModel}); parent.form = new FormGroup({'group': formModel});
controlGroupDir = new NgControlGroup(parent, [], []); controlGroupDir = new FormGroupName(parent, [], []);
controlGroupDir.name = 'group'; controlGroupDir.name = 'group';
}); });

View File

@ -991,7 +991,7 @@ export function main() {
new FormGroup({'nested': new FormGroup({'login': new FormControl('value')})}); new FormGroup({'nested': new FormGroup({'login': new FormControl('value')})});
const t = `<div [formGroup]="form"> const t = `<div [formGroup]="form">
<div ngControlGroup="nested"> <div formGroupName="nested">
<input type="text" formControlName="login"> <input type="text" formControlName="login">
</div> </div>
</div>`; </div>`;
@ -1014,7 +1014,7 @@ export function main() {
new FormGroup({'nested': new FormGroup({'login': new FormControl('value')})}); new FormGroup({'nested': new FormGroup({'login': new FormControl('value')})});
const t = `<div [formGroup]="form"> const t = `<div [formGroup]="form">
<div ngControlGroup="nested"> <div formGroupName="nested">
<input type="text" formControlName="login"> <input type="text" formControlName="login">
</div> </div>
</div>`; </div>`;
@ -1083,8 +1083,8 @@ export function main() {
it('should add new controls and control groups', it('should add new controls and control groups',
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
const t = `<form> const t = `<form>
<div ngControlGroup="user"> <div ngModelGroup="user">
<input type="text" formControlName="login"> <input type="text" name="login" ngModel>
</div> </div>
</form>`; </form>`;
@ -1137,7 +1137,7 @@ export function main() {
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
const t = `<form> const t = `<form>
<div *ngIf="name == 'show'"> <div *ngIf="name == 'show'">
<input type="text" formControlName="login"> <input type="text" name="login" ngModel>
</div> </div>
</form>`; </form>`;
@ -1161,7 +1161,7 @@ export function main() {
it('should remove control groups', it('should remove control groups',
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
const t = `<form> const t = `<form>
<div *ngIf="name=='show'" ngControlGroup="user"> <div *ngIf="name=='show'" ngModelGroup="user">
<input type="text" name="login" ngModel> <input type="text" name="login" ngModel>
</div> </div>
</form>`; </form>`;
@ -1492,7 +1492,7 @@ export function main() {
// {{x.valid}} used to crash because valid() tried to read a property // {{x.valid}} used to crash because valid() tried to read a property
// from form.control before it was set. This test verifies this bug is // from form.control before it was set. This test verifies this bug is
// fixed. // fixed.
const t = `<form><div ngControlGroup="x" #x="ngForm"> const t = `<form><div ngModelGroup="x" #x="ngModelGroup">
<input type="text" name="test" ngModel></div>{{x.valid}}</form>`; <input type="text" name="test" ngModel></div>{{x.valid}}</form>`;
let fixture = tcb.overrideTemplate(MyComp8, t).createFakeAsync(MyComp8); let fixture = tcb.overrideTemplate(MyComp8, t).createFakeAsync(MyComp8);
tick(); tick();