feat(forms): added ng-model
This commit is contained in:
parent
17e1d7f117
commit
559f54e92b
@ -1,6 +1,7 @@
|
|||||||
import {Type, CONST_EXPR} from 'angular2/src/facade/lang';
|
import {Type, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
import {ControlNameDirective} from './directives/control_name_directive';
|
import {ControlNameDirective} from './directives/control_name_directive';
|
||||||
import {FormControlDirective} from './directives/form_control_directive';
|
import {FormControlDirective} from './directives/form_control_directive';
|
||||||
|
import {NgModelDirective} from './directives/ng_model_directive';
|
||||||
import {ControlGroupDirective} from './directives/control_group_directive';
|
import {ControlGroupDirective} from './directives/control_group_directive';
|
||||||
import {FormModelDirective} from './directives/form_model_directive';
|
import {FormModelDirective} from './directives/form_model_directive';
|
||||||
import {TemplateDrivenFormDirective} from './directives/template_driven_form_directive';
|
import {TemplateDrivenFormDirective} from './directives/template_driven_form_directive';
|
||||||
@ -10,6 +11,7 @@ import {SelectControlValueAccessor} from './directives/select_control_value_acce
|
|||||||
|
|
||||||
export {ControlNameDirective} from './directives/control_name_directive';
|
export {ControlNameDirective} from './directives/control_name_directive';
|
||||||
export {FormControlDirective} from './directives/form_control_directive';
|
export {FormControlDirective} from './directives/form_control_directive';
|
||||||
|
export {NgModelDirective} from './directives/ng_model_directive';
|
||||||
export {ControlDirective} from './directives/control_directive';
|
export {ControlDirective} from './directives/control_directive';
|
||||||
export {ControlGroupDirective} from './directives/control_group_directive';
|
export {ControlGroupDirective} from './directives/control_group_directive';
|
||||||
export {FormModelDirective} from './directives/form_model_directive';
|
export {FormModelDirective} from './directives/form_model_directive';
|
||||||
@ -32,6 +34,7 @@ export const formDirectives: List<Type> = CONST_EXPR([
|
|||||||
ControlGroupDirective,
|
ControlGroupDirective,
|
||||||
|
|
||||||
FormControlDirective,
|
FormControlDirective,
|
||||||
|
NgModelDirective,
|
||||||
FormModelDirective,
|
FormModelDirective,
|
||||||
TemplateDrivenFormDirective,
|
TemplateDrivenFormDirective,
|
||||||
|
|
||||||
|
@ -15,7 +15,8 @@ import {ControlValueAccessor} from './control_value_accessor';
|
|||||||
* @exportedAs angular2/forms
|
* @exportedAs angular2/forms
|
||||||
*/
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: 'input[type=checkbox][control],input[type=checkbox][form-control]',
|
selector:
|
||||||
|
'input[type=checkbox][control],input[type=checkbox][form-control],input[type=checkbox][ng-model]',
|
||||||
hostListeners: {'change': 'onChange($event.target.checked)'},
|
hostListeners: {'change': 'onChange($event.target.checked)'},
|
||||||
hostProperties: {'checked': 'checked'}
|
hostProperties: {'checked': 'checked'}
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import {FormDirective} from './form_directive';
|
import {FormDirective} from './form_directive';
|
||||||
import {List} from 'angular2/src/facade/collection';
|
import {List} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A directive that contains a group of [ControlDirective].
|
||||||
|
*
|
||||||
|
* @exportedAs angular2/forms
|
||||||
|
*/
|
||||||
export class ControlContainerDirective {
|
export class ControlContainerDirective {
|
||||||
name: string;
|
name: string;
|
||||||
get formDirective(): FormDirective { return null; }
|
get formDirective(): FormDirective { return null; }
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import {ControlValueAccessor} from './control_value_accessor';
|
import {ControlValueAccessor} from './control_value_accessor';
|
||||||
import {Validators} from '../validators';
|
import {Validators} from '../validators';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A directive that bind a [Control] object to a DOM element.
|
||||||
|
*
|
||||||
|
* @exportedAs angular2/forms
|
||||||
|
*/
|
||||||
export class ControlDirective {
|
export class ControlDirective {
|
||||||
name: string = null;
|
name: string = null;
|
||||||
valueAccessor: ControlValueAccessor = null;
|
valueAccessor: ControlValueAccessor = null;
|
||||||
@ -8,4 +13,6 @@ export class ControlDirective {
|
|||||||
|
|
||||||
get path(): List<string> { return null; }
|
get path(): List<string> { return null; }
|
||||||
constructor() { this.validator = Validators.nullValidator; }
|
constructor() { this.validator = Validators.nullValidator; }
|
||||||
|
|
||||||
|
viewToModelUpdate(newValue: any): void {}
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,8 @@ const controlGroupBinding = CONST_EXPR(
|
|||||||
*
|
*
|
||||||
* # Example
|
* # Example
|
||||||
*
|
*
|
||||||
* In this example, we bind the control group to the form element, and we bind the login and
|
* In this example, we create a control group, and we bind the login and
|
||||||
* password controls to the
|
* password controls to the login and password elements.
|
||||||
* login and password elements.
|
|
||||||
*
|
*
|
||||||
* Here we use {@link formDirectives}, rather than importing each form directive individually, e.g.
|
* Here we use {@link formDirectives}, rather than importing each form directive individually, e.g.
|
||||||
* `ControlDirective`, `ControlGroupDirective`. This is just a shorthand for the same end result.
|
* `ControlDirective`, `ControlGroupDirective`. This is just a shorthand for the same end result.
|
||||||
@ -25,10 +24,13 @@ const controlGroupBinding = CONST_EXPR(
|
|||||||
* @Component({selector: "login-comp"})
|
* @Component({selector: "login-comp"})
|
||||||
* @View({
|
* @View({
|
||||||
* directives: [formDirectives],
|
* directives: [formDirectives],
|
||||||
* template: "<form [control-group]='loginForm'>" +
|
* template:
|
||||||
|
* "<form [form-model]='loginForm'>" +
|
||||||
|
* "<div control-group="credentials">
|
||||||
* "Login <input type='text' control='login'>" +
|
* "Login <input type='text' control='login'>" +
|
||||||
* "Password <input type='password' control='password'>" +
|
* "Password <input type='password' control='password'>" +
|
||||||
* "<button (click)="onLogin()">Login</button>" +
|
* "<button (click)="onLogin()">Login</button>" +
|
||||||
|
* "</div>"
|
||||||
* "</form>"
|
* "</form>"
|
||||||
* })
|
* })
|
||||||
* class LoginComp {
|
* class LoginComp {
|
||||||
@ -36,8 +38,10 @@ const controlGroupBinding = CONST_EXPR(
|
|||||||
*
|
*
|
||||||
* constructor() {
|
* constructor() {
|
||||||
* this.loginForm = new ControlGroup({
|
* this.loginForm = new ControlGroup({
|
||||||
* login: new Control(""),
|
* credentials: new ControlGroup({
|
||||||
* password: new Control("")
|
* login: new Control(""),
|
||||||
|
* password: new Control("")
|
||||||
|
* })
|
||||||
* });
|
* });
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
import {List} from 'angular2/src/facade/collection';
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
import {Directive, Ancestor, onDestroy, onInit} from 'angular2/angular2';
|
import {List, StringMapWrapper, StringMap} from 'angular2/src/facade/collection';
|
||||||
|
import {Directive, Ancestor, onDestroy, onChange} from 'angular2/angular2';
|
||||||
import {FORWARD_REF, Binding, Inject} from 'angular2/di';
|
import {FORWARD_REF, Binding, Inject} from 'angular2/di';
|
||||||
|
|
||||||
import {ControlContainerDirective} from './control_container_directive';
|
import {ControlContainerDirective} from './control_container_directive';
|
||||||
@ -11,11 +12,12 @@ const controlNameBinding =
|
|||||||
CONST_EXPR(new Binding(ControlDirective, {toAlias: FORWARD_REF(() => ControlNameDirective)}));
|
CONST_EXPR(new Binding(ControlDirective, {toAlias: FORWARD_REF(() => ControlNameDirective)}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds a control to a DOM element.
|
* Binds a control with the specified name to a DOM element.
|
||||||
*
|
*
|
||||||
* # Example
|
* # Example
|
||||||
*
|
*
|
||||||
* In this example, we bind the control to an input element. When the value of the input element
|
* In this example, we bind the login control to an input element. When the value of the input
|
||||||
|
* element
|
||||||
* changes, the value of
|
* changes, the value of
|
||||||
* the control will reflect that change. Likewise, if the value of the control changes, the input
|
* the control will reflect that change. Likewise, if the value of the control changes, the input
|
||||||
* element reflects that
|
* element reflects that
|
||||||
@ -28,13 +30,23 @@ const controlNameBinding =
|
|||||||
* @Component({selector: "login-comp"})
|
* @Component({selector: "login-comp"})
|
||||||
* @View({
|
* @View({
|
||||||
* directives: [formDirectives],
|
* directives: [formDirectives],
|
||||||
* template: "<input type='text' [control]='loginControl'>"
|
* template:
|
||||||
|
* "<form [form-model]='loginForm'>" +
|
||||||
|
* "Login <input type='text' control='login'>" +
|
||||||
|
* "<button (click)="onLogin()">Login</button>" +
|
||||||
|
* "</form>"
|
||||||
* })
|
* })
|
||||||
* class LoginComp {
|
* class LoginComp {
|
||||||
* loginControl:Control;
|
* loginForm:ControlGroup;
|
||||||
*
|
*
|
||||||
* constructor() {
|
* constructor() {
|
||||||
* this.loginControl = new Control('');
|
* this.loginForm = new ControlGroup({
|
||||||
|
* login: new Control(""),
|
||||||
|
* });
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* onLogin() {
|
||||||
|
* // this.loginForm.value
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
@ -45,20 +57,37 @@ const controlNameBinding =
|
|||||||
@Directive({
|
@Directive({
|
||||||
selector: '[control]',
|
selector: '[control]',
|
||||||
hostInjector: [controlNameBinding],
|
hostInjector: [controlNameBinding],
|
||||||
properties: ['name: control'],
|
properties: ['name: control', 'model: ng-model'],
|
||||||
lifecycle: [onDestroy, onInit]
|
events: ['ngModel'],
|
||||||
|
lifecycle: [onDestroy, onChange]
|
||||||
})
|
})
|
||||||
export class ControlNameDirective extends ControlDirective {
|
export class ControlNameDirective extends ControlDirective {
|
||||||
_parent: ControlContainerDirective;
|
_parent: ControlContainerDirective;
|
||||||
|
ngModel: EventEmitter;
|
||||||
|
model: any;
|
||||||
|
_added: boolean;
|
||||||
|
|
||||||
constructor(@Ancestor() _parent: ControlContainerDirective) {
|
constructor(@Ancestor() _parent: ControlContainerDirective) {
|
||||||
super();
|
super();
|
||||||
this._parent = _parent;
|
this._parent = _parent;
|
||||||
|
this.ngModel = new EventEmitter();
|
||||||
|
this._added = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onInit() { this.formDirective.addControl(this); }
|
onChange(c: StringMap<string, any>) {
|
||||||
|
if (!this._added) {
|
||||||
|
this.formDirective.addControl(this);
|
||||||
|
this._added = true;
|
||||||
|
}
|
||||||
|
if (StringMapWrapper.contains(c, "model")) {
|
||||||
|
this.formDirective.updateModel(this, this.model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onDestroy() { this.formDirective.removeControl(this); }
|
onDestroy() { this.formDirective.removeControl(this); }
|
||||||
|
|
||||||
|
viewToModelUpdate(newValue: any): void { ObservableWrapper.callNext(this.ngModel, newValue); }
|
||||||
|
|
||||||
get path(): List<string> { return controlPath(this.name, this._parent); }
|
get path(): List<string> { return controlPath(this.name, this._parent); }
|
||||||
|
|
||||||
get formDirective(): any { return this._parent.formDirective; }
|
get formDirective(): any { return this._parent.formDirective; }
|
||||||
|
@ -11,14 +11,15 @@ import {ControlValueAccessor} from './control_value_accessor';
|
|||||||
*
|
*
|
||||||
* # Example
|
* # Example
|
||||||
* ```
|
* ```
|
||||||
* <input type="text" [control]="loginControl">
|
* <input type="text" [form-control]="loginControl">
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @exportedAs angular2/forms
|
* @exportedAs angular2/forms
|
||||||
*/
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector:
|
selector: 'input:not([type=checkbox])[control],textarea[control],' +
|
||||||
'input:not([type=checkbox])[control],textarea[control],input:not([type=checkbox])[form-control],textarea[form-control]',
|
'input:not([type=checkbox])[form-control],textarea[form-control],' +
|
||||||
|
'input:not([type=checkbox])[ng-model],textarea[ng-model]',
|
||||||
hostListeners:
|
hostListeners:
|
||||||
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
|
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
|
||||||
hostProperties: {'value': 'value'}
|
hostProperties: {'value': 'value'}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
|
|
||||||
import {Directive, Ancestor, onChange} from 'angular2/angular2';
|
import {Directive, Ancestor, onChange} from 'angular2/angular2';
|
||||||
import {FORWARD_REF, Binding} from 'angular2/di';
|
import {FORWARD_REF, Binding} from 'angular2/di';
|
||||||
|
|
||||||
@ -9,17 +11,63 @@ import {setUpControl} from './shared';
|
|||||||
const formControlBinding =
|
const formControlBinding =
|
||||||
CONST_EXPR(new Binding(ControlDirective, {toAlias: FORWARD_REF(() => FormControlDirective)}));
|
CONST_EXPR(new Binding(ControlDirective, {toAlias: FORWARD_REF(() => FormControlDirective)}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a control to a DOM element.
|
||||||
|
*
|
||||||
|
* # Example
|
||||||
|
*
|
||||||
|
* In this example, we bind the control to an input element. When the value of the input element
|
||||||
|
* changes, the value of
|
||||||
|
* the control will reflect that change. Likewise, if the value of the control changes, the input
|
||||||
|
* element reflects that
|
||||||
|
* change.
|
||||||
|
*
|
||||||
|
* Here we use {@link formDirectives}, rather than importing each form directive individually, e.g.
|
||||||
|
* `ControlDirective`, `ControlGroupDirective`. This is just a shorthand for the same end result.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Component({selector: "login-comp"})
|
||||||
|
* @View({
|
||||||
|
* directives: [formDirectives],
|
||||||
|
* template: "<input type='text' [form-control]='loginControl'>"
|
||||||
|
* })
|
||||||
|
* class LoginComp {
|
||||||
|
* loginControl:Control;
|
||||||
|
*
|
||||||
|
* constructor() {
|
||||||
|
* this.loginControl = new Control('');
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @exportedAs angular2/forms
|
||||||
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[form-control]',
|
selector: '[form-control]',
|
||||||
hostInjector: [formControlBinding],
|
hostInjector: [formControlBinding],
|
||||||
properties: ['control: form-control'],
|
properties: ['control: form-control', 'model: ng-model'],
|
||||||
|
events: ['ngModel'],
|
||||||
lifecycle: [onChange]
|
lifecycle: [onChange]
|
||||||
})
|
})
|
||||||
export class FormControlDirective extends ControlDirective {
|
export class FormControlDirective extends ControlDirective {
|
||||||
control: Control;
|
control: Control;
|
||||||
|
ngModel: EventEmitter;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.ngModel = new EventEmitter();
|
||||||
|
}
|
||||||
|
|
||||||
onChange(_) {
|
onChange(_) {
|
||||||
setUpControl(this.control, this);
|
setUpControl(this.control, this);
|
||||||
this.control.updateValidity();
|
this.control.updateValidity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set model(value) {
|
||||||
|
this.control.updateValue(value);
|
||||||
|
this.valueAccessor.writeValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
viewToModelUpdate(newValue: any): void { ObservableWrapper.callNext(this.ngModel, newValue); }
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,5 @@ export interface FormDirective {
|
|||||||
removeControl(dir: ControlDirective): void;
|
removeControl(dir: ControlDirective): void;
|
||||||
addControlGroup(dir: ControlGroupDirective): void;
|
addControlGroup(dir: ControlGroupDirective): void;
|
||||||
removeControlGroup(dir: ControlGroupDirective): void;
|
removeControlGroup(dir: ControlGroupDirective): void;
|
||||||
|
updateModel(dir: ControlDirective, value: any): void;
|
||||||
}
|
}
|
@ -6,12 +6,53 @@ import {ControlDirective} from './control_directive';
|
|||||||
import {ControlGroupDirective} from './control_group_directive';
|
import {ControlGroupDirective} from './control_group_directive';
|
||||||
import {ControlContainerDirective} from './control_container_directive';
|
import {ControlContainerDirective} from './control_container_directive';
|
||||||
import {FormDirective} from './form_directive';
|
import {FormDirective} from './form_directive';
|
||||||
import {ControlGroup} from '../model';
|
import {Control, ControlGroup} from '../model';
|
||||||
import {setUpControl} from './shared';
|
import {setUpControl} from './shared';
|
||||||
|
|
||||||
const formDirectiveBinding = CONST_EXPR(
|
const formDirectiveBinding = CONST_EXPR(
|
||||||
new Binding(ControlContainerDirective, {toAlias: FORWARD_REF(() => FormModelDirective)}));
|
new Binding(ControlContainerDirective, {toAlias: FORWARD_REF(() => FormModelDirective)}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a control group to a DOM element.
|
||||||
|
*
|
||||||
|
* # Example
|
||||||
|
*
|
||||||
|
* In this example, we bind the control group to the form element, and we bind the login and
|
||||||
|
* password controls to the
|
||||||
|
* login and password elements.
|
||||||
|
*
|
||||||
|
* Here we use {@link formDirectives}, rather than importing each form directive individually, e.g.
|
||||||
|
* `ControlDirective`, `ControlGroupDirective`. This is just a shorthand for the same end result.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Component({selector: "login-comp"})
|
||||||
|
* @View({
|
||||||
|
* directives: [formDirectives],
|
||||||
|
* template: "<form [form-model]='loginForm'>" +
|
||||||
|
* "Login <input type='text' control='login'>" +
|
||||||
|
* "Password <input type='password' control='password'>" +
|
||||||
|
* "<button (click)="onLogin()">Login</button>" +
|
||||||
|
* "</form>"
|
||||||
|
* })
|
||||||
|
* class LoginComp {
|
||||||
|
* loginForm:ControlGroup;
|
||||||
|
*
|
||||||
|
* constructor() {
|
||||||
|
* this.loginForm = new ControlGroup({
|
||||||
|
* login: new Control(""),
|
||||||
|
* password: new Control("")
|
||||||
|
* });
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* onLogin() {
|
||||||
|
* // this.loginForm.value
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @exportedAs angular2/forms
|
||||||
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[form-model]',
|
selector: '[form-model]',
|
||||||
hostInjector: [formDirectiveBinding],
|
hostInjector: [formDirectiveBinding],
|
||||||
@ -46,6 +87,12 @@ export class FormModelDirective extends ControlContainerDirective implements For
|
|||||||
|
|
||||||
removeControlGroup(dir: ControlGroupDirective) {}
|
removeControlGroup(dir: ControlGroupDirective) {}
|
||||||
|
|
||||||
|
updateModel(dir: ControlDirective, value: any): void {
|
||||||
|
var c = <Control>this.form.find(dir.path);
|
||||||
|
c.value = value;
|
||||||
|
dir.valueAccessor.writeValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
_updateDomValue() {
|
_updateDomValue() {
|
||||||
ListWrapper.forEach(this.directives, dir => {
|
ListWrapper.forEach(this.directives, dir => {
|
||||||
var c: any = this.form.find(dir.path);
|
var c: any = this.form.find(dir.path);
|
||||||
|
51
modules/angular2/src/forms/directives/ng_model_directive.ts
Normal file
51
modules/angular2/src/forms/directives/ng_model_directive.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
|
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
|
import {Directive, Ancestor, onChange} from 'angular2/angular2';
|
||||||
|
import {FORWARD_REF, Binding} from 'angular2/di';
|
||||||
|
|
||||||
|
import {ControlDirective} from './control_directive';
|
||||||
|
import {Control} from '../model';
|
||||||
|
import {setUpControl} from './shared';
|
||||||
|
|
||||||
|
const formControlBinding =
|
||||||
|
CONST_EXPR(new Binding(ControlDirective, {toAlias: FORWARD_REF(() => NgModelDirective)}));
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[ng-model]:not([control]):not([form-control])',
|
||||||
|
hostInjector: [formControlBinding],
|
||||||
|
properties: ['model: ng-model'],
|
||||||
|
events: ['ngModel'],
|
||||||
|
lifecycle: [onChange]
|
||||||
|
})
|
||||||
|
export class NgModelDirective extends ControlDirective {
|
||||||
|
control: Control;
|
||||||
|
ngModel: EventEmitter;
|
||||||
|
model: any;
|
||||||
|
_added: boolean;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.control = new Control("");
|
||||||
|
this.ngModel = new EventEmitter();
|
||||||
|
this._added = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(c) {
|
||||||
|
if (!this._added) {
|
||||||
|
setUpControl(this.control, this);
|
||||||
|
this.control.updateValidity();
|
||||||
|
this._added = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (StringMapWrapper.contains(c, "model")) {
|
||||||
|
this.control.value = this.model;
|
||||||
|
this.valueAccessor.writeValue(this.model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get path(): List<string> { return []; }
|
||||||
|
|
||||||
|
viewToModelUpdate(newValue: any): void { ObservableWrapper.callNext(this.ngModel, newValue); }
|
||||||
|
}
|
@ -17,7 +17,7 @@ import {ControlValueAccessor} from './control_value_accessor';
|
|||||||
* @exportedAs angular2/forms
|
* @exportedAs angular2/forms
|
||||||
*/
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: 'select[control],select[form-control]',
|
selector: 'select[control],select[form-control],select[ng-model]',
|
||||||
hostListeners:
|
hostListeners:
|
||||||
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
|
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
|
||||||
hostProperties: {'value': 'value'}
|
hostProperties: {'value': 'value'}
|
||||||
|
@ -18,7 +18,10 @@ export function setUpControl(c: Control, dir: ControlDirective) {
|
|||||||
|
|
||||||
c.validator = Validators.compose([c.validator, dir.validator]);
|
c.validator = Validators.compose([c.validator, dir.validator]);
|
||||||
dir.valueAccessor.writeValue(c.value);
|
dir.valueAccessor.writeValue(c.value);
|
||||||
dir.valueAccessor.registerOnChange(newValue => c.updateValue(newValue));
|
dir.valueAccessor.registerOnChange(newValue => {
|
||||||
|
dir.viewToModelUpdate(newValue);
|
||||||
|
c.updateValue(newValue);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _throwError(dir: ControlDirective, message: string): void {
|
function _throwError(dir: ControlDirective, message: string): void {
|
||||||
|
@ -62,6 +62,14 @@ export class TemplateDrivenFormDirective extends ControlContainerDirective imple
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateModel(dir: ControlDirective, value: any): void {
|
||||||
|
this._later(_ => {
|
||||||
|
var c = <Control>this.form.find(dir.path);
|
||||||
|
c.value = value;
|
||||||
|
dir.valueAccessor.writeValue(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_findContainer(path: List<string>): ControlGroup {
|
_findContainer(path: List<string>): ControlGroup {
|
||||||
ListWrapper.removeLast(path);
|
ListWrapper.removeLast(path);
|
||||||
return <ControlGroup>this.form.find(path);
|
return <ControlGroup>this.form.find(path);
|
||||||
|
@ -8,11 +8,12 @@ import {
|
|||||||
dispatchEvent,
|
dispatchEvent,
|
||||||
fakeAsync,
|
fakeAsync,
|
||||||
flushMicrotasks,
|
flushMicrotasks,
|
||||||
|
tick,
|
||||||
el,
|
el,
|
||||||
expect,
|
expect,
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
it,
|
it,
|
||||||
|
inject,
|
||||||
|
iit,
|
||||||
xit
|
xit
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
@ -347,6 +348,56 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should support ng-model for complex forms",
|
||||||
|
inject(
|
||||||
|
[TestBed], fakeAsync(tb => {
|
||||||
|
var form = new ControlGroup({"name": new Control("")});
|
||||||
|
var ctx = MyComp.create({name: "oldValue", form: form});
|
||||||
|
|
||||||
|
var t =
|
||||||
|
`<div [form-model]="form"><input type="text" control="name" [(ng-model)]="name"></div>`;
|
||||||
|
|
||||||
|
tb.createView(MyComp, {context: ctx, html: t})
|
||||||
|
.then((view) => {
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
var input = view.querySelector("input");
|
||||||
|
expect(input.value).toEqual("oldValue");
|
||||||
|
|
||||||
|
input.value = "updatedValue";
|
||||||
|
dispatchEvent(input, "change");
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(ctx.name).toEqual("updatedValue");
|
||||||
|
});
|
||||||
|
flushMicrotasks();
|
||||||
|
})));
|
||||||
|
|
||||||
|
it("should support ng-model for single fields",
|
||||||
|
inject([TestBed], fakeAsync(tb => {
|
||||||
|
var form = new Control("");
|
||||||
|
var ctx = MyComp.create({name: "oldValue", form: form});
|
||||||
|
|
||||||
|
var t = `<div><input type="text" [form-control]="form" [(ng-model)]="name"></div>`;
|
||||||
|
|
||||||
|
tb.createView(MyComp, {context: ctx, html: t})
|
||||||
|
.then((view) => {
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
var input = view.querySelector("input");
|
||||||
|
expect(input.value).toEqual("oldValue");
|
||||||
|
|
||||||
|
input.value = "updatedValue";
|
||||||
|
dispatchEvent(input, "change");
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(ctx.name).toEqual("updatedValue");
|
||||||
|
});
|
||||||
|
flushMicrotasks();
|
||||||
|
})));
|
||||||
|
|
||||||
describe("template-driven forms", () => {
|
describe("template-driven forms", () => {
|
||||||
it("should add new controls and control groups",
|
it("should add new controls and control groups",
|
||||||
inject([TestBed], fakeAsync(tb => {
|
inject([TestBed], fakeAsync(tb => {
|
||||||
@ -365,7 +416,7 @@ export function main() {
|
|||||||
view.rawView.elementInjectors[0].get(TemplateDrivenFormDirective);
|
view.rawView.elementInjectors[0].get(TemplateDrivenFormDirective);
|
||||||
expect(form.controls['user']).not.toBeDefined();
|
expect(form.controls['user']).not.toBeDefined();
|
||||||
|
|
||||||
flushMicrotasks();
|
tick();
|
||||||
|
|
||||||
expect(form.controls['user']).toBeDefined();
|
expect(form.controls['user']).toBeDefined();
|
||||||
expect(form.controls['user'].controls['login']).toBeDefined();
|
expect(form.controls['user'].controls['login']).toBeDefined();
|
||||||
@ -388,13 +439,13 @@ export function main() {
|
|||||||
var form = view.rawView.elementInjectors[0].get(
|
var form = view.rawView.elementInjectors[0].get(
|
||||||
TemplateDrivenFormDirective);
|
TemplateDrivenFormDirective);
|
||||||
|
|
||||||
flushMicrotasks();
|
tick();
|
||||||
|
|
||||||
expect(form.controls['login']).toBeDefined();
|
expect(form.controls['login']).toBeDefined();
|
||||||
|
|
||||||
ctx.name = 'hide';
|
ctx.name = 'hide';
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
flushMicrotasks();
|
tick();
|
||||||
|
|
||||||
expect(form.controls['login']).not.toBeDefined();
|
expect(form.controls['login']).not.toBeDefined();
|
||||||
});
|
});
|
||||||
@ -430,6 +481,56 @@ export function main() {
|
|||||||
});
|
});
|
||||||
flushMicrotasks();
|
flushMicrotasks();
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
it("should support ng-model for complex forms",
|
||||||
|
inject([TestBed], fakeAsync(tb => {
|
||||||
|
var ctx = MyComp.create({name: "oldValue"});
|
||||||
|
|
||||||
|
var t = `<div form>
|
||||||
|
<input type="text" control="name" [(ng-model)]="name">
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
tb.createView(MyComp, {context: ctx, html: t})
|
||||||
|
.then((view) => {
|
||||||
|
view.detectChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
var input = view.querySelector("input");
|
||||||
|
expect(input.value).toEqual("oldValue");
|
||||||
|
|
||||||
|
input.value = "updatedValue";
|
||||||
|
dispatchEvent(input, "change");
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(ctx.name).toEqual("updatedValue");
|
||||||
|
});
|
||||||
|
flushMicrotasks();
|
||||||
|
})));
|
||||||
|
|
||||||
|
|
||||||
|
it("should support ng-model for single fields",
|
||||||
|
inject([TestBed], fakeAsync(tb => {
|
||||||
|
var ctx = MyComp.create({name: "oldValue"});
|
||||||
|
|
||||||
|
var t = `<div><input type="text" [(ng-model)]="name"></div>`;
|
||||||
|
|
||||||
|
tb.createView(MyComp, {context: ctx, html: t})
|
||||||
|
.then((view) => {
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
var input = view.querySelector("input");
|
||||||
|
expect(input.value).toEqual("oldValue");
|
||||||
|
|
||||||
|
input.value = "updatedValue";
|
||||||
|
dispatchEvent(input, "change");
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(ctx.name).toEqual("updatedValue");
|
||||||
|
});
|
||||||
|
flushMicrotasks();
|
||||||
|
})));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user