Skip to content

Commit 28d88c5

Browse files
TedSandervsavkin
authored andcommitted
feat(validators): Allow errors at both the group/array level or their children
Allow ControlGroups and ControlArrays to contain errors from their level, and errors from their children. [Design Doc](https://docs.google.com/document/d/1EnJ3-_iFpVKFz1ifN1LkXSGQ7h3A72OQGry2g8eo7IA/edit?pli=1#heading=h.j53rt81eegm4) BREAKING CHANGE: errors format has changed from validators. Now errors from a control or an array's children are prefixed with 'controls' while errors from the object itself are left at the root level. Example: Given a Control group as follows: var group = new ControlGroup({ login: new Control("", required), password: new Control("", required), passwordConfirm: new Control("", required) }); Before: group.errors { login: {required: true}, password: {required: true}, passwordConfirm: {required: true}, } After: group.errors { controls: { login: {required: true}, password: {required: true}, passwordConfirm: {required: true}, } }
1 parent c9fba3f commit 28d88c5

3 files changed

Lines changed: 81 additions & 26 deletions

File tree

modules/angular2/src/core/forms/validators.ts

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export class Validators {
4646
static compose(validators: Function[]): Function {
4747
if (isBlank(validators)) return Validators.nullValidator;
4848

49-
return function(control: modelModule.Control) {
49+
return function(control: modelModule.AbstractControl) {
5050
var res = ListWrapper.reduce(validators, (res, validator) => {
5151
var errors = validator(control);
5252
return isPresent(errors) ? StringMapWrapper.merge(<any>res, <any>errors) : res;
@@ -55,33 +55,25 @@ export class Validators {
5555
};
5656
}
5757

58-
static group(group: modelModule.ControlGroup): {[key: string]: any[]} {
58+
static group(group: modelModule.ControlGroup): {[key: string]: any} {
5959
var res: {[key: string]: any[]} = {};
6060
StringMapWrapper.forEach(group.controls, (control, name) => {
6161
if (group.contains(name) && isPresent(control.errors)) {
62-
Validators._mergeErrors(control, res);
62+
res[name] = control.errors;
6363
}
6464
});
65-
return StringMapWrapper.isEmpty(res) ? null : res;
65+
return StringMapWrapper.isEmpty(res) ? null : {'controls': res};
6666
}
6767

68-
static array(array: modelModule.ControlArray): {[key: string]: any[]} {
69-
var res: {[key: string]: any[]} = {};
68+
static array(array: modelModule.ControlArray): {[key: string]: any} {
69+
var res: any[] = [];
70+
var anyErrors: boolean = false;
7071
array.controls.forEach((control) => {
72+
res.push(control.errors);
7173
if (isPresent(control.errors)) {
72-
Validators._mergeErrors(control, res);
73-
}
74-
});
75-
return StringMapWrapper.isEmpty(res) ? null : res;
76-
}
77-
78-
static _mergeErrors(control: modelModule.AbstractControl, res: {[key: string]: any[]}): void {
79-
StringMapWrapper.forEach(control.errors, (value, error) => {
80-
if (!StringMapWrapper.contains(res, error)) {
81-
res[error] = [];
74+
anyErrors = true;
8275
}
83-
var current: any[] = res[error];
84-
current.push(control);
8576
});
77+
return anyErrors ? {'controls': res} : null;
8678
}
8779
}

modules/angular2/test/core/forms/model_spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ export function main() {
192192

193193
expect(g.valid).toEqual(false);
194194

195-
expect(g.errors).toEqual({"required": [one]});
195+
expect(g.errors).toEqual({"controls": {"one": {"required": true}}});
196196
});
197197

198198
it("should run the validator with the value changes", () => {
@@ -445,7 +445,7 @@ export function main() {
445445
]);
446446

447447
expect(a.valid).toBe(false);
448-
expect(a.errors).toEqual({"required": [a.controls[1]]});
448+
expect(a.errors).toEqual({"controls": [null, {"required": true}, null]});
449449
});
450450

451451
it("should run the validator when the value changes", () => {

modules/angular2/test/core/forms/validators_spec.ts

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import {
99
afterEach,
1010
el
1111
} from 'angular2/testing_internal';
12-
import {ControlGroup, Control, Validators} from 'angular2/core';
12+
import {ControlGroup, Control, Validators, AbstractControl, ControlArray} from 'angular2/core';
1313

1414
export function main() {
1515
function validator(key: string, error: any) {
16-
return function(c: Control) {
16+
return function(c: AbstractControl) {
1717
var r = {};
1818
r[key] = error;
1919
return r;
@@ -87,25 +87,88 @@ export function main() {
8787
describe("controlGroupValidator", () => {
8888
it("should collect errors from the child controls", () => {
8989
var one = new Control("one", validator("a", true));
90-
var two = new Control("one", validator("b", true));
90+
var two = new Control("two", validator("b", true));
9191
var g = new ControlGroup({"one": one, "two": two});
9292

93-
expect(Validators.group(g)).toEqual({"a": [one], "b": [two]});
93+
expect(Validators.group(g)).toEqual({"controls": {"one": {"a": true}, "two": {"b": true}}});
9494
});
9595

9696
it("should not include controls that have no errors", () => {
9797
var one = new Control("one", validator("a", true));
9898
var two = new Control("two");
9999
var g = new ControlGroup({"one": one, "two": two});
100100

101-
expect(Validators.group(g)).toEqual({"a": [one]});
101+
expect(Validators.group(g)).toEqual({"controls": {"one": {"a": true}}});
102102
});
103103

104104
it("should return null when no errors", () => {
105105
var g = new ControlGroup({"one": new Control("one")});
106106

107107
expect(Validators.group(g)).toEqual(null);
108108
});
109+
110+
it("should return control errors mixed with group errors", () => {
111+
var one = new Control("one", validator("a", true));
112+
var g = new ControlGroup({"one": one}, null,
113+
Validators.compose([validator("b", true), Validators.group]));
114+
115+
expect(g.validator(g)).toEqual({"b": true, "controls": {"one": {"a": true}}});
116+
});
117+
118+
it("should return nested control group errors mixed with group errors", () => {
119+
var one = new Control("one", validator("a", true));
120+
var g = new ControlGroup({"one": one}, null,
121+
Validators.compose([validator("b", true), Validators.group]));
122+
var two = new Control("two", validator("c", true));
123+
var gTwo = new ControlGroup({"two": two, "group": g});
124+
125+
expect(gTwo.validator(gTwo))
126+
.toEqual({
127+
"controls":
128+
{"two": {"c": true}, "group": {"b": true, "controls": {"one": {"a": true}}}}
129+
});
130+
});
131+
});
132+
133+
describe("controlArrayValidator", () => {
134+
it("should collect errors from the child controls", () => {
135+
var one = new Control("one", validator("a", true));
136+
var two = new Control("two", validator("b", true));
137+
var a = new ControlArray([one, two]);
138+
139+
expect(Validators.array(a)).toEqual({"controls": [{"a": true}, {"b": true}]});
140+
});
141+
142+
it("should not include controls that have no errors", () => {
143+
var one = new Control("one");
144+
var two = new Control("two", validator("a", true));
145+
var three = new Control("three");
146+
var a = new ControlArray([one, two, three]);
147+
148+
expect(Validators.array(a)).toEqual({"controls": [null, {"a": true}, null]});
149+
});
150+
151+
it("should return null when no errors", () => {
152+
var a = new ControlArray([new Control("one")]);
153+
154+
expect(Validators.array(a)).toEqual(null);
155+
});
156+
157+
it("should return control errors mixed with group errors", () => {
158+
var one = new Control("one", validator("a", true));
159+
var a =
160+
new ControlArray([one], Validators.compose([validator("b", true), Validators.array]));
161+
162+
expect(a.validator(a)).toEqual({"b": true, "controls": [{"a": true}]});
163+
});
164+
165+
it("should return nested array errors ", () => {
166+
var one = new Control("one", validator("a", true));
167+
var a = new ControlArray([one]);
168+
var a2 = new ControlArray([a]);
169+
170+
expect(Validators.array(a2)).toEqual({"controls": [{"controls": [{"a": true}]}]});
171+
});
109172
});
110173
});
111-
}
174+
}

0 commit comments

Comments
 (0)