Skip to content

Commit 559f54e

Browse files
committed
feat(forms): added ng-model
1 parent 17e1d7f commit 559f54e

15 files changed

+338
-29
lines changed

modules/angular2/src/forms/directives.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {Type, CONST_EXPR} from 'angular2/src/facade/lang';
22
import {ControlNameDirective} from './directives/control_name_directive';
33
import {FormControlDirective} from './directives/form_control_directive';
4+
import {NgModelDirective} from './directives/ng_model_directive';
45
import {ControlGroupDirective} from './directives/control_group_directive';
56
import {FormModelDirective} from './directives/form_model_directive';
67
import {TemplateDrivenFormDirective} from './directives/template_driven_form_directive';
@@ -10,6 +11,7 @@ import {SelectControlValueAccessor} from './directives/select_control_value_acce
1011

1112
export {ControlNameDirective} from './directives/control_name_directive';
1213
export {FormControlDirective} from './directives/form_control_directive';
14+
export {NgModelDirective} from './directives/ng_model_directive';
1315
export {ControlDirective} from './directives/control_directive';
1416
export {ControlGroupDirective} from './directives/control_group_directive';
1517
export {FormModelDirective} from './directives/form_model_directive';
@@ -32,6 +34,7 @@ export const formDirectives: List<Type> = CONST_EXPR([
3234
ControlGroupDirective,
3335

3436
FormControlDirective,
37+
NgModelDirective,
3538
FormModelDirective,
3639
TemplateDrivenFormDirective,
3740

modules/angular2/src/forms/directives/checkbox_value_accessor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import {ControlValueAccessor} from './control_value_accessor';
1515
* @exportedAs angular2/forms
1616
*/
1717
@Directive({
18-
selector: 'input[type=checkbox][control],input[type=checkbox][form-control]',
18+
selector:
19+
'input[type=checkbox][control],input[type=checkbox][form-control],input[type=checkbox][ng-model]',
1920
hostListeners: {'change': 'onChange($event.target.checked)'},
2021
hostProperties: {'checked': 'checked'}
2122
})

modules/angular2/src/forms/directives/control_container_directive.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import {FormDirective} from './form_directive';
22
import {List} from 'angular2/src/facade/collection';
33

4+
/**
5+
* A directive that contains a group of [ControlDirective].
6+
*
7+
* @exportedAs angular2/forms
8+
*/
49
export class ControlContainerDirective {
510
name: string;
611
get formDirective(): FormDirective { return null; }
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import {ControlValueAccessor} from './control_value_accessor';
22
import {Validators} from '../validators';
33

4+
/**
5+
* A directive that bind a [Control] object to a DOM element.
6+
*
7+
* @exportedAs angular2/forms
8+
*/
49
export class ControlDirective {
510
name: string = null;
611
valueAccessor: ControlValueAccessor = null;
712
validator: Function;
813

914
get path(): List<string> { return null; }
1015
constructor() { this.validator = Validators.nullValidator; }
16+
17+
viewToModelUpdate(newValue: any): void {}
1118
}

modules/angular2/src/forms/directives/control_group_directive.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ const controlGroupBinding = CONST_EXPR(
1414
*
1515
* # Example
1616
*
17-
* In this example, we bind the control group to the form element, and we bind the login and
18-
* password controls to the
19-
* login and password elements.
17+
* In this example, we create a control group, and we bind the login and
18+
* password controls to the login and password elements.
2019
*
2120
* Here we use {@link formDirectives}, rather than importing each form directive individually, e.g.
2221
* `ControlDirective`, `ControlGroupDirective`. This is just a shorthand for the same end result.
@@ -25,19 +24,24 @@ const controlGroupBinding = CONST_EXPR(
2524
* @Component({selector: "login-comp"})
2625
* @View({
2726
* directives: [formDirectives],
28-
* template: "<form [control-group]='loginForm'>" +
27+
* template:
28+
* "<form [form-model]='loginForm'>" +
29+
* "<div control-group="credentials">
2930
* "Login <input type='text' control='login'>" +
3031
* "Password <input type='password' control='password'>" +
3132
* "<button (click)="onLogin()">Login</button>" +
33+
* "</div>"
3234
* "</form>"
3335
* })
3436
* class LoginComp {
3537
* loginForm:ControlGroup;
3638
*
3739
* constructor() {
3840
* this.loginForm = new ControlGroup({
39-
* login: new Control(""),
40-
* password: new Control("")
41+
* credentials: new ControlGroup({
42+
* login: new Control(""),
43+
* password: new Control("")
44+
* })
4145
* });
4246
* }
4347
*

modules/angular2/src/forms/directives/control_name_directive.ts

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {CONST_EXPR} from 'angular2/src/facade/lang';
2-
import {List} from 'angular2/src/facade/collection';
3-
import {Directive, Ancestor, onDestroy, onInit} from 'angular2/angular2';
2+
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
3+
import {List, StringMapWrapper, StringMap} from 'angular2/src/facade/collection';
4+
import {Directive, Ancestor, onDestroy, onChange} from 'angular2/angular2';
45
import {FORWARD_REF, Binding, Inject} from 'angular2/di';
56

67
import {ControlContainerDirective} from './control_container_directive';
@@ -11,11 +12,12 @@ const controlNameBinding =
1112
CONST_EXPR(new Binding(ControlDirective, {toAlias: FORWARD_REF(() => ControlNameDirective)}));
1213

1314
/**
14-
* Binds a control to a DOM element.
15+
* Binds a control with the specified name to a DOM element.
1516
*
1617
* # Example
1718
*
18-
* In this example, we bind the control to an input element. When the value of the input element
19+
* In this example, we bind the login control to an input element. When the value of the input
20+
* element
1921
* changes, the value of
2022
* the control will reflect that change. Likewise, if the value of the control changes, the input
2123
* element reflects that
@@ -28,13 +30,23 @@ const controlNameBinding =
2830
* @Component({selector: "login-comp"})
2931
* @View({
3032
* directives: [formDirectives],
31-
* template: "<input type='text' [control]='loginControl'>"
33+
* template:
34+
* "<form [form-model]='loginForm'>" +
35+
* "Login <input type='text' control='login'>" +
36+
* "<button (click)="onLogin()">Login</button>" +
37+
* "</form>"
3238
* })
3339
* class LoginComp {
34-
* loginControl:Control;
40+
* loginForm:ControlGroup;
3541
*
3642
* constructor() {
37-
* this.loginControl = new Control('');
43+
* this.loginForm = new ControlGroup({
44+
* login: new Control(""),
45+
* });
46+
* }
47+
*
48+
* onLogin() {
49+
* // this.loginForm.value
3850
* }
3951
* }
4052
*
@@ -45,20 +57,37 @@ const controlNameBinding =
4557
@Directive({
4658
selector: '[control]',
4759
hostInjector: [controlNameBinding],
48-
properties: ['name: control'],
49-
lifecycle: [onDestroy, onInit]
60+
properties: ['name: control', 'model: ng-model'],
61+
events: ['ngModel'],
62+
lifecycle: [onDestroy, onChange]
5063
})
5164
export class ControlNameDirective extends ControlDirective {
5265
_parent: ControlContainerDirective;
66+
ngModel: EventEmitter;
67+
model: any;
68+
_added: boolean;
69+
5370
constructor(@Ancestor() _parent: ControlContainerDirective) {
5471
super();
5572
this._parent = _parent;
73+
this.ngModel = new EventEmitter();
74+
this._added = false;
5675
}
5776

58-
onInit() { this.formDirective.addControl(this); }
77+
onChange(c: StringMap<string, any>) {
78+
if (!this._added) {
79+
this.formDirective.addControl(this);
80+
this._added = true;
81+
}
82+
if (StringMapWrapper.contains(c, "model")) {
83+
this.formDirective.updateModel(this, this.model);
84+
}
85+
}
5986

6087
onDestroy() { this.formDirective.removeControl(this); }
6188

89+
viewToModelUpdate(newValue: any): void { ObservableWrapper.callNext(this.ngModel, newValue); }
90+
6291
get path(): List<string> { return controlPath(this.name, this._parent); }
6392

6493
get formDirective(): any { return this._parent.formDirective; }

modules/angular2/src/forms/directives/default_value_accessor.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@ import {ControlValueAccessor} from './control_value_accessor';
1111
*
1212
* # Example
1313
* ```
14-
* <input type="text" [control]="loginControl">
14+
* <input type="text" [form-control]="loginControl">
1515
* ```
1616
*
1717
* @exportedAs angular2/forms
1818
*/
1919
@Directive({
20-
selector:
21-
'input:not([type=checkbox])[control],textarea[control],input:not([type=checkbox])[form-control],textarea[form-control]',
20+
selector: 'input:not([type=checkbox])[control],textarea[control],' +
21+
'input:not([type=checkbox])[form-control],textarea[form-control],' +
22+
'input:not([type=checkbox])[ng-model],textarea[ng-model]',
2223
hostListeners:
2324
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
2425
hostProperties: {'value': 'value'}

modules/angular2/src/forms/directives/form_control_directive.ts

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import {CONST_EXPR} from 'angular2/src/facade/lang';
2+
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
3+
24
import {Directive, Ancestor, onChange} from 'angular2/angular2';
35
import {FORWARD_REF, Binding} from 'angular2/di';
46

@@ -9,17 +11,63 @@ import {setUpControl} from './shared';
911
const formControlBinding =
1012
CONST_EXPR(new Binding(ControlDirective, {toAlias: FORWARD_REF(() => FormControlDirective)}));
1113

14+
/**
15+
* Binds a control to a DOM element.
16+
*
17+
* # Example
18+
*
19+
* In this example, we bind the control to an input element. When the value of the input element
20+
* changes, the value of
21+
* the control will reflect that change. Likewise, if the value of the control changes, the input
22+
* element reflects that
23+
* change.
24+
*
25+
* Here we use {@link formDirectives}, rather than importing each form directive individually, e.g.
26+
* `ControlDirective`, `ControlGroupDirective`. This is just a shorthand for the same end result.
27+
*
28+
* ```
29+
* @Component({selector: "login-comp"})
30+
* @View({
31+
* directives: [formDirectives],
32+
* template: "<input type='text' [form-control]='loginControl'>"
33+
* })
34+
* class LoginComp {
35+
* loginControl:Control;
36+
*
37+
* constructor() {
38+
* this.loginControl = new Control('');
39+
* }
40+
* }
41+
*
42+
* ```
43+
*
44+
* @exportedAs angular2/forms
45+
*/
1246
@Directive({
1347
selector: '[form-control]',
1448
hostInjector: [formControlBinding],
15-
properties: ['control: form-control'],
49+
properties: ['control: form-control', 'model: ng-model'],
50+
events: ['ngModel'],
1651
lifecycle: [onChange]
1752
})
1853
export class FormControlDirective extends ControlDirective {
1954
control: Control;
55+
ngModel: EventEmitter;
56+
57+
constructor() {
58+
super();
59+
this.ngModel = new EventEmitter();
60+
}
2061

2162
onChange(_) {
2263
setUpControl(this.control, this);
2364
this.control.updateValidity();
2465
}
66+
67+
set model(value) {
68+
this.control.updateValue(value);
69+
this.valueAccessor.writeValue(value);
70+
}
71+
72+
viewToModelUpdate(newValue: any): void { ObservableWrapper.callNext(this.ngModel, newValue); }
2573
}

modules/angular2/src/forms/directives/form_directive.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export interface FormDirective {
66
removeControl(dir: ControlDirective): void;
77
addControlGroup(dir: ControlGroupDirective): void;
88
removeControlGroup(dir: ControlGroupDirective): void;
9+
updateModel(dir: ControlDirective, value: any): void;
910
}

modules/angular2/src/forms/directives/form_model_directive.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,53 @@ import {ControlDirective} from './control_directive';
66
import {ControlGroupDirective} from './control_group_directive';
77
import {ControlContainerDirective} from './control_container_directive';
88
import {FormDirective} from './form_directive';
9-
import {ControlGroup} from '../model';
9+
import {Control, ControlGroup} from '../model';
1010
import {setUpControl} from './shared';
1111

1212
const formDirectiveBinding = CONST_EXPR(
1313
new Binding(ControlContainerDirective, {toAlias: FORWARD_REF(() => FormModelDirective)}));
1414

15+
/**
16+
* Binds a control group to a DOM element.
17+
*
18+
* # Example
19+
*
20+
* In this example, we bind the control group to the form element, and we bind the login and
21+
* password controls to the
22+
* login and password elements.
23+
*
24+
* Here we use {@link formDirectives}, rather than importing each form directive individually, e.g.
25+
* `ControlDirective`, `ControlGroupDirective`. This is just a shorthand for the same end result.
26+
*
27+
* ```
28+
* @Component({selector: "login-comp"})
29+
* @View({
30+
* directives: [formDirectives],
31+
* template: "<form [form-model]='loginForm'>" +
32+
* "Login <input type='text' control='login'>" +
33+
* "Password <input type='password' control='password'>" +
34+
* "<button (click)="onLogin()">Login</button>" +
35+
* "</form>"
36+
* })
37+
* class LoginComp {
38+
* loginForm:ControlGroup;
39+
*
40+
* constructor() {
41+
* this.loginForm = new ControlGroup({
42+
* login: new Control(""),
43+
* password: new Control("")
44+
* });
45+
* }
46+
*
47+
* onLogin() {
48+
* // this.loginForm.value
49+
* }
50+
* }
51+
*
52+
* ```
53+
*
54+
* @exportedAs angular2/forms
55+
*/
1556
@Directive({
1657
selector: '[form-model]',
1758
hostInjector: [formDirectiveBinding],
@@ -46,6 +87,12 @@ export class FormModelDirective extends ControlContainerDirective implements For
4687

4788
removeControlGroup(dir: ControlGroupDirective) {}
4889

90+
updateModel(dir: ControlDirective, value: any): void {
91+
var c  = <Control>this.form.find(dir.path);
92+
c.value = value;
93+
dir.valueAccessor.writeValue(value);
94+
}
95+
4996
_updateDomValue() {
5097
ListWrapper.forEach(this.directives, dir => {
5198
var c: any = this.form.find(dir.path);

0 commit comments

Comments
 (0)