diff --git a/modules/angular2/src/core/forms/directives/abstract_control_directive.ts b/modules/angular2/src/core/forms/directives/abstract_control_directive.ts index ac836dec0d..94a7afeaa4 100644 --- a/modules/angular2/src/core/forms/directives/abstract_control_directive.ts +++ b/modules/angular2/src/core/forms/directives/abstract_control_directive.ts @@ -12,6 +12,8 @@ export class AbstractControlDirective { return isPresent(this.control) ? this.control.errors : null; } + get controlsErrors(): any { return isPresent(this.control) ? this.control.controlsErrors : null; } + get pristine(): boolean { return isPresent(this.control) ? this.control.pristine : null; } get dirty(): boolean { return isPresent(this.control) ? this.control.dirty : null; } diff --git a/modules/angular2/src/core/forms/directives/ng_form.ts b/modules/angular2/src/core/forms/directives/ng_form.ts index 0b68411c35..62ac673abf 100644 --- a/modules/angular2/src/core/forms/directives/ng_form.ts +++ b/modules/angular2/src/core/forms/directives/ng_form.ts @@ -104,7 +104,7 @@ export class NgForm extends ControlContainer implements Form { var ctrl = new Control(); setUpControl(ctrl, dir); container.addControl(dir.name, ctrl); - ctrl.updateValidity(); + ctrl.updateValueAndValidity({emitEvent: false}); }); } @@ -115,7 +115,7 @@ export class NgForm extends ControlContainer implements Form { var container = this._findContainer(dir.path); if (isPresent(container)) { container.removeControl(dir.name); - container.updateValidity(); + container.updateValueAndValidity({emitEvent: false}); } }); } @@ -125,7 +125,7 @@ export class NgForm extends ControlContainer implements Form { var container = this._findContainer(dir.path); var group = new ControlGroup({}); container.addControl(dir.name, group); - group.updateValidity(); + group.updateValueAndValidity({emitEvent: false}); }); } @@ -134,7 +134,7 @@ export class NgForm extends ControlContainer implements Form { var container = this._findContainer(dir.path); if (isPresent(container)) { container.removeControl(dir.name); - container.updateValidity(); + container.updateValueAndValidity({emitEvent: false}); } }); } diff --git a/modules/angular2/src/core/forms/directives/ng_form_control.ts b/modules/angular2/src/core/forms/directives/ng_form_control.ts index 610d5e3ecd..d7107d0248 100644 --- a/modules/angular2/src/core/forms/directives/ng_form_control.ts +++ b/modules/angular2/src/core/forms/directives/ng_form_control.ts @@ -85,7 +85,7 @@ export class NgFormControl extends NgControl implements OnChanges { onChanges(changes: {[key: string]: SimpleChange}): void { if (this._isControlChanged(changes)) { setUpControl(this.form, this); - this.form.updateValidity(); + this.form.updateValueAndValidity({emitEvent: false}); } if (isPropertyUpdated(changes, this.viewModel)) { this.form.updateValue(this.model); diff --git a/modules/angular2/src/core/forms/directives/ng_form_model.ts b/modules/angular2/src/core/forms/directives/ng_form_model.ts index 879f8bfdd7..5369118e30 100644 --- a/modules/angular2/src/core/forms/directives/ng_form_model.ts +++ b/modules/angular2/src/core/forms/directives/ng_form_model.ts @@ -112,7 +112,7 @@ export class NgFormModel extends ControlContainer implements Form, addControl(dir: NgControl): void { var ctrl: any = this.form.find(dir.path); setUpControl(ctrl, dir); - ctrl.updateValidity(); + ctrl.updateValueAndValidity({emitEvent: false}); this.directives.push(dir); } diff --git a/modules/angular2/src/core/forms/directives/ng_model.ts b/modules/angular2/src/core/forms/directives/ng_model.ts index ee342b5be3..8410abc643 100644 --- a/modules/angular2/src/core/forms/directives/ng_model.ts +++ b/modules/angular2/src/core/forms/directives/ng_model.ts @@ -61,7 +61,7 @@ export class NgModel extends NgControl implements OnChanges { onChanges(changes: {[key: string]: SimpleChange}) { if (!this._added) { setUpControl(this._control, this); - this._control.updateValidity(); + this._control.updateValueAndValidity({emitEvent: false}); this._added = true; } diff --git a/modules/angular2/src/core/forms/model.ts b/modules/angular2/src/core/forms/model.ts index ba006866fb..8dcef80501 100644 --- a/modules/angular2/src/core/forms/model.ts +++ b/modules/angular2/src/core/forms/model.ts @@ -46,22 +46,20 @@ function _find(control: AbstractControl, path: Array| string) { /** * */ -export class AbstractControl { +export abstract class AbstractControl { /** @internal */ _value: any; - /** @internal */ - _status: string; - /** @internal */ - _errors: {[key: string]: any}; - /** @internal */ - _pristine: boolean = true; - /** @internal */ - _touched: boolean = false; - /** @internal */ - _parent: ControlGroup | ControlArray; + /** @internal */ _valueChanges: EventEmitter; + private _status: string; + private _errors: {[key: string]: any}; + private _controlsErrors: any; + private _pristine: boolean = true; + private _touched: boolean = false; + private _parent: ControlGroup | ControlArray; + constructor(public validator: Function) {} get value(): any { return this._value; } @@ -70,8 +68,16 @@ export class AbstractControl { get valid(): boolean { return this._status === VALID; } + /** + * Returns the errors of this control. + */ get errors(): {[key: string]: any} { return this._errors; } + /** + * Returns the errors of the child controls. + */ + get controlsErrors(): any { return this._controlsErrors; } + get pristine(): boolean { return this._pristine; } get dirty(): boolean { return !this.pristine; } @@ -105,17 +111,6 @@ export class AbstractControl { setParent(parent: ControlGroup | ControlArray): void { this._parent = parent; } - updateValidity({onlySelf}: {onlySelf?: boolean} = {}): void { - onlySelf = normalizeBool(onlySelf); - - this._errors = this.validator(this); - this._status = isPresent(this._errors) ? INVALID : VALID; - - if (isPresent(this._parent) && !onlySelf) { - this._parent.updateValidity({onlySelf: onlySelf}); - } - } - updateValueAndValidity({onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}): void { onlySelf = normalizeBool(onlySelf); @@ -124,7 +119,8 @@ export class AbstractControl { this._updateValue(); this._errors = this.validator(this); - this._status = isPresent(this._errors) ? INVALID : VALID; + this._controlsErrors = this._calculateControlsErrors(); + this._status = this._calculateStatus(); if (emitEvent) { ObservableWrapper.callNext(this._valueChanges, this._value); @@ -135,6 +131,38 @@ export class AbstractControl { } } + /** + * Sets errors on a control. + * + * This is used when validations are run not automatically, but manually by the user. + * + * Calling `setErrors` will also update the validity of the parent control. + * + * ## Usage + * + * ``` + * var login = new Control("someLogin"); + * login.setErrors({ + * "notUnique": true + * }); + * + * expect(login.valid).toEqual(false); + * expect(login.errors).toEqual({"notUnique": true}); + * + * login.updateValue("someOtherLogin"); + * + * expect(login.valid).toEqual(true); + * ``` + */ + setErrors(errors: {[key: string]: any}): void { + this._errors = errors; + this._status = this._calculateStatus(); + + if (isPresent(this._parent)) { + this._parent._updateControlsErrors(); + } + } + find(path: Array| string): AbstractControl { return _find(this, path); } getError(errorCode: string, path: string[] = null): any { @@ -151,7 +179,23 @@ export class AbstractControl { } /** @internal */ - _updateValue(): void {} + _updateControlsErrors(): void { + this._controlsErrors = this._calculateControlsErrors(); + this._status = this._calculateStatus(); + + if (isPresent(this._parent)) { + this._parent._updateControlsErrors(); + } + } + + private _calculateStatus(): string { + return isPresent(this._errors) || isPresent(this._controlsErrors) ? INVALID : VALID; + } + + /** @internal */ + abstract _updateValue(): void; + /** @internal */ + abstract _calculateControlsErrors(): any; } /** @@ -177,7 +221,7 @@ export class Control extends AbstractControl { constructor(value: any = null, validator: Function = Validators.nullValidator) { super(validator); this._value = value; - this.updateValidity({onlySelf: true}); + this.updateValueAndValidity({onlySelf: true, emitEvent: false}); this._valueChanges = new EventEmitter(); } @@ -203,6 +247,16 @@ export class Control extends AbstractControl { this.updateValueAndValidity({onlySelf: onlySelf, emitEvent: emitEvent}); } + /** + * @internal + */ + _updateValue() {} + + /** + * @internal + */ + _calculateControlsErrors() { return null; } + /** * Register a listener for change events. */ @@ -226,14 +280,14 @@ export class ControlGroup extends AbstractControl { private _optionals: {[key: string]: boolean}; constructor(public controls: {[key: string]: AbstractControl}, - optionals: {[key: string]: boolean} = null, validator: Function = Validators.group) { + optionals: {[key: string]: boolean} = null, + validator: Function = Validators.nullValidator) { super(validator); this._optionals = isPresent(optionals) ? optionals : {}; this._valueChanges = new EventEmitter(); this._setParentForControls(); - this._value = this._reduceValue(); - this.updateValidity({onlySelf: true}); + this.updateValueAndValidity({onlySelf: true, emitEvent: false}); } addControl(name: string, control: AbstractControl): void { @@ -266,6 +320,9 @@ export class ControlGroup extends AbstractControl { /** @internal */ _updateValue() { this._value = this._reduceValue(); } + /** @internal */ + _calculateControlsErrors() { return Validators.group(this); } + /** @internal */ _reduceValue() { return this._reduceChildren({}, (acc, control, name) => { @@ -314,14 +371,13 @@ export class ControlGroup extends AbstractControl { * ### Example ([live demo](http://plnkr.co/edit/23DESOpbNnBpBHZt1BR4?p=preview)) */ export class ControlArray extends AbstractControl { - constructor(public controls: AbstractControl[], validator: Function = Validators.array) { + constructor(public controls: AbstractControl[], validator: Function = Validators.nullValidator) { super(validator); this._valueChanges = new EventEmitter(); this._setParentForControls(); - this._updateValue(); - this.updateValidity({onlySelf: true}); + this.updateValueAndValidity({onlySelf: true, emitEvent: false}); } /** @@ -363,6 +419,9 @@ export class ControlArray extends AbstractControl { /** @internal */ _updateValue(): void { this._value = this.controls.map((control) => control.value); } + /** @internal */ + _calculateControlsErrors() { return Validators.array(this); } + /** @internal */ _setParentForControls(): void { this.controls.forEach((control) => { control.setParent(this); }); diff --git a/modules/angular2/src/core/forms/validators.ts b/modules/angular2/src/core/forms/validators.ts index aca8932af2..2f4a1a5c1a 100644 --- a/modules/angular2/src/core/forms/validators.ts +++ b/modules/angular2/src/core/forms/validators.ts @@ -62,10 +62,10 @@ export class Validators { res[name] = control.errors; } }); - return StringMapWrapper.isEmpty(res) ? null : {'controls': res}; + return StringMapWrapper.isEmpty(res) ? null : res; } - static array(array: modelModule.ControlArray): {[key: string]: any} { + static array(array: modelModule.ControlArray): any[] { var res: any[] = []; var anyErrors: boolean = false; array.controls.forEach((control) => { @@ -74,6 +74,6 @@ export class Validators { anyErrors = true; } }); - return anyErrors ? {'controls': res} : null; + return anyErrors ? res : null; } } diff --git a/modules/angular2/test/core/forms/form_builder_spec.ts b/modules/angular2/test/core/forms/form_builder_spec.ts index d119da7935..5e061fa7c1 100644 --- a/modules/angular2/test/core/forms/form_builder_spec.ts +++ b/modules/angular2/test/core/forms/form_builder_spec.ts @@ -53,7 +53,7 @@ export function main() { it("should use default validators when no validators are provided", () => { var g = b.group({"login": "some value"}); expect(g.controls["login"].validator).toBe(Validators.nullValidator); - expect(g.validator).toBe(Validators.group); + expect(g.validator).toBe(Validators.nullValidator); }); it("should create control arrays", () => { diff --git a/modules/angular2/test/core/forms/model_spec.ts b/modules/angular2/test/core/forms/model_spec.ts index 039e8dccce..1e5cd52f67 100644 --- a/modules/angular2/test/core/forms/model_spec.ts +++ b/modules/angular2/test/core/forms/model_spec.ts @@ -151,6 +151,61 @@ export function main() { c.updateValue("new"); })); }); + + describe("setErrors", () => { + it("should set errors on a control", () => { + var c = new Control("someValue", Validators.nullValidator); + + c.setErrors({"someError": true}); + + expect(c.valid).toEqual(false); + expect(c.errors).toEqual({"someError": true}); + }); + + it("should reset the errors and validity when the value changes", () => { + var c = new Control("someValue", Validators.required); + + c.setErrors({"someError": true}); + c.updateValue(""); + + expect(c.errors).toEqual({"required": true}); + }); + + it("should update the parent group's validity", () => { + var c = new Control("someValue"); + var g = new ControlGroup({"one": c}); + + expect(g.valid).toEqual(true); + + c.setErrors({"someError": true}); + + expect(g.controlsErrors).toEqual({"one": {"someError": true}}); + expect(g.valid).toEqual(false); + }); + + it("should not reset parent's errors", () => { + var c = new Control("someValue"); + var g = new ControlGroup({"one": c}); + + g.setErrors({"someGroupError": true}); + c.setErrors({"someError": true}); + + expect(g.errors).toEqual({"someGroupError": true}); + }); + + it("update a value should reset errosr", () => { + var c = new Control("oldValue"); + var g = new ControlGroup({"one": c}); + + g.setErrors({"someGroupError": true}); + c.setErrors({"someError": true}); + + c.updateValue("newValue"); + + expect(c.errors).toEqual(null); + expect(g.errors).toEqual(null); + }); + }); }); describe("ControlGroup", () => { @@ -176,14 +231,12 @@ export function main() { }); }); - - describe("validator", () => { + describe("controlsErrors", () => { it("should run the validator with the initial value (valid)", () => { var g = new ControlGroup({"one": new Control('value', Validators.required)}); expect(g.valid).toEqual(true); - - expect(g.errors).toEqual(null); + expect(g.controlsErrors).toEqual(null); }); it("should run the validator with the initial value (invalid)", () => { @@ -191,8 +244,7 @@ export function main() { var g = new ControlGroup({"one": one}); expect(g.valid).toEqual(false); - - expect(g.errors).toEqual({"controls": {"one": {"required": true}}}); + expect(g.controlsErrors).toEqual({"one": {"required": true}}); }); it("should run the validator with the value changes", () => { @@ -201,8 +253,28 @@ export function main() { c.updateValue("some value"); + expect(g.valid).toEqual(true); + expect(g.controlsErrors).toEqual(null); + }); + }); + + describe("errors", () => { + it("should run the validator when the value changes", () => { + var simpleValidator = (c) => + c.controls["one"].value != "correct" ? {"broken": true} : null; + + var c = new Control(null); + var g = new ControlGroup({"one": c}, null, simpleValidator); + + c.updateValue("correct"); + expect(g.valid).toEqual(true); expect(g.errors).toEqual(null); + + c.updateValue("incorrect"); + + expect(g.valid).toEqual(false); + expect(g.errors).toEqual({"broken": true}); }); }); @@ -278,102 +350,102 @@ export function main() { expect(group.valid).toEqual(false); }); + }); - describe("valueChanges", () => { - var g, c1, c2; + describe("valueChanges", () => { + var g, c1, c2; - beforeEach(() => { - c1 = new Control("old1"); - c2 = new Control("old2"); - g = new ControlGroup({"one": c1, "two": c2}, {"two": true}); - }); - - it("should fire an event after the value has been updated", - inject([AsyncTestCompleter], (async) => { - ObservableWrapper.subscribe(g.valueChanges, (value) => { - expect(g.value).toEqual({'one': 'new1', 'two': 'old2'}); - expect(value).toEqual({'one': 'new1', 'two': 'old2'}); - async.done(); - }); - c1.updateValue("new1"); - })); - - it("should fire an event after the control's observable fired an event", - inject([AsyncTestCompleter], (async) => { - var controlCallbackIsCalled = false; - - ObservableWrapper.subscribe(c1.valueChanges, - (value) => { controlCallbackIsCalled = true; }); - - ObservableWrapper.subscribe(g.valueChanges, (value) => { - expect(controlCallbackIsCalled).toBe(true); - async.done(); - }); - - c1.updateValue("new1"); - })); - - it("should fire an event when a control is excluded", - inject([AsyncTestCompleter], (async) => { - ObservableWrapper.subscribe(g.valueChanges, (value) => { - expect(value).toEqual({'one': 'old1'}); - async.done(); - }); - - g.exclude("two"); - })); - - it("should fire an event when a control is included", - inject([AsyncTestCompleter], (async) => { - g.exclude("two"); - - ObservableWrapper.subscribe(g.valueChanges, (value) => { - expect(value).toEqual({'one': 'old1', 'two': 'old2'}); - async.done(); - }); - - g.include("two"); - })); - - it("should fire an event every time a control is updated", - inject([AsyncTestCompleter], (async) => { - var loggedValues = []; - - ObservableWrapper.subscribe(g.valueChanges, (value) => { - loggedValues.push(value); - - if (loggedValues.length == 2) { - expect(loggedValues) - .toEqual([{"one": "new1", "two": "old2"}, {"one": "new1", "two": "new2"}]); - async.done(); - } - }); - - c1.updateValue("new1"); - c2.updateValue("new2"); - })); - - xit("should not fire an event when an excluded control is updated", - inject([AsyncTestCompleter], (async) => { - // hard to test without hacking zones - })); + beforeEach(() => { + c1 = new Control("old1"); + c2 = new Control("old2"); + g = new ControlGroup({"one": c1, "two": c2}, {"two": true}); }); - describe("getError", () => { - it("should return the error when it is present", () => { - var c = new Control("", Validators.required); - var g = new ControlGroup({"one": c}); - expect(c.getError("required")).toEqual(true); - expect(g.getError("required", ["one"])).toEqual(true); - }); + it("should fire an event after the value has been updated", + inject([AsyncTestCompleter], (async) => { + ObservableWrapper.subscribe(g.valueChanges, (value) => { + expect(g.value).toEqual({'one': 'new1', 'two': 'old2'}); + expect(value).toEqual({'one': 'new1', 'two': 'old2'}); + async.done(); + }); + c1.updateValue("new1"); + })); - it("should return null otherwise", () => { - var c = new Control("not empty", Validators.required); - var g = new ControlGroup({"one": c}); - expect(c.getError("invalid")).toEqual(null); - expect(g.getError("required", ["one"])).toEqual(null); - expect(g.getError("required", ["invalid"])).toEqual(null); - }); + it("should fire an event after the control's observable fired an event", + inject([AsyncTestCompleter], (async) => { + var controlCallbackIsCalled = false; + + ObservableWrapper.subscribe(c1.valueChanges, + (value) => { controlCallbackIsCalled = true; }); + + ObservableWrapper.subscribe(g.valueChanges, (value) => { + expect(controlCallbackIsCalled).toBe(true); + async.done(); + }); + + c1.updateValue("new1"); + })); + + it("should fire an event when a control is excluded", + inject([AsyncTestCompleter], (async) => { + ObservableWrapper.subscribe(g.valueChanges, (value) => { + expect(value).toEqual({'one': 'old1'}); + async.done(); + }); + + g.exclude("two"); + })); + + it("should fire an event when a control is included", + inject([AsyncTestCompleter], (async) => { + g.exclude("two"); + + ObservableWrapper.subscribe(g.valueChanges, (value) => { + expect(value).toEqual({'one': 'old1', 'two': 'old2'}); + async.done(); + }); + + g.include("two"); + })); + + it("should fire an event every time a control is updated", + inject([AsyncTestCompleter], (async) => { + var loggedValues = []; + + ObservableWrapper.subscribe(g.valueChanges, (value) => { + loggedValues.push(value); + + if (loggedValues.length == 2) { + expect(loggedValues) + .toEqual([{"one": "new1", "two": "old2"}, {"one": "new1", "two": "new2"}]); + async.done(); + } + }); + + c1.updateValue("new1"); + c2.updateValue("new2"); + })); + + xit("should not fire an event when an excluded control is updated", + inject([AsyncTestCompleter], (async) => { + // hard to test without hacking zones + })); + }); + + describe("getError", () => { + it("should return the error when it is present", () => { + var c = new Control("", Validators.required); + var g = new ControlGroup({"one": c}); + expect(c.getError("required")).toEqual(true); + expect(g.getError("required", ["one"])).toEqual(true); + }); + + it("should return null otherwise", () => { + var c = new Control("not empty", Validators.required); + var g = new ControlGroup({"one": c}); + expect(c.getError("invalid")).toEqual(null); + expect(g.getError("required", ["one"])).toEqual(null); + expect(g.getError("required", ["invalid"])).toEqual(null); }); }); }); @@ -428,13 +500,13 @@ export function main() { }); }); - describe("validator", () => { + describe("controlsErrors", () => { it("should run the validator with the initial value (valid)", () => { var a = new ControlArray( [new Control(1, Validators.required), new Control(2, Validators.required)]); expect(a.valid).toBe(true); - expect(a.errors).toBe(null); + expect(a.controlsErrors).toBe(null); }); it("should run the validator with the initial value (invalid)", () => { @@ -445,7 +517,7 @@ export function main() { ]); expect(a.valid).toBe(false); - expect(a.errors).toEqual({"controls": [null, {"required": true}, null]}); + expect(a.controlsErrors).toEqual([null, {"required": true}, null]); }); it("should run the validator when the value changes", () => { @@ -457,10 +529,30 @@ export function main() { c.updateValue("some value"); expect(a.valid).toBe(true); - expect(a.errors).toBe(null); + expect(a.controlsErrors).toBe(null); }); }); + describe("errors", () => { + it("should run the validator when the value changes", () => { + var simpleValidator = (c) => c.controls[0].value != "correct" ? {"broken": true} : null; + + var c = new Control(null); + var g = new ControlArray([c], simpleValidator); + + c.updateValue("correct"); + + expect(g.valid).toEqual(true); + expect(g.errors).toEqual(null); + + c.updateValue("incorrect"); + + expect(g.valid).toEqual(false); + expect(g.errors).toEqual({"broken": true}); + }); + }); + + describe("dirty", () => { var c: Control; var a: ControlArray; @@ -564,38 +656,38 @@ export function main() { a.push(c2); })); }); - }); - describe("find", () => { - it("should return null when path is null", () => { - var g = new ControlGroup({}); - expect(g.find(null)).toEqual(null); - }); + describe("find", () => { + it("should return null when path is null", () => { + var g = new ControlGroup({}); + expect(g.find(null)).toEqual(null); + }); - it("should return null when path is empty", () => { - var g = new ControlGroup({}); - expect(g.find([])).toEqual(null); - }); + it("should return null when path is empty", () => { + var g = new ControlGroup({}); + expect(g.find([])).toEqual(null); + }); - it("should return null when path is invalid", () => { - var g = new ControlGroup({}); - expect(g.find(["one", "two"])).toEqual(null); - }); + it("should return null when path is invalid", () => { + var g = new ControlGroup({}); + expect(g.find(["one", "two"])).toEqual(null); + }); - it("should return a child of a control group", () => { - var g = new ControlGroup( - {"one": new Control("111"), "nested": new ControlGroup({"two": new Control("222")})}); + it("should return a child of a control group", () => { + var g = new ControlGroup( + {"one": new Control("111"), "nested": new ControlGroup({"two": new Control("222")})}); - expect(g.find(["nested", "two"]).value).toEqual("222"); - expect(g.find(["one"]).value).toEqual("111"); - expect(g.find("nested/two").value).toEqual("222"); - expect(g.find("one").value).toEqual("111"); - }); + expect(g.find(["nested", "two"]).value).toEqual("222"); + expect(g.find(["one"]).value).toEqual("111"); + expect(g.find("nested/two").value).toEqual("222"); + expect(g.find("one").value).toEqual("111"); + }); - it("should return an element of an array", () => { - var g = new ControlGroup({"array": new ControlArray([new Control("111")])}); + it("should return an element of an array", () => { + var g = new ControlGroup({"array": new ControlArray([new Control("111")])}); - expect(g.find(["array", 0]).value).toEqual("111"); + expect(g.find(["array", 0]).value).toEqual("111"); + }); }); }); }); diff --git a/modules/angular2/test/core/forms/validators_spec.ts b/modules/angular2/test/core/forms/validators_spec.ts index f81585b9b4..86eed1fb3b 100644 --- a/modules/angular2/test/core/forms/validators_spec.ts +++ b/modules/angular2/test/core/forms/validators_spec.ts @@ -90,7 +90,7 @@ export function main() { var two = new Control("two", validator("b", true)); var g = new ControlGroup({"one": one, "two": two}); - expect(Validators.group(g)).toEqual({"controls": {"one": {"a": true}, "two": {"b": true}}}); + expect(Validators.group(g)).toEqual({"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({"controls": {"one": {"a": true}}}); + expect(Validators.group(g)).toEqual({"one": {"a": true}}); }); it("should return null when no errors", () => { @@ -106,28 +106,6 @@ 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", () => { @@ -136,7 +114,7 @@ export function main() { var two = new Control("two", validator("b", true)); var a = new ControlArray([one, two]); - expect(Validators.array(a)).toEqual({"controls": [{"a": true}, {"b": true}]}); + expect(Validators.array(a)).toEqual([{"a": true}, {"b": true}]); }); it("should not include controls that have no errors", () => { @@ -145,7 +123,7 @@ export function main() { var three = new Control("three"); var a = new ControlArray([one, two, three]); - expect(Validators.array(a)).toEqual({"controls": [null, {"a": true}, null]}); + expect(Validators.array(a)).toEqual([null, {"a": true}, null]); }); it("should return null when no errors", () => { @@ -153,22 +131,6 @@ export function main() { 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}]}]}); - }); }); }); } diff --git a/modules/angular2/test/public_api_spec.ts b/modules/angular2/test/public_api_spec.ts index c287a70e4a..8d5254c13b 100644 --- a/modules/angular2/test/public_api_spec.ts +++ b/modules/angular2/test/public_api_spec.ts @@ -43,9 +43,11 @@ var NG_API = [ 'AbstractControl', 'AbstractControl.dirty', 'AbstractControl.errors', + 'AbstractControl.controlsErrors', 'AbstractControl.find()', 'AbstractControl.getError()', 'AbstractControl.hasError()', + 'AbstractControl.setErrors()', 'AbstractControl.markAsDirty()', 'AbstractControl.markAsPending()', 'AbstractControl.markAsTouched()', @@ -55,7 +57,6 @@ var NG_API = [ 'AbstractControl.status', 'AbstractControl.touched', 'AbstractControl.untouched', - 'AbstractControl.updateValidity()', 'AbstractControl.updateValueAndValidity()', 'AbstractControl.valid', 'AbstractControl.validator', @@ -66,6 +67,7 @@ var NG_API = [ 'AbstractControlDirective.control', 'AbstractControlDirective.dirty', 'AbstractControlDirective.errors', + 'AbstractControlDirective.controlsErrors', 'AbstractControlDirective.pristine', 'AbstractControlDirective.touched', 'AbstractControlDirective.untouched', @@ -276,6 +278,7 @@ var NG_API = [ 'Control', 'Control.dirty', 'Control.errors', + 'Control.controlsErrors', 'Control.find()', 'Control.getError()', 'Control.hasError()', @@ -289,7 +292,6 @@ var NG_API = [ 'Control.status', 'Control.touched', 'Control.untouched', - 'Control.updateValidity()', 'Control.updateValue()', 'Control.updateValueAndValidity()', 'Control.valid', @@ -297,12 +299,14 @@ var NG_API = [ 'Control.validator=', 'Control.value', 'Control.valueChanges', + 'Control.setErrors()', 'ControlArray', 'ControlArray.at()', 'ControlArray.controls', 'ControlArray.controls=', 'ControlArray.dirty', 'ControlArray.errors', + 'ControlArray.controlsErrors', 'ControlArray.find()', 'ControlArray.getError()', 'ControlArray.hasError()', @@ -319,17 +323,18 @@ var NG_API = [ 'ControlArray.status', 'ControlArray.touched', 'ControlArray.untouched', - 'ControlArray.updateValidity()', 'ControlArray.updateValueAndValidity()', 'ControlArray.valid', 'ControlArray.validator', 'ControlArray.validator=', 'ControlArray.value', 'ControlArray.valueChanges', + 'ControlArray.setErrors()', 'ControlContainer', 'ControlContainer.control', 'ControlContainer.dirty', 'ControlContainer.errors', + 'ControlContainer.controlsErrors', 'ControlContainer.formDirective', 'ControlContainer.name', 'ControlContainer.name=', @@ -346,6 +351,7 @@ var NG_API = [ 'ControlGroup.controls=', 'ControlGroup.dirty', 'ControlGroup.errors', + 'ControlGroup.controlsErrors', 'ControlGroup.exclude()', 'ControlGroup.find()', 'ControlGroup.getError()', @@ -361,13 +367,13 @@ var NG_API = [ 'ControlGroup.status', 'ControlGroup.touched', 'ControlGroup.untouched', - 'ControlGroup.updateValidity()', 'ControlGroup.updateValueAndValidity()', 'ControlGroup.valid', 'ControlGroup.validator', 'ControlGroup.validator=', 'ControlGroup.value', 'ControlGroup.valueChanges', + 'ControlGroup.setErrors()', 'CurrencyPipe', 'CurrencyPipe.transform()', 'CyclicDependencyError', @@ -622,6 +628,7 @@ var NG_API = [ 'NgControl.control', 'NgControl.dirty', 'NgControl.errors', + 'NgControl.controlsErrors', 'NgControl.name', 'NgControl.name=', 'NgControl.path', @@ -638,6 +645,7 @@ var NG_API = [ 'NgControlGroup.control', 'NgControlGroup.dirty', 'NgControlGroup.errors', + 'NgControlGroup.controlsErrors', 'NgControlGroup.formDirective', 'NgControlGroup.name', 'NgControlGroup.name=', @@ -660,6 +668,7 @@ var NG_API = [ 'NgControlName.control', 'NgControlName.dirty', 'NgControlName.errors', + 'NgControlName.controlsErrors', 'NgControlName.formDirective', 'NgControlName.model', 'NgControlName.model=', @@ -694,6 +703,7 @@ var NG_API = [ 'NgForm.controls', 'NgForm.dirty', 'NgForm.errors', + 'NgForm.controlsErrors', 'NgForm.form', 'NgForm.form=', 'NgForm.formDirective', @@ -717,6 +727,7 @@ var NG_API = [ 'NgFormControl.control', 'NgFormControl.dirty', 'NgFormControl.errors', + 'NgFormControl.controlsErrors', 'NgFormControl.form', 'NgFormControl.form=', 'NgFormControl.model', @@ -748,6 +759,7 @@ var NG_API = [ 'NgFormModel.directives=', 'NgFormModel.dirty', 'NgFormModel.errors', + 'NgFormModel.controlsErrors', 'NgFormModel.form', 'NgFormModel.form=', 'NgFormModel.formDirective', @@ -774,6 +786,7 @@ var NG_API = [ 'NgModel.control', 'NgModel.dirty', 'NgModel.errors', + 'NgModel.controlsErrors', 'NgModel.model', 'NgModel.model=', 'NgModel.name',