Skip to content

Commit 733915d

Browse files
committed
feat(forms): add support for nested forms
1 parent 7ddfbf8 commit 733915d

File tree

4 files changed

+202
-132
lines changed

4 files changed

+202
-132
lines changed

modules/angular2/src/forms/directives.js

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {Template, Component, Decorator, NgElement, Ancestor, onChange} from 'angular2/core';
2+
import {Optional} from 'angular2/di';
23
import {DOM} from 'angular2/src/dom/dom_adapter';
3-
import {isBlank, isPresent, CONST} from 'angular2/src/facade/lang';
4+
import {isBlank, isPresent, isString, CONST} from 'angular2/src/facade/lang';
45
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
56
import {ControlGroup, Control} from './model';
67
import * as validators from './validators';
@@ -64,7 +65,7 @@ function controlValueAccessorFor(controlType:string):ControlValueAccessor {
6465
}
6566
})
6667
export class ControlDirective {
67-
_groupDecorator:ControlGroupDirective;
68+
_groupDirective:ControlGroupDirective;
6869
_el:NgElement;
6970

7071
controlName:string;
@@ -73,8 +74,8 @@ export class ControlDirective {
7374

7475
validator:Function;
7576

76-
constructor(@Ancestor() groupDecorator:ControlGroupDirective, el:NgElement) {
77-
this._groupDecorator = groupDecorator;
77+
constructor(@Ancestor() groupDirective:ControlGroupDirective, el:NgElement) {
78+
this._groupDirective = groupDirective;
7879
this._el = el;
7980
this.validator = validators.nullValidator;
8081
}
@@ -86,7 +87,7 @@ export class ControlDirective {
8687
}
8788

8889
_initialize() {
89-
this._groupDecorator.addDirective(this);
90+
this._groupDirective.addDirective(this);
9091

9192
var c = this._control();
9293
c.validator = validators.compose([c.validator, this.validator]);
@@ -108,7 +109,7 @@ export class ControlDirective {
108109
}
109110

110111
_control() {
111-
return this._groupDecorator.findControl(this.controlName);
112+
return this._groupDirective.findControl(this.controlName);
112113
}
113114
}
114115

@@ -119,25 +120,45 @@ export class ControlDirective {
119120
}
120121
})
121122
export class ControlGroupDirective {
123+
_groupDirective:ControlGroupDirective;
124+
_controlGroupName:string;
125+
122126
_controlGroup:ControlGroup;
123127
_directives:List<ControlDirective>;
124128

125-
constructor() {
129+
constructor(@Optional() @Ancestor() groupDirective:ControlGroupDirective) {
126130
super();
131+
this._groupDirective = groupDirective;
127132
this._directives = ListWrapper.create();
128133
}
129134

130-
set controlGroup(controlGroup:ControlGroup) {
131-
this._controlGroup = controlGroup;
135+
set controlGroup(controlGroup) {
136+
if (isString(controlGroup)) {
137+
this._controlGroupName = controlGroup;
138+
} else {
139+
this._controlGroup = controlGroup;
140+
}
141+
this._updateDomValue();
142+
}
143+
144+
_updateDomValue() {
132145
ListWrapper.forEach(this._directives, (cd) => cd._updateDomValue());
133146
}
134147

135148
addDirective(c:ControlDirective) {
136149
ListWrapper.push(this._directives, c);
137150
}
138151

139-
findControl(name:string):Control {
140-
return this._controlGroup.controls[name];
152+
findControl(name:string):any {
153+
return this._getControlGroup().controls[name];
154+
}
155+
156+
_getControlGroup():ControlGroup {
157+
if (isPresent(this._controlGroupName)) {
158+
return this._groupDirective.findControl(this._controlGroupName)
159+
} else {
160+
return this._controlGroup;
161+
}
141162
}
142163
}
143164

modules/angular2/src/forms/model.js

Lines changed: 25 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,25 @@ export const INVALID = "INVALID";
1616
// setParent(parent){}
1717
//}
1818

19-
export class Control {
19+
export class AbstractControl {
2020
_value:any;
2121
_status:string;
2222
_errors;
2323
_dirty:boolean;
2424
_parent:ControlGroup;
2525
validator:Function;
2626

27-
constructor(value:any, validator:Function = nullValidator) {
28-
this._value = value;
27+
constructor(validator:Function = nullValidator) {
2928
this.validator = validator;
3029
this._dirty = true;
3130
}
3231

33-
updateValue(value:any) {
34-
this._value = value;
35-
this._dirty = true;
36-
this._updateParent();
37-
}
38-
3932
get active():boolean {
4033
return true;
4134
}
4235

4336
get value() {
37+
this._updateIfNeeded();
4438
return this._value;
4539
}
4640

@@ -64,11 +58,6 @@ export class Control {
6458
}
6559

6660
_updateIfNeeded() {
67-
if (this._dirty) {
68-
this._dirty = false;
69-
this._errors = this.validator(this);
70-
this._status = isPresent(this._errors) ? INVALID : VALID;
71-
}
7261
}
7362

7463
_updateParent() {
@@ -78,39 +67,34 @@ export class Control {
7867
}
7968
}
8069

81-
export class ControlGroup {
82-
_value:any;
83-
_status:string;
84-
_errors;
85-
_dirty:boolean;
86-
validator:Function;
87-
controls;
88-
89-
constructor(controls, validator:Function = controlGroupValidator) {
90-
this.controls = controls;
91-
this.validator = validator;
92-
this._dirty = true;
93-
this._setParentForControls();
70+
export class Control extends AbstractControl {
71+
constructor(value:any, validator:Function = nullValidator) {
72+
super(validator);
73+
this._value = value;
9474
}
9575

96-
get value() {
97-
this._updateIfNeeded();
98-
return this._value;
76+
updateValue(value:any) {
77+
this._value = value;
78+
this._dirty = true;
79+
this._updateParent();
9980
}
10081

101-
get status() {
102-
this._updateIfNeeded();
103-
return this._status;
82+
_updateIfNeeded() {
83+
if (this._dirty) {
84+
this._dirty = false;
85+
this._errors = this.validator(this);
86+
this._status = isPresent(this._errors) ? INVALID : VALID;
87+
}
10488
}
89+
}
10590

106-
get valid() {
107-
this._updateIfNeeded();
108-
return this._status === VALID;
109-
}
91+
export class ControlGroup extends AbstractControl {
92+
controls;
11093

111-
get errors() {
112-
this._updateIfNeeded();
113-
return this._errors;
94+
constructor(controls, validator:Function = controlGroupValidator) {
95+
super(validator);
96+
this.controls = controls;
97+
this._setParentForControls();
11498
}
11599

116100
_setParentForControls() {
@@ -140,6 +124,7 @@ export class ControlGroup {
140124

141125
_controlChanged() {
142126
this._dirty = true;
127+
this._updateParent();
143128
}
144129
}
145130

modules/angular2/test/forms/integration_spec.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,54 @@ export function main() {
218218
});
219219
});
220220
});
221+
222+
describe("nested forms", () => {
223+
it("should init DOM with the given form object", (done) => {
224+
var form = new ControlGroup({
225+
"nested": new ControlGroup({
226+
"login": new Control("value")
227+
})
228+
});
229+
var ctx = new MyComp(form);
230+
231+
var t = `<div [control-group]="form">
232+
<div control-group="nested">
233+
<input type="text" control="login">
234+
</div>
235+
</div>`;
236+
237+
compile(MyComp, t, ctx, (view) => {
238+
var input = queryView(view, "input")
239+
expect(input.value).toEqual("value");
240+
done();
241+
});
242+
});
243+
244+
it("should update the control group values on DOM change", (done) => {
245+
var form = new ControlGroup({
246+
"nested": new ControlGroup({
247+
"login": new Control("value")
248+
})
249+
});
250+
var ctx = new MyComp(form);
251+
252+
var t = `<div [control-group]="form">
253+
<div control-group="nested">
254+
<input type="text" control="login">
255+
</div>
256+
</div>`;
257+
258+
compile(MyComp, t, ctx, (view) => {
259+
var input = queryView(view, "input")
260+
261+
input.value = "updatedValue";
262+
dispatchEvent(input, "change");
263+
264+
expect(form.value).toEqual({"nested" : {"login" : "updatedValue"}});
265+
done();
266+
});
267+
});
268+
});
221269
});
222270
}
223271

0 commit comments

Comments
 (0)