feat(forms): Implement a way to manually set errors on a control
Example: var login = new Control("someLogin"); c.setErrors({"notUnique": true}); expect(c.valid).toEqual(false); expect(c.errors).toEqual({"notUnique": true}); c.updateValue("newLogin"); expect(c.valid).toEqual(true); BREAKING CHANGE: Before: ControlGroup.errors and ControlArray.errors returned a reduced value of their children controls' errors. After: ControlGroup.errors and ControlArray.errors return the errors of the group and array. And ControlGroup.controlsErrors and ControlArray.controlsErrors return the reduce value of their children controls' errors. Closes #4917
This commit is contained in:
parent
689ded5c47
commit
ed4826b08c
|
@ -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; }
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,22 +46,20 @@ function _find(control: AbstractControl, path: Array<string | number>| 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 | number>| 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); });
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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", () => {
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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}]}]});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
|
|
Loading…
Reference in New Issue