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 @@ -21,4 +21,6 @@ export class AbstractControlDirective {
get touched(): boolean { return isPresent(this.control) ? this.control.touched : null; }

get untouched(): boolean { return isPresent(this.control) ? this.control.untouched : null; }

get path(): string[] { return null; }
}
1 change: 0 additions & 1 deletion modules/angular2/src/core/forms/directives/ng_control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export class NgControl extends AbstractControlDirective {
valueAccessor: ControlValueAccessor = null;

get validator(): Function { return null; }
get path(): string[] { return null; }

viewToModelUpdate(newValue: any): void {}
}
14 changes: 11 additions & 3 deletions modules/angular2/src/core/forms/directives/ng_control_group.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {OnInit, OnDestroy} from 'angular2/lifecycle_hooks';
import {Directive} from 'angular2/src/core/metadata';
import {Inject, Host, SkipSelf, forwardRef, Provider} from 'angular2/src/core/di';
import {Optional, Inject, Host, SkipSelf, forwardRef, Provider} from 'angular2/src/core/di';
import {ListWrapper} from 'angular2/src/core/facade/collection';
import {CONST_EXPR} from 'angular2/src/core/facade/lang';

import {ControlContainer} from './control_container';
import {controlPath} from './shared';
import {ControlGroup} from '../model';
import {Form} from './form_interface';
import {Validators, NG_VALIDATORS} from '../validators';

const controlGroupBinding =
CONST_EXPR(new Provider(ControlContainer, {useExisting: forwardRef(() => NgControlGroup)}));
Expand Down Expand Up @@ -60,9 +61,14 @@ export class NgControlGroup extends ControlContainer implements OnInit,
OnDestroy {
/** @internal */
_parent: ControlContainer;
constructor(@Host() @SkipSelf() _parent: ControlContainer) {

private _validators: Function[];

constructor(@Host() @SkipSelf() parent: ControlContainer,
@Optional() @Inject(NG_VALIDATORS) validators: Function[]) {
super();
this._parent = _parent;
this._parent = parent;
this._validators = validators;
}

onInit(): void { this.formDirective.addControlGroup(this); }
Expand All @@ -74,4 +80,6 @@ export class NgControlGroup extends ControlContainer implements OnInit,
get path(): string[] { return controlPath(this.name, this._parent); }

get formDirective(): Form { return this._parent.formDirective; }

get validator(): Function { return Validators.compose(this._validators); }
}
13 changes: 10 additions & 3 deletions modules/angular2/src/core/forms/directives/ng_form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import {
import {StringMapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
import {isPresent, isBlank, CONST_EXPR} from 'angular2/src/core/facade/lang';
import {Directive} from 'angular2/src/core/metadata';
import {forwardRef, Provider} from 'angular2/src/core/di';
import {forwardRef, Provider, Optional, Inject} from 'angular2/src/core/di';
import {NgControl} from './ng_control';
import {Form} from './form_interface';
import {NgControlGroup} from './ng_control_group';
import {ControlContainer} from './control_container';
import {AbstractControl, ControlGroup, Control} from '../model';
import {setUpControl} from './shared';
import {setUpControl, setUpControlGroup} from './shared';
import {Validators, NG_VALIDATORS} from '../validators';

const formDirectiveProvider =
CONST_EXPR(new Provider(ControlContainer, {useExisting: forwardRef(() => NgForm)}));
Expand Down Expand Up @@ -87,9 +88,14 @@ const formDirectiveProvider =
exportAs: 'form'
})
export class NgForm extends ControlContainer implements Form {
form: ControlGroup = new ControlGroup({});
form: ControlGroup;
ngSubmit = new EventEmitter();

constructor(@Optional() @Inject(NG_VALIDATORS) validators: Function[]) {
super();
this.form = new ControlGroup({}, null, Validators.compose(validators));
}

get formDirective(): Form { return this; }

get control(): ControlGroup { return this.form; }
Expand Down Expand Up @@ -124,6 +130,7 @@ export class NgForm extends ControlContainer implements Form {
this._later(_ => {
var container = this._findContainer(dir.path);
var group = new ControlGroup({});
setUpControlGroup(group, dir);
container.addControl(dir.name, group);
group.updateValueAndValidity({emitEvent: false});
});
Expand Down
29 changes: 24 additions & 5 deletions modules/angular2/src/core/forms/directives/ng_form_model.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import {CONST_EXPR} from 'angular2/src/core/facade/lang';
import {ListWrapper} from 'angular2/src/core/facade/collection';
import {ListWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
import {ObservableWrapper, EventEmitter} from 'angular2/src/core/facade/async';
import {SimpleChange} from 'angular2/src/core/change_detection';

import {OnChanges} from 'angular2/lifecycle_hooks';
import {Directive} from 'angular2/src/core/metadata';
import {forwardRef, Provider} from 'angular2/src/core/di';
import {forwardRef, Provider, Inject, Optional} from 'angular2/src/core/di';
import {NgControl} from './ng_control';
import {NgControlGroup} from './ng_control_group';
import {ControlContainer} from './control_container';
import {Form} from './form_interface';
import {Control, ControlGroup} from '../model';
import {setUpControl} from './shared';
import {setUpControl, setUpControlGroup} from './shared';
import {Validators, NG_VALIDATORS} from '../validators';

const formDirectiveProvider =
CONST_EXPR(new Provider(ControlContainer, {useExisting: forwardRef(() => NgFormModel)}));
Expand Down Expand Up @@ -100,8 +102,21 @@ export class NgFormModel extends ControlContainer implements Form,
form: ControlGroup = null;
directives: NgControl[] = [];
ngSubmit = new EventEmitter();
private _validators: Function[];

onChanges(_): void { this._updateDomValue(); }
constructor(@Optional() @Inject(NG_VALIDATORS) validators: Function[]) {
super();
this._validators = validators;
}

onChanges(changes: {[key: string]: SimpleChange}): void {
if (StringMapWrapper.contains(changes, "form")) {
var c = Validators.compose(this._validators);
this.form.validator = Validators.compose([this.form.validator, c]);
}

this._updateDomValue();
}

get formDirective(): Form { return this; }

Expand All @@ -120,7 +135,11 @@ export class NgFormModel extends ControlContainer implements Form,

removeControl(dir: NgControl): void { ListWrapper.remove(this.directives, dir); }

addControlGroup(dir: NgControlGroup) {}
addControlGroup(dir: NgControlGroup) {
var ctrl: any = this.form.find(dir.path);
setUpControlGroup(ctrl, dir);
ctrl.updateValueAndValidity({emitEvent: false});
}

removeControlGroup(dir: NgControlGroup) {}

Expand Down
11 changes: 9 additions & 2 deletions modules/angular2/src/core/forms/directives/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptio

import {ControlContainer} from './control_container';
import {NgControl} from './ng_control';
import {Control} from '../model';
import {AbstractControlDirective} from './abstract_control_directive';
import {NgControlGroup} from './ng_control_group';
import {Control, ControlGroup} from '../model';
import {Validators} from '../validators';
import {ControlValueAccessor} from './control_value_accessor';
import {ElementRef, QueryList} from 'angular2/src/core/linker';
Expand Down Expand Up @@ -42,7 +44,12 @@ export function setUpControl(control: Control, dir: NgControl): void {
dir.valueAccessor.registerOnTouched(() => control.markAsTouched());
}

function _throwError(dir: NgControl, message: string): void {
export function setUpControlGroup(control: ControlGroup, dir: NgControlGroup) {
if (isBlank(control)) _throwError(dir, "Cannot find control");
control.validator = Validators.compose([control.validator, dir.validator]);
}

function _throwError(dir: AbstractControlDirective, message: string): void {
var path = dir.path.join(" -> ");
throw new BaseException(`${message} '${path}'`);
}
Expand Down
24 changes: 21 additions & 3 deletions modules/angular2/src/core/forms/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,15 @@ export class ControlGroup extends AbstractControl {
_updateValue() { this._value = this._reduceValue(); }

/** @internal */
_calculateControlsErrors() { return Validators.group(this); }
_calculateControlsErrors() {
var res = {};
StringMapWrapper.forEach(this.controls, (control, name) => {
if (this.contains(name) && isPresent(control.errors)) {
res[name] = control.errors;
}
});
return StringMapWrapper.isEmpty(res) ? null : res;
}

/** @internal */
_reduceValue() {
Expand Down Expand Up @@ -420,10 +428,20 @@ export class ControlArray extends AbstractControl {
_updateValue(): void { this._value = this.controls.map((control) => control.value); }

/** @internal */
_calculateControlsErrors() { return Validators.array(this); }
_calculateControlsErrors() {
var res = [];
var anyErrors = false;
this.controls.forEach((control) => {
res.push(control.errors);
if (isPresent(control.errors)) {
anyErrors = true;
}
});
return anyErrors ? res : null;
}

/** @internal */
_setParentForControls(): void {
this.controls.forEach((control) => { control.setParent(this); });
}
}
}
22 changes: 0 additions & 22 deletions modules/angular2/src/core/forms/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,26 +54,4 @@ export class Validators {
return StringMapWrapper.isEmpty(res) ? null : res;
};
}

static group(group: modelModule.ControlGroup): {[key: string]: any} {
var res: {[key: string]: any[]} = {};
StringMapWrapper.forEach(group.controls, (control, name) => {
if (group.contains(name) && isPresent(control.errors)) {
res[name] = control.errors;
}
});
return StringMapWrapper.isEmpty(res) ? null : res;
}

static array(array: modelModule.ControlArray): any[] {
var res: any[] = [];
var anyErrors: boolean = false;
array.controls.forEach((control) => {
res.push(control.errors);
if (isPresent(control.errors)) {
anyErrors = true;
}
});
return anyErrors ? res : null;
}
}
63 changes: 55 additions & 8 deletions modules/angular2/test/core/forms/directives_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,12 @@ export function main() {
var loginControlDir;

beforeEach(() => {
form = new NgFormModel();
formModel = new ControlGroup({"login": new Control(null)});
form = new NgFormModel([]);
formModel = new ControlGroup({
"login": new Control(),
"passwords":
new ControlGroup({"password": new Control(), "passwordConfirm": new Control()})
});
form.form = formModel;

loginControlDir = new NgControlName(form, [], [defaultAccessor]);
Expand Down Expand Up @@ -167,6 +171,26 @@ export function main() {
});
});

describe("addControlGroup", () => {
var matchingPasswordsValidator = (g) => {
if (g.controls["password"].value != g.controls["passwordConfirm"].value) {
return {"differentPasswords": true};
} else {
return null;
}
};

it("should set up validator", () => {
var group = new NgControlGroup(form, [matchingPasswordsValidator]);
group.name = "passwords";
form.addControlGroup(group);

formModel.find(["passwords", "password"]).updateValue("somePassword");

expect(formModel.hasError("differentPasswords", ["passwords"])).toEqual(true);
});
});

describe("removeControl", () => {
it("should remove the directive to the list of directives included in the form", () => {
form.addControl(loginControlDir);
Expand All @@ -181,10 +205,22 @@ export function main() {

formModel.find(["login"]).updateValue("new value");

form.onChanges(null);
form.onChanges({});

expect((<any>loginControlDir.valueAccessor).writtenValue).toEqual("new value");
});

it("should set up validator", () => {
var formValidator = (c) => ({"custom": true});
var f = new NgFormModel([formValidator]);
f.form = formModel;
f.onChanges({"form": formModel});

// trigger validation
formModel.controls["login"].updateValue("");

expect(formModel.errors).toEqual({"custom": true});
});
});
});

Expand All @@ -195,10 +231,10 @@ export function main() {
var personControlGroupDir;

beforeEach(() => {
form = new NgForm();
form = new NgForm([]);
formModel = form.form;

personControlGroupDir = new NgControlGroup(form);
personControlGroupDir = new NgControlGroup(form, []);
personControlGroupDir.name = "person";

loginControlDir = new NgControlName(personControlGroupDir, null, [defaultAccessor]);
Expand Down Expand Up @@ -246,6 +282,17 @@ export function main() {

// should update the form's value and validity
});

it("should set up validator", fakeAsync(() => {
var formValidator = (c) => ({"custom": true});
var f = new NgForm([formValidator]);
f.addControlGroup(personControlGroupDir);
f.addControl(loginControlDir);

flushMicrotasks();

expect(f.form.errors).toEqual({"custom": true});
}));
});

describe("NgControlGroup", () => {
Expand All @@ -255,9 +302,9 @@ export function main() {
beforeEach(() => {
formModel = new ControlGroup({"login": new Control(null)});

var parent = new NgFormModel();
var parent = new NgFormModel([]);
parent.form = new ControlGroup({"group": formModel});
controlGroupDir = new NgControlGroup(parent);
controlGroupDir = new NgControlGroup(parent, []);
controlGroupDir.name = "group";
});

Expand Down Expand Up @@ -356,7 +403,7 @@ export function main() {
beforeEach(() => {
formModel = new Control("name");

var parent = new NgFormModel();
var parent = new NgFormModel([]);
parent.form = new ControlGroup({"name": formModel});
controlNameDir = new NgControlName(parent, [], [defaultAccessor]);
controlNameDir.name = "name";
Expand Down
Loading