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}, } }
This commit is contained in:
parent
c9fba3fa1f
commit
28d88c5b12
|
@ -46,7 +46,7 @@ export class Validators {
|
|||
static compose(validators: Function[]): Function {
|
||||
if (isBlank(validators)) return Validators.nullValidator;
|
||||
|
||||
return function(control: modelModule.Control) {
|
||||
return function(control: modelModule.AbstractControl) {
|
||||
var res = ListWrapper.reduce(validators, (res, validator) => {
|
||||
var errors = validator(control);
|
||||
return isPresent(errors) ? StringMapWrapper.merge(<any>res, <any>errors) : res;
|
||||
|
@ -55,33 +55,25 @@ export class Validators {
|
|||
};
|
||||
}
|
||||
|
||||
static group(group: modelModule.ControlGroup): {[key: string]: any[]} {
|
||||
static group(group: modelModule.ControlGroup): {[key: string]: any} {
|
||||
var res: {[key: string]: any[]} = {};
|
||||
StringMapWrapper.forEach(group.controls, (control, name) => {
|
||||
if (group.contains(name) && isPresent(control.errors)) {
|
||||
Validators._mergeErrors(control, res);
|
||||
res[name] = control.errors;
|
||||
}
|
||||
});
|
||||
return StringMapWrapper.isEmpty(res) ? null : res;
|
||||
return StringMapWrapper.isEmpty(res) ? null : {'controls': res};
|
||||
}
|
||||
|
||||
static array(array: modelModule.ControlArray): {[key: string]: any[]} {
|
||||
var res: {[key: string]: any[]} = {};
|
||||
static array(array: modelModule.ControlArray): {[key: string]: any} {
|
||||
var res: any[] = [];
|
||||
var anyErrors: boolean = false;
|
||||
array.controls.forEach((control) => {
|
||||
res.push(control.errors);
|
||||
if (isPresent(control.errors)) {
|
||||
Validators._mergeErrors(control, res);
|
||||
anyErrors = true;
|
||||
}
|
||||
});
|
||||
return StringMapWrapper.isEmpty(res) ? null : res;
|
||||
}
|
||||
|
||||
static _mergeErrors(control: modelModule.AbstractControl, res: {[key: string]: any[]}): void {
|
||||
StringMapWrapper.forEach(control.errors, (value, error) => {
|
||||
if (!StringMapWrapper.contains(res, error)) {
|
||||
res[error] = [];
|
||||
}
|
||||
var current: any[] = res[error];
|
||||
current.push(control);
|
||||
});
|
||||
return anyErrors ? {'controls': res} : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -192,7 +192,7 @@ export function main() {
|
|||
|
||||
expect(g.valid).toEqual(false);
|
||||
|
||||
expect(g.errors).toEqual({"required": [one]});
|
||||
expect(g.errors).toEqual({"controls": {"one": {"required": true}}});
|
||||
});
|
||||
|
||||
it("should run the validator with the value changes", () => {
|
||||
|
@ -445,7 +445,7 @@ export function main() {
|
|||
]);
|
||||
|
||||
expect(a.valid).toBe(false);
|
||||
expect(a.errors).toEqual({"required": [a.controls[1]]});
|
||||
expect(a.errors).toEqual({"controls": [null, {"required": true}, null]});
|
||||
});
|
||||
|
||||
it("should run the validator when the value changes", () => {
|
||||
|
|
|
@ -9,11 +9,11 @@ import {
|
|||
afterEach,
|
||||
el
|
||||
} from 'angular2/testing_internal';
|
||||
import {ControlGroup, Control, Validators} from 'angular2/core';
|
||||
import {ControlGroup, Control, Validators, AbstractControl, ControlArray} from 'angular2/core';
|
||||
|
||||
export function main() {
|
||||
function validator(key: string, error: any) {
|
||||
return function(c: Control) {
|
||||
return function(c: AbstractControl) {
|
||||
var r = {};
|
||||
r[key] = error;
|
||||
return r;
|
||||
|
@ -87,10 +87,10 @@ export function main() {
|
|||
describe("controlGroupValidator", () => {
|
||||
it("should collect errors from the child controls", () => {
|
||||
var one = new Control("one", validator("a", true));
|
||||
var two = new Control("one", validator("b", true));
|
||||
var two = new Control("two", validator("b", true));
|
||||
var g = new ControlGroup({"one": one, "two": two});
|
||||
|
||||
expect(Validators.group(g)).toEqual({"a": [one], "b": [two]});
|
||||
expect(Validators.group(g)).toEqual({"controls": {"one": {"a": true}, "two": {"b": true}}});
|
||||
});
|
||||
|
||||
it("should not include controls that have no errors", () => {
|
||||
|
@ -98,7 +98,7 @@ export function main() {
|
|||
var two = new Control("two");
|
||||
var g = new ControlGroup({"one": one, "two": two});
|
||||
|
||||
expect(Validators.group(g)).toEqual({"a": [one]});
|
||||
expect(Validators.group(g)).toEqual({"controls": {"one": {"a": true}}});
|
||||
});
|
||||
|
||||
it("should return null when no errors", () => {
|
||||
|
@ -106,6 +106,69 @@ export function main() {
|
|||
|
||||
expect(Validators.group(g)).toEqual(null);
|
||||
});
|
||||
|
||||
it("should return control errors mixed with group errors", () => {
|
||||
var one = new Control("one", validator("a", true));
|
||||
var g = new ControlGroup({"one": one}, null,
|
||||
Validators.compose([validator("b", true), Validators.group]));
|
||||
|
||||
expect(g.validator(g)).toEqual({"b": true, "controls": {"one": {"a": true}}});
|
||||
});
|
||||
|
||||
it("should return nested control group errors mixed with group errors", () => {
|
||||
var one = new Control("one", validator("a", true));
|
||||
var g = new ControlGroup({"one": one}, null,
|
||||
Validators.compose([validator("b", true), Validators.group]));
|
||||
var two = new Control("two", validator("c", true));
|
||||
var gTwo = new ControlGroup({"two": two, "group": g});
|
||||
|
||||
expect(gTwo.validator(gTwo))
|
||||
.toEqual({
|
||||
"controls":
|
||||
{"two": {"c": true}, "group": {"b": true, "controls": {"one": {"a": true}}}}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("controlArrayValidator", () => {
|
||||
it("should collect errors from the child controls", () => {
|
||||
var one = new Control("one", validator("a", true));
|
||||
var two = new Control("two", validator("b", true));
|
||||
var a = new ControlArray([one, two]);
|
||||
|
||||
expect(Validators.array(a)).toEqual({"controls": [{"a": true}, {"b": true}]});
|
||||
});
|
||||
|
||||
it("should not include controls that have no errors", () => {
|
||||
var one = new Control("one");
|
||||
var two = new Control("two", validator("a", true));
|
||||
var three = new Control("three");
|
||||
var a = new ControlArray([one, two, three]);
|
||||
|
||||
expect(Validators.array(a)).toEqual({"controls": [null, {"a": true}, null]});
|
||||
});
|
||||
|
||||
it("should return null when no errors", () => {
|
||||
var a = new ControlArray([new Control("one")]);
|
||||
|
||||
expect(Validators.array(a)).toEqual(null);
|
||||
});
|
||||
|
||||
it("should return control errors mixed with group errors", () => {
|
||||
var one = new Control("one", validator("a", true));
|
||||
var a =
|
||||
new ControlArray([one], Validators.compose([validator("b", true), Validators.array]));
|
||||
|
||||
expect(a.validator(a)).toEqual({"b": true, "controls": [{"a": true}]});
|
||||
});
|
||||
|
||||
it("should return nested array errors ", () => {
|
||||
var one = new Control("one", validator("a", true));
|
||||
var a = new ControlArray([one]);
|
||||
var a2 = new ControlArray([a]);
|
||||
|
||||
expect(Validators.array(a2)).toEqual({"controls": [{"controls": [{"a": true}]}]});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue