refactor(forms): wrapped all validators into the Validator class

This commit is contained in:
vsavkin 2015-03-19 14:21:40 -07:00
parent 41b53e71e1
commit a12dc7d75a
8 changed files with 69 additions and 72 deletions

View File

@ -4,7 +4,7 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
import {isBlank, isPresent, isString, CONST} from 'angular2/src/facade/lang'; import {isBlank, isPresent, isString, CONST} from 'angular2/src/facade/lang';
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection'; import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {ControlGroup, Control} from './model'; import {ControlGroup, Control} from './model';
import * as validators from './validators'; import {Validators} from './validators';
@CONST() @CONST()
export class ControlValueAccessor { export class ControlValueAccessor {
@ -79,7 +79,7 @@ export class ControlDirective {
this._el = el; this._el = el;
this.controlName = null; this.controlName = null;
this.type = null; this.type = null;
this.validator = validators.nullValidator; this.validator = Validators.nullValidator;
} }
// TODO: vsavkin this should be moved into the constructor once static bindings // TODO: vsavkin this should be moved into the constructor once static bindings
@ -92,7 +92,7 @@ export class ControlDirective {
this._groupDirective.addDirective(this); this._groupDirective.addDirective(this);
var c = this._control(); var c = this._control();
c.validator = validators.compose([c.validator, this.validator]); c.validator = Validators.compose([c.validator, this.validator]);
if (isBlank(this.valueAccessor)) { if (isBlank(this.valueAccessor)) {
this.valueAccessor = controlValueAccessorFor(this.type); this.valueAccessor = controlValueAccessorFor(this.type);

View File

@ -1,6 +1,6 @@
import {isPresent} from 'angular2/src/facade/lang'; import {isPresent} from 'angular2/src/facade/lang';
import {StringMap, StringMapWrapper} from 'angular2/src/facade/collection'; import {StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
import {nullValidator, controlGroupValidator} from './validators'; import {Validators} from './validators';
export const VALID = "VALID"; export const VALID = "VALID";
export const INVALID = "INVALID"; export const INVALID = "INVALID";
@ -26,7 +26,7 @@ export class AbstractControl {
_parent:ControlGroup; _parent:ControlGroup;
validator:Function; validator:Function;
constructor(validator:Function = nullValidator) { constructor(validator:Function) {
this.validator = validator; this.validator = validator;
this._updateNeeded = true; this._updateNeeded = true;
this._pristine = true; this._pristine = true;
@ -76,7 +76,7 @@ export class AbstractControl {
} }
export class Control extends AbstractControl { export class Control extends AbstractControl {
constructor(value:any, validator:Function = nullValidator) { constructor(value:any, validator:Function = Validators.nullValidator) {
super(validator); super(validator);
this._value = value; this._value = value;
} }
@ -101,7 +101,7 @@ export class ControlGroup extends AbstractControl {
controls; controls;
optionals; optionals;
constructor(controls, optionals = null, validator:Function = controlGroupValidator) { constructor(controls, optionals = null, validator:Function = Validators.group) {
super(validator); super(validator);
this.controls = controls; this.controls = controls;
this.optionals = isPresent(optionals) ? optionals : {}; this.optionals = isPresent(optionals) ? optionals : {};

View File

@ -1,13 +1,12 @@
import {Decorator} from 'angular2/angular2'; import {Decorator} from 'angular2/angular2';
import {ControlDirective} from 'angular2/forms'; import {ControlDirective, Validators} from 'angular2/forms';
import * as validators from 'angular2/forms';
@Decorator({ @Decorator({
selector: '[required]' selector: '[required]'
}) })
export class RequiredValidatorDirective { export class RequiredValidatorDirective {
constructor(c:ControlDirective) { constructor(c:ControlDirective) {
c.validator = validators.compose([c.validator, validators.required]); c.validator = Validators.compose([c.validator, Validators.required]);
} }
} }

View File

@ -3,35 +3,37 @@ import {List, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collectio
import * as modelModule from './model'; import * as modelModule from './model';
export function required(c:modelModule.Control) { export class Validators {
return isBlank(c.value) || c.value == "" ? {"required" : true} : null; static required(c:modelModule.Control) {
} return isBlank(c.value) || c.value == "" ? {"required": true} : null;
}
export function nullValidator(c:modelModule.Control) { static nullValidator(c:modelModule.Control) {
return null; return null;
} }
export function compose(validators:List<Function>):Function { static compose(validators:List<Function>):Function {
return function(c:modelModule.Control) { return function (c:modelModule.Control) {
var res = ListWrapper.reduce(validators, (res, validator) => { var res = ListWrapper.reduce(validators, (res, validator) => {
var errors = validator(c); var errors = validator(c);
return isPresent(errors) ? StringMapWrapper.merge(res, errors) : res; return isPresent(errors) ? StringMapWrapper.merge(res, errors) : res;
}, {}); }, {});
return StringMapWrapper.isEmpty(res) ? null : res;
}
}
static group(c:modelModule.ControlGroup) {
var res = {};
StringMapWrapper.forEach(c.controls, (control, name) => {
if (c.contains(name) && isPresent(control.errors)) {
StringMapWrapper.forEach(control.errors, (value, error) => {
if (!StringMapWrapper.contains(res, error)) {
res[error] = [];
}
ListWrapper.push(res[error], control);
});
}
});
return StringMapWrapper.isEmpty(res) ? null : res; return StringMapWrapper.isEmpty(res) ? null : res;
} }
} }
export function controlGroupValidator(c:modelModule.ControlGroup) {
var res = {};
StringMapWrapper.forEach(c.controls, (control, name) => {
if (c.contains(name) && isPresent(control.errors)) {
StringMapWrapper.forEach(control.errors, (value, error) => {
if (! StringMapWrapper.contains(res, error)) {
res[error] = [];
}
ListWrapper.push(res[error], control);
});
}
});
return StringMapWrapper.isEmpty(res) ? null : res;
}

View File

@ -1,6 +1,5 @@
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, el} from 'angular2/test_lib'; import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, el} from 'angular2/test_lib';
import {Control, FormBuilder} from 'angular2/forms'; import {Control, FormBuilder, Validators} from 'angular2/forms';
import * as validations from 'angular2/forms';
export function main() { export function main() {
describe("Form Builder", () => { describe("Form Builder", () => {
@ -21,21 +20,21 @@ export function main() {
it("should create controls from an array", () => { it("should create controls from an array", () => {
var g = b.group({ var g = b.group({
"login": ["some value"], "login": ["some value"],
"password": ["some value", validations.required] "password": ["some value", Validators.required]
}); });
expect(g.controls["login"].value).toEqual("some value"); expect(g.controls["login"].value).toEqual("some value");
expect(g.controls["password"].value).toEqual("some value"); expect(g.controls["password"].value).toEqual("some value");
expect(g.controls["password"].validator).toEqual(validations.required); expect(g.controls["password"].validator).toEqual(Validators.required);
}); });
it("should use controls", () => { it("should use controls", () => {
var g = b.group({ var g = b.group({
"login": b.control("some value", validations.required) "login": b.control("some value", Validators.required)
}); });
expect(g.controls["login"].value).toEqual("some value"); expect(g.controls["login"].value).toEqual("some value");
expect(g.controls["login"].validator).toBe(validations.required); expect(g.controls["login"].validator).toBe(Validators.required);
}); });
it("should create groups with optional controls", () => { it("should create groups with optional controls", () => {
@ -49,17 +48,17 @@ export function main() {
it("should create groups with a custom validator", () => { it("should create groups with a custom validator", () => {
var g = b.group({ var g = b.group({
"login": "some value" "login": "some value"
}, {"validator": validations.nullValidator}); }, {"validator": Validators.nullValidator});
expect(g.validator).toBe(validations.nullValidator); expect(g.validator).toBe(Validators.nullValidator);
}); });
it("should use default validators when no validators are provided", () => { it("should use default validators when no validators are provided", () => {
var g = b.group({ var g = b.group({
"login": "some value" "login": "some value"
}); });
expect(g.controls["login"].validator).toBe(validations.nullValidator); expect(g.controls["login"].validator).toBe(Validators.nullValidator);
expect(g.validator).toBe(validations.controlGroupValidator); expect(g.validator).toBe(Validators.group);
}); });
}); });
} }

View File

@ -31,9 +31,7 @@ import {Injector} from 'angular2/di';
import {Component, Decorator, Template} from 'angular2/angular2'; import {Component, Decorator, Template} from 'angular2/angular2';
import {ControlGroupDirective, ControlDirective, Control, ControlGroup, OptionalControl, import {ControlGroupDirective, ControlDirective, Control, ControlGroup, OptionalControl,
ControlValueAccessor, RequiredValidatorDirective} from 'angular2/forms'; ControlValueAccessor, RequiredValidatorDirective, Validators} from 'angular2/forms';
import * as validators from 'angular2/src/forms/validators';
export function main() { export function main() {
function detectChanges(view) { function detectChanges(view) {
@ -261,7 +259,7 @@ export function main() {
})); }));
it("should use validators defined in the model", inject([AsyncTestCompleter], (async) => { it("should use validators defined in the model", inject([AsyncTestCompleter], (async) => {
var form = new ControlGroup({"login": new Control("aa", validators.required)}); var form = new ControlGroup({"login": new Control("aa", Validators.required)});
var ctx = new MyComp(form); var ctx = new MyComp(form);
var t = `<div [control-group]="form"> var t = `<div [control-group]="form">

View File

@ -1,24 +1,23 @@
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, el} from 'angular2/test_lib'; import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, el} from 'angular2/test_lib';
import {ControlGroup, Control, OptionalControl} from 'angular2/forms'; import {ControlGroup, Control, OptionalControl, Validators} from 'angular2/forms';
import * as validations from 'angular2/forms';
export function main() { export function main() {
describe("Form Model", () => { describe("Form Model", () => {
describe("Control", () => { describe("Control", () => {
describe("validator", () => { describe("validator", () => {
it("should run validator with the initial value", () => { it("should run validator with the initial value", () => {
var c = new Control("value", validations.required); var c = new Control("value", Validators.required);
expect(c.valid).toEqual(true); expect(c.valid).toEqual(true);
}); });
it("should rerun the validator when the value changes", () => { it("should rerun the validator when the value changes", () => {
var c = new Control("value", validations.required); var c = new Control("value", Validators.required);
c.updateValue(null); c.updateValue(null);
expect(c.valid).toEqual(false); expect(c.valid).toEqual(false);
}); });
it("should return errors", () => { it("should return errors", () => {
var c = new Control(null, validations.required); var c = new Control(null, Validators.required);
expect(c.errors).toEqual({"required" : true}); expect(c.errors).toEqual({"required" : true});
}); });
}); });
@ -83,7 +82,7 @@ export function main() {
describe("validator", () => { describe("validator", () => {
it("should run the validator with the initial value (valid)", () => { it("should run the validator with the initial value (valid)", () => {
var g = new ControlGroup({ var g = new ControlGroup({
"one": new Control('value', validations.required) "one": new Control('value', Validators.required)
}); });
expect(g.valid).toEqual(true); expect(g.valid).toEqual(true);
@ -92,7 +91,7 @@ export function main() {
}); });
it("should run the validator with the initial value (invalid)", () => { it("should run the validator with the initial value (invalid)", () => {
var one = new Control(null, validations.required); var one = new Control(null, Validators.required);
var g = new ControlGroup({"one": one}); var g = new ControlGroup({"one": one});
expect(g.valid).toEqual(false); expect(g.valid).toEqual(false);
@ -101,7 +100,7 @@ export function main() {
}); });
it("should run the validator with the value changes", () => { it("should run the validator with the value changes", () => {
var c = new Control(null, validations.required); var c = new Control(null, Validators.required);
var g = new ControlGroup({"one": c}); var g = new ControlGroup({"one": c});
c.updateValue("some value"); c.updateValue("some value");
@ -174,10 +173,10 @@ export function main() {
expect(group.value).toEqual({"required" : "requiredValue", "optional" : "optionalValue"}); expect(group.value).toEqual({"required" : "requiredValue", "optional" : "optionalValue"});
}); });
it("should not run validations on an inactive component", () => { it("should not run Validators on an inactive component", () => {
var group = new ControlGroup({ var group = new ControlGroup({
"required": new Control("requiredValue", validations.required), "required": new Control("requiredValue", Validators.required),
"optional": new Control("", validations.required) "optional": new Control("", Validators.required)
}, { }, {
"optional": false "optional": false
}); });

View File

@ -1,5 +1,5 @@
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, el} from 'angular2/test_lib'; import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, el} from 'angular2/test_lib';
import {ControlGroup, Control, required, compose, controlGroupValidator, nullValidator} from 'angular2/forms'; import {ControlGroup, Control, Validators} from 'angular2/forms';
export function main() { export function main() {
function validator(key:string, error:any){ function validator(key:string, error:any){
@ -13,31 +13,31 @@ export function main() {
describe("Validators", () => { describe("Validators", () => {
describe("required", () => { describe("required", () => {
it("should error on an empty string", () => { it("should error on an empty string", () => {
expect(required(new Control(""))).toEqual({"required" : true}); expect(Validators.required(new Control(""))).toEqual({"required" : true});
}); });
it("should error on null", () => { it("should error on null", () => {
expect(required(new Control(null))).toEqual({"required" : true}); expect(Validators.required(new Control(null))).toEqual({"required" : true});
}); });
it("should not error on a non-empty string", () => { it("should not error on a non-empty string", () => {
expect(required(new Control("not empty"))).toEqual(null); expect(Validators.required(new Control("not empty"))).toEqual(null);
}); });
}); });
describe("compose", () => { describe("compose", () => {
it("should collect errors from all the validators", () => { it("should collect errors from all the validators", () => {
var c = compose([validator("a", true), validator("b", true)]); var c = Validators.compose([validator("a", true), validator("b", true)]);
expect(c(new Control(""))).toEqual({"a" : true, "b" : true}); expect(c(new Control(""))).toEqual({"a" : true, "b" : true});
}); });
it("should run validators left to right", () => { it("should run validators left to right", () => {
var c = compose([validator("a", 1), validator("a", 2)]); var c = Validators.compose([validator("a", 1), validator("a", 2)]);
expect(c(new Control(""))).toEqual({"a" : 2}); expect(c(new Control(""))).toEqual({"a" : 2});
}); });
it("should return null when no errors", () => { it("should return null when no errors", () => {
var c = compose([nullValidator, nullValidator]); var c = Validators.compose([Validators.nullValidator, Validators.nullValidator]);
expect(c(new Control(""))).toEqual(null); expect(c(new Control(""))).toEqual(null);
}); });
}); });
@ -48,7 +48,7 @@ export function main() {
var two = new Control("one", validator("b", true)); var two = new Control("one", validator("b", true));
var g = new ControlGroup({"one" : one, "two" : two}); var g = new ControlGroup({"one" : one, "two" : two});
expect(controlGroupValidator(g)).toEqual({ expect(Validators.group(g)).toEqual({
"a" : [one], "a" : [one],
"b" : [two] "b" : [two]
}); });
@ -59,7 +59,7 @@ export function main() {
var two = new Control("two"); var two = new Control("two");
var g = new ControlGroup({"one" : one, "two" : two}); var g = new ControlGroup({"one" : one, "two" : two});
expect(controlGroupValidator(g)).toEqual({ expect(Validators.group(g)).toEqual({
"a": [one] "a": [one]
}); });
}); });
@ -69,7 +69,7 @@ export function main() {
"one" : new Control("one") "one" : new Control("one")
}); });
expect(controlGroupValidator(g)).toEqual(null); expect(Validators.group(g)).toEqual(null);
}); });
}); });
}); });