X Tutup
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
8 changes: 4 additions & 4 deletions modules/angular2/src/core/forms/directives/ng_form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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});
});
}

Expand All @@ -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});
}
});
}
Expand All @@ -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});
});
}

Expand All @@ -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});
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
2 changes: 1 addition & 1 deletion modules/angular2/src/core/forms/directives/ng_model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
121 changes: 90 additions & 31 deletions modules/angular2/src/core/forms/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand All @@ -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; }
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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 {
Expand All @@ -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;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just making a Note: When async validators are added this will need to get more complicated. Probably something like:

  • any errors: return INVALD
  • any pending controls: return PENDING
  • else VALID

}

/** @internal */
abstract _updateValue(): void;
/** @internal */
abstract _calculateControlsErrors(): any;
}

/**
Expand All @@ -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();
}

Expand All @@ -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.
*/
Expand All @@ -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 {
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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});
}

/**
Expand Down Expand Up @@ -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); }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it makes sense to move group, and array from Validators? I'm worried that a user might try to pass them in the constructor, or pass them in as a combined validator. Should these functions be used as a regular validator at this point?


/** @internal */
_setParentForControls(): void {
this.controls.forEach((control) => { control.setParent(this); });
Expand Down
6 changes: 3 additions & 3 deletions modules/angular2/src/core/forms/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -74,6 +74,6 @@ export class Validators {
anyErrors = true;
}
});
return anyErrors ? {'controls': res} : null;
return anyErrors ? res : null;
}
}
2 changes: 1 addition & 1 deletion modules/angular2/test/core/forms/form_builder_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down
Loading
X Tutup