Skip to content

Commit ff84506

Browse files
committed
feat(forms): added support for arrays of controls
1 parent 0ae33b7 commit ff84506

File tree

5 files changed

+297
-47
lines changed

5 files changed

+297
-47
lines changed

modules/angular2/src/forms/form_builder.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
1+
import {StringMapWrapper, ListWrapper, List} from 'angular2/src/facade/collection';
22
import {isPresent} from 'angular2/src/facade/lang';
33
import * as modelModule from './model';
44

@@ -24,6 +24,15 @@ export class FormBuilder {
2424
}
2525
}
2626

27+
array(controlsConfig:List, validator:Function = null):modelModule.ControlArray {
28+
var controls = ListWrapper.map(controlsConfig, (c) => this._createControl(c));
29+
if (isPresent(validator)) {
30+
return new modelModule.ControlArray(controls, validator);
31+
} else {
32+
return new modelModule.ControlArray(controls);
33+
}
34+
}
35+
2736
_reduceControls(controlsConfig) {
2837
var controls = {};
2938
StringMapWrapper.forEach(controlsConfig, (controlConfig, controlName) => {
@@ -33,7 +42,9 @@ export class FormBuilder {
3342
}
3443

3544
_createControl(controlConfig) {
36-
if (controlConfig instanceof modelModule.Control || controlConfig instanceof modelModule.ControlGroup) {
45+
if (controlConfig instanceof modelModule.Control ||
46+
controlConfig instanceof modelModule.ControlGroup ||
47+
controlConfig instanceof modelModule.ControlArray) {
3748
return controlConfig;
3849

3950
} else if (ListWrapper.isList(controlConfig)) {

modules/angular2/src/forms/model.js

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {isPresent} from 'angular2/src/facade/lang';
22
import {Observable, ObservableWrapper} from 'angular2/src/facade/async';
3-
import {StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
3+
import {StringMap, StringMapWrapper, ListWrapper, List} from 'angular2/src/facade/collection';
44
import {Validators} from './validators';
55

66
export const VALID = "VALID";
@@ -23,9 +23,12 @@ export class AbstractControl {
2323
_status:string;
2424
_errors;
2525
_pristine:boolean;
26-
_parent:ControlGroup;
26+
_parent:any; /* ControlGroup | ControlArray */
2727
validator:Function;
2828

29+
valueChanges:Observable;
30+
_valueChangesController;
31+
2932
constructor(validator:Function) {
3033
this.validator = validator;
3134
this._pristine = true;
@@ -67,9 +70,6 @@ export class AbstractControl {
6770
}
6871

6972
export class Control extends AbstractControl {
70-
valueChanges:Observable;
71-
_valueChangesController;
72-
7373
constructor(value:any, validator:Function = Validators.nullValidator) {
7474
super(validator);
7575
this._setValueErrorsStatus(value);
@@ -98,9 +98,6 @@ export class ControlGroup extends AbstractControl {
9898
controls;
9999
optionals;
100100

101-
valueChanges:Observable;
102-
_valueChangesController;
103-
104101
constructor(controls, optionals = null, validator:Function = Validators.group) {
105102
super(validator);
106103
this.controls = controls;
@@ -170,4 +167,65 @@ export class ControlGroup extends AbstractControl {
170167
var isOptional = StringMapWrapper.contains(this.optionals, controlName);
171168
return !isOptional || StringMapWrapper.get(this.optionals, controlName);
172169
}
170+
}
171+
172+
export class ControlArray extends AbstractControl {
173+
controls:List;
174+
175+
constructor(controls:List, validator:Function = Validators.array) {
176+
super(validator);
177+
this.controls = controls;
178+
179+
this._valueChangesController = ObservableWrapper.createController();
180+
this.valueChanges = ObservableWrapper.createObservable(this._valueChangesController);
181+
182+
this._setParentForControls();
183+
this._setValueErrorsStatus();
184+
}
185+
186+
at(index:number) {
187+
return this.controls[index];
188+
}
189+
190+
push(control) {
191+
ListWrapper.push(this.controls, control);
192+
control.setParent(this);
193+
this._updateValue();
194+
}
195+
196+
insert(index:number, control) {
197+
ListWrapper.insert(this.controls, index, control);
198+
control.setParent(this);
199+
this._updateValue();
200+
}
201+
202+
removeAt(index:number) {
203+
ListWrapper.removeAt(this.controls, index);
204+
this._updateValue();
205+
}
206+
207+
get length() {
208+
return this.controls.length;
209+
}
210+
211+
_updateValue() {
212+
this._setValueErrorsStatus();
213+
this._pristine = false;
214+
215+
ObservableWrapper.callNext(this._valueChangesController, this._value);
216+
217+
this._updateParent();
218+
}
219+
220+
_setParentForControls() {
221+
ListWrapper.forEach(this.controls, (control) => {
222+
control.setParent(this);
223+
});
224+
}
225+
226+
_setValueErrorsStatus() {
227+
this._value = ListWrapper.map(this.controls, (c) => c.value);
228+
this._errors = this.validator(this);
229+
this._status = isPresent(this._errors) ? INVALID : VALID;
230+
}
173231
}

modules/angular2/src/forms/validators.js

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,28 @@ export class Validators {
2626
var res = {};
2727
StringMapWrapper.forEach(c.controls, (control, name) => {
2828
if (c.contains(name) && isPresent(control.errors)) {
29-
StringMapWrapper.forEach(control.errors, (value, error) => {
30-
if (!StringMapWrapper.contains(res, error)) {
31-
res[error] = [];
32-
}
33-
ListWrapper.push(res[error], control);
34-
});
29+
Validators._mergeErrors(control, res);
3530
}
3631
});
3732
return StringMapWrapper.isEmpty(res) ? null : res;
3833
}
34+
35+
static array(c:modelModule.ControlArray) {
36+
var res = {};
37+
ListWrapper.forEach(c.controls, (control) => {
38+
if (isPresent(control.errors)) {
39+
Validators._mergeErrors(control, res);
40+
}
41+
});
42+
return StringMapWrapper.isEmpty(res) ? null : res;
43+
}
44+
45+
static _mergeErrors(control, res) {
46+
StringMapWrapper.forEach(control.errors, (value, error) => {
47+
if (!StringMapWrapper.contains(res, error)) {
48+
res[error] = [];
49+
}
50+
ListWrapper.push(res[error], control);
51+
});
52+
}
3953
}

modules/angular2/test/forms/form_builder_spec.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,18 @@ export function main() {
6060
expect(g.controls["login"].validator).toBe(Validators.nullValidator);
6161
expect(g.validator).toBe(Validators.group);
6262
});
63+
64+
it("should create control arrays", () => {
65+
var c = b.control("three");
66+
var a = b.array([
67+
"one",
68+
["two", Validators.required],
69+
c,
70+
b.array(['four'])
71+
]);
72+
73+
expect(a.value).toEqual(['one', 'two', 'three', ['four']]);
74+
});
6375
});
64-
}
76+
}
77+

0 commit comments

Comments
 (0)