refactor(forms): refactored forms to user Query to get html validators
This commit is contained in:
parent
85b8a15374
commit
4d1ed509e3
|
@ -38,8 +38,6 @@ export class BaseQueryList<T> {
|
||||||
removeCallback(callback) { ListWrapper.remove(this._callbacks, callback); }
|
removeCallback(callback) { ListWrapper.remove(this._callbacks, callback); }
|
||||||
|
|
||||||
get length() { return this._results.length; }
|
get length() { return this._results.length; }
|
||||||
|
|
||||||
get first() { return ListWrapper.first(this._results); }
|
get first() { return ListWrapper.first(this._results); }
|
||||||
|
|
||||||
get last() { return ListWrapper.last(this._results); }
|
get last() { return ListWrapper.last(this._results); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,6 +184,8 @@ class ListWrapper {
|
||||||
|
|
||||||
bool isListLikeIterable(obj) => obj is Iterable;
|
bool isListLikeIterable(obj) => obj is Iterable;
|
||||||
|
|
||||||
|
List<T> iterableToList(Iterable<T> ii) => ii.toList();
|
||||||
|
|
||||||
void iterateListLike(iter, fn(item)) {
|
void iterateListLike(iter, fn(item)) {
|
||||||
assert(iter is Iterable);
|
assert(iter is Iterable);
|
||||||
for (var item in iter) {
|
for (var item in iter) {
|
||||||
|
|
|
@ -253,7 +253,13 @@ export function iterateListLike(obj, fn: Function) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export function iterableToList<T>(ii:Iterable<T>):List<T> {
|
||||||
|
var res = [];
|
||||||
|
for (var i of ii) {
|
||||||
|
res.push(i);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
// Safari and Internet Explorer do not support the iterable parameter to the
|
// Safari and Internet Explorer do not support the iterable parameter to the
|
||||||
// Set constructor. We work around that by manually adding the items.
|
// Set constructor. We work around that by manually adding the items.
|
||||||
|
|
|
@ -24,7 +24,7 @@ export {ControlValueAccessor} from './directives/control_value_accessor';
|
||||||
export {DefaultValueAccessor} from './directives/default_value_accessor';
|
export {DefaultValueAccessor} from './directives/default_value_accessor';
|
||||||
export {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
|
export {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
|
||||||
export {SelectControlValueAccessor} from './directives/select_control_value_accessor';
|
export {SelectControlValueAccessor} from './directives/select_control_value_accessor';
|
||||||
export {NgRequiredValidator} from './directives/validators';
|
export {NgValidator, NgRequiredValidator} from './directives/validators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {ControlValueAccessor} from './control_value_accessor';
|
import {ControlValueAccessor} from './control_value_accessor';
|
||||||
import {Validators} from '../validators';
|
|
||||||
import {Control} from '../model';
|
import {Control} from '../model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,11 +11,10 @@ import {Control} from '../model';
|
||||||
export class NgControl {
|
export class NgControl {
|
||||||
name: string = null;
|
name: string = null;
|
||||||
valueAccessor: ControlValueAccessor = null;
|
valueAccessor: ControlValueAccessor = null;
|
||||||
validator: Function;
|
|
||||||
|
|
||||||
|
get validator(): Function { return null; }
|
||||||
get path(): List<string> { return null; }
|
get path(): List<string> { return null; }
|
||||||
get control(): Control { return null; }
|
get control(): Control { return null; }
|
||||||
constructor() { this.validator = Validators.nullValidator; }
|
|
||||||
|
|
||||||
viewToModelUpdate(newValue: any): void {}
|
viewToModelUpdate(newValue: any): void {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
import {List, StringMapWrapper, StringMap} from 'angular2/src/facade/collection';
|
import {List, StringMapWrapper, StringMap} from 'angular2/src/facade/collection';
|
||||||
import {Directive, Ancestor, onDestroy, onChange} from 'angular2/angular2';
|
|
||||||
|
import {Directive, Ancestor, onDestroy, onChange, Query, QueryList} from 'angular2/angular2';
|
||||||
import {forwardRef, Binding, Inject} from 'angular2/di';
|
import {forwardRef, Binding, Inject} from 'angular2/di';
|
||||||
|
|
||||||
import {ControlContainer} from './control_container';
|
import {ControlContainer} from './control_container';
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
import {controlPath} from './shared';
|
import {NgValidator} from './validators';
|
||||||
|
import {controlPath, composeNgValidator} from './shared';
|
||||||
import {Control} from '../model';
|
import {Control} from '../model';
|
||||||
|
|
||||||
const controlNameBinding =
|
const controlNameBinding =
|
||||||
|
@ -82,13 +84,17 @@ export class NgControlName extends NgControl {
|
||||||
_parent: ControlContainer;
|
_parent: ControlContainer;
|
||||||
ngModel: EventEmitter;
|
ngModel: EventEmitter;
|
||||||
model: any;
|
model: any;
|
||||||
|
ngValidators: QueryList<NgValidator>;
|
||||||
_added: boolean;
|
_added: boolean;
|
||||||
|
|
||||||
constructor(@Ancestor() _parent: ControlContainer) {
|
// Scope the query once https://github.com/angular/angular/issues/2603 is fixed
|
||||||
|
constructor(@Ancestor() parent: ControlContainer,
|
||||||
|
@Query(NgValidator) ngValidators: QueryList<NgValidator>) {
|
||||||
super();
|
super();
|
||||||
this._parent = _parent;
|
this._parent = parent;
|
||||||
this.ngModel = new EventEmitter();
|
this.ngModel = new EventEmitter();
|
||||||
this._added = false;
|
this._added = false;
|
||||||
|
this.ngValidators = ngValidators;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(c: StringMap<string, any>) {
|
onChange(c: StringMap<string, any>) {
|
||||||
|
@ -101,7 +107,6 @@ export class NgControlName extends NgControl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
onDestroy() { this.formDirective.removeControl(this); }
|
onDestroy() { this.formDirective.removeControl(this); }
|
||||||
|
|
||||||
viewToModelUpdate(newValue: any): void { ObservableWrapper.callNext(this.ngModel, newValue); }
|
viewToModelUpdate(newValue: any): void { ObservableWrapper.callNext(this.ngModel, newValue); }
|
||||||
|
@ -111,4 +116,6 @@ export class NgControlName extends NgControl {
|
||||||
get formDirective(): any { return this._parent.formDirective; }
|
get formDirective(): any { return this._parent.formDirective; }
|
||||||
|
|
||||||
get control(): Control { return this.formDirective.getControl(this); }
|
get control(): Control { return this.formDirective.getControl(this); }
|
||||||
|
|
||||||
|
get validator(): Function { return composeNgValidator(this.ngValidators); }
|
||||||
}
|
}
|
|
@ -2,12 +2,13 @@ import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
|
|
||||||
import {Directive, Ancestor, onChange} from 'angular2/angular2';
|
import {Directive, Ancestor, onChange, Query, QueryList} from 'angular2/angular2';
|
||||||
import {forwardRef, Binding} from 'angular2/di';
|
import {forwardRef, Binding} from 'angular2/di';
|
||||||
|
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
import {Control} from '../model';
|
import {Control} from '../model';
|
||||||
import {setUpControl} from './shared';
|
import {NgValidator} from './validators';
|
||||||
|
import {setUpControl, composeNgValidator} from './shared';
|
||||||
|
|
||||||
const formControlBinding =
|
const formControlBinding =
|
||||||
CONST_EXPR(new Binding(NgControl, {toAlias: forwardRef(() => NgFormControl)}));
|
CONST_EXPR(new Binding(NgControl, {toAlias: forwardRef(() => NgFormControl)}));
|
||||||
|
@ -72,11 +73,14 @@ export class NgFormControl extends NgControl {
|
||||||
ngModel: EventEmitter;
|
ngModel: EventEmitter;
|
||||||
_added: boolean;
|
_added: boolean;
|
||||||
model: any;
|
model: any;
|
||||||
|
ngValidators: QueryList<NgValidator>;
|
||||||
|
|
||||||
constructor() {
|
// Scope the query once https://github.com/angular/angular/issues/2603 is fixed
|
||||||
|
constructor(@Query(NgValidator) ngValidators: QueryList<NgValidator>) {
|
||||||
super();
|
super();
|
||||||
this.ngModel = new EventEmitter();
|
this.ngModel = new EventEmitter();
|
||||||
this._added = false;
|
this._added = false;
|
||||||
|
this.ngValidators = ngValidators;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(c) {
|
onChange(c) {
|
||||||
|
@ -90,9 +94,11 @@ export class NgFormControl extends NgControl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get path(): List<string> { return []; }
|
||||||
|
|
||||||
get control(): Control { return this.form; }
|
get control(): Control { return this.form; }
|
||||||
|
|
||||||
get path(): List<string> { return []; }
|
get validator(): Function { return composeNgValidator(this.ngValidators); }
|
||||||
|
|
||||||
viewToModelUpdate(newValue: any): void { ObservableWrapper.callNext(this.ngModel, newValue); }
|
viewToModelUpdate(newValue: any): void { ObservableWrapper.callNext(this.ngModel, newValue); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,13 @@ import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
import {Directive, Ancestor, onChange} from 'angular2/angular2';
|
import {Directive, Ancestor, onChange, QueryList, Query} from 'angular2/angular2';
|
||||||
import {forwardRef, Binding} from 'angular2/di';
|
import {forwardRef, Binding} from 'angular2/di';
|
||||||
|
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
import {Control} from '../model';
|
import {Control} from '../model';
|
||||||
import {setUpControl} from './shared';
|
import {NgValidator} from './validators';
|
||||||
|
import {setUpControl, composeNgValidator} from './shared';
|
||||||
|
|
||||||
const formControlBinding = CONST_EXPR(new Binding(NgControl, {toAlias: forwardRef(() => NgModel)}));
|
const formControlBinding = CONST_EXPR(new Binding(NgControl, {toAlias: forwardRef(() => NgModel)}));
|
||||||
|
|
||||||
|
@ -42,13 +43,20 @@ export class NgModel extends NgControl {
|
||||||
_added = false;
|
_added = false;
|
||||||
ngModel = new EventEmitter();
|
ngModel = new EventEmitter();
|
||||||
model: any;
|
model: any;
|
||||||
|
ngValidators: QueryList<NgValidator>;
|
||||||
|
|
||||||
|
// Scope the query once https://github.com/angular/angular/issues/2603 is fixed
|
||||||
|
constructor(@Query(NgValidator) ngValidators: QueryList<NgValidator>) {
|
||||||
|
super();
|
||||||
|
this.ngValidators = ngValidators;
|
||||||
|
}
|
||||||
|
|
||||||
onChange(c) {
|
onChange(c) {
|
||||||
if (!this._added) {
|
if (!this._added) {
|
||||||
setUpControl(this._control, this);
|
setUpControl(this._control, this);
|
||||||
this.control.updateValidity();
|
this._control.updateValidity();
|
||||||
this._added = true;
|
this._added = true;
|
||||||
};
|
}
|
||||||
|
|
||||||
if (StringMapWrapper.contains(c, "model")) {
|
if (StringMapWrapper.contains(c, "model")) {
|
||||||
this._control.updateValue(this.model);
|
this._control.updateValue(this.model);
|
||||||
|
@ -59,5 +67,7 @@ export class NgModel extends NgControl {
|
||||||
|
|
||||||
get path(): List<string> { return []; }
|
get path(): List<string> { return []; }
|
||||||
|
|
||||||
|
get validator(): Function { return composeNgValidator(this.ngValidators); }
|
||||||
|
|
||||||
viewToModelUpdate(newValue: any): void { ObservableWrapper.callNext(this.ngModel, newValue); }
|
viewToModelUpdate(newValue: any): void { ObservableWrapper.callNext(this.ngModel, newValue); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper, iterableToList} from 'angular2/src/facade/collection';
|
||||||
import {isBlank, BaseException} from 'angular2/src/facade/lang';
|
import {isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
import {ControlContainer} from './control_container';
|
import {ControlContainer} from './control_container';
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
|
import {NgValidator} from './validators';
|
||||||
import {Control} from '../model';
|
import {Control} from '../model';
|
||||||
import {Validators} from '../validators';
|
import {Validators} from '../validators';
|
||||||
import {Renderer, ElementRef} from 'angular2/angular2';
|
import {Renderer, ElementRef, QueryList} from 'angular2/angular2';
|
||||||
|
|
||||||
|
|
||||||
export function controlPath(name, parent: ControlContainer) {
|
export function controlPath(name, parent: ControlContainer) {
|
||||||
|
@ -35,6 +36,11 @@ export function setUpControl(c: Control, dir: NgControl) {
|
||||||
dir.valueAccessor.registerOnTouched(() => c.markAsTouched());
|
dir.valueAccessor.registerOnTouched(() => c.markAsTouched());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function composeNgValidator(ngValidators: QueryList<NgValidator>): Function {
|
||||||
|
if (isBlank(ngValidators)) return Validators.nullValidator;
|
||||||
|
return Validators.compose(iterableToList(ngValidators).map(v => v.validator));
|
||||||
|
}
|
||||||
|
|
||||||
function _throwError(dir: NgControl, message: string): void {
|
function _throwError(dir: NgControl, message: string): void {
|
||||||
var path = ListWrapper.join(dir.path, " -> ");
|
var path = ListWrapper.join(dir.path, " -> ");
|
||||||
throw new BaseException(`${message} '${path}'`);
|
throw new BaseException(`${message} '${path}'`);
|
||||||
|
|
|
@ -1,10 +1,19 @@
|
||||||
|
import {forwardRef, Binding} from 'angular2/di';
|
||||||
|
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
import {Directive} from '../../../angular2';
|
import {Directive} from '../../../angular2';
|
||||||
import {Validators} from '../validators';
|
import {Validators} from '../validators';
|
||||||
import {NgControl} from '../directives';
|
|
||||||
|
|
||||||
@Directive({selector: '[required][ng-control],[required][ng-form-control],[required][ng-model]'})
|
export class NgValidator {
|
||||||
export class NgRequiredValidator {
|
get validator(): Function { throw "Is not implemented"; }
|
||||||
constructor(c: NgControl) {
|
}
|
||||||
c.validator = Validators.compose([c.validator, Validators.required]);
|
|
||||||
}
|
const requiredValidatorBinding =
|
||||||
|
CONST_EXPR(new Binding(NgValidator, {toAlias: forwardRef(() => NgRequiredValidator)}));
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[required][ng-control],[required][ng-form-control],[required][ng-model]',
|
||||||
|
hostInjector: [requiredValidatorBinding]
|
||||||
|
})
|
||||||
|
export class NgRequiredValidator extends NgValidator {
|
||||||
|
get validator(): Function { return Validators.required; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,9 @@ import {
|
||||||
AsyncTestCompleter,
|
AsyncTestCompleter,
|
||||||
inject
|
inject
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {QueryList} from 'angular2/angular2';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ControlGroup,
|
ControlGroup,
|
||||||
Control,
|
Control,
|
||||||
|
@ -22,7 +25,9 @@ import {
|
||||||
ControlValueAccessor,
|
ControlValueAccessor,
|
||||||
Validators,
|
Validators,
|
||||||
NgForm,
|
NgForm,
|
||||||
NgFormControl
|
NgModel,
|
||||||
|
NgFormControl,
|
||||||
|
NgRequiredValidator
|
||||||
} from 'angular2/forms';
|
} from 'angular2/forms';
|
||||||
|
|
||||||
class DummyControlValueAccessor implements ControlValueAccessor {
|
class DummyControlValueAccessor implements ControlValueAccessor {
|
||||||
|
@ -46,14 +51,14 @@ export function main() {
|
||||||
formModel = new ControlGroup({"login": new Control(null)});
|
formModel = new ControlGroup({"login": new Control(null)});
|
||||||
form.form = formModel;
|
form.form = formModel;
|
||||||
|
|
||||||
loginControlDir = new NgControlName(form);
|
loginControlDir = new NgControlName(form, new QueryList<any>());
|
||||||
loginControlDir.name = "login";
|
loginControlDir.name = "login";
|
||||||
loginControlDir.valueAccessor = new DummyControlValueAccessor();
|
loginControlDir.valueAccessor = new DummyControlValueAccessor();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("addControl", () => {
|
describe("addControl", () => {
|
||||||
it("should throw when no control found", () => {
|
it("should throw when no control found", () => {
|
||||||
var dir = new NgControlName(form);
|
var dir = new NgControlName(form, null);
|
||||||
dir.name = "invalidName";
|
dir.name = "invalidName";
|
||||||
|
|
||||||
expect(() => form.addControl(dir))
|
expect(() => form.addControl(dir))
|
||||||
|
@ -61,7 +66,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw when no value accessor", () => {
|
it("should throw when no value accessor", () => {
|
||||||
var dir = new NgControlName(form);
|
var dir = new NgControlName(form, null);
|
||||||
dir.name = "login";
|
dir.name = "login";
|
||||||
|
|
||||||
expect(() => form.addControl(dir))
|
expect(() => form.addControl(dir))
|
||||||
|
@ -69,7 +74,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set up validator", () => {
|
it("should set up validator", () => {
|
||||||
loginControlDir.validator = Validators.required;
|
loginControlDir.ngValidators.reset([new NgRequiredValidator()]);
|
||||||
|
|
||||||
expect(formModel.find(["login"]).valid).toBe(true);
|
expect(formModel.find(["login"]).valid).toBe(true);
|
||||||
|
|
||||||
|
@ -127,7 +132,7 @@ export function main() {
|
||||||
personControlGroupDir = new NgControlGroup(form);
|
personControlGroupDir = new NgControlGroup(form);
|
||||||
personControlGroupDir.name = "person";
|
personControlGroupDir.name = "person";
|
||||||
|
|
||||||
loginControlDir = new NgControlName(personControlGroupDir);
|
loginControlDir = new NgControlName(personControlGroupDir, null);
|
||||||
loginControlDir.name = "login";
|
loginControlDir.name = "login";
|
||||||
loginControlDir.valueAccessor = new DummyControlValueAccessor();
|
loginControlDir.valueAccessor = new DummyControlValueAccessor();
|
||||||
});
|
});
|
||||||
|
@ -168,7 +173,7 @@ export function main() {
|
||||||
var control;
|
var control;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
controlDir = new NgFormControl();
|
controlDir = new NgFormControl(new QueryList<any>());
|
||||||
controlDir.valueAccessor = new DummyControlValueAccessor();
|
controlDir.valueAccessor = new DummyControlValueAccessor();
|
||||||
|
|
||||||
control = new Control(null);
|
control = new Control(null);
|
||||||
|
@ -176,7 +181,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set up validator", () => {
|
it("should set up validator", () => {
|
||||||
controlDir.validator = Validators.required;
|
controlDir.ngValidators.reset([new NgRequiredValidator()]);
|
||||||
|
|
||||||
expect(control.valid).toBe(true);
|
expect(control.valid).toBe(true);
|
||||||
|
|
||||||
|
@ -186,5 +191,25 @@ export function main() {
|
||||||
expect(control.valid).toBe(false);
|
expect(control.valid).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("NgModel", () => {
|
||||||
|
var ngModel;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
ngModel = new NgModel(new QueryList<any>());
|
||||||
|
ngModel.valueAccessor = new DummyControlValueAccessor();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set up validator", () => {
|
||||||
|
ngModel.ngValidators.reset([new NgRequiredValidator()]);
|
||||||
|
|
||||||
|
expect(ngModel.control.valid).toBe(true);
|
||||||
|
|
||||||
|
// this will add the required validator and recalculate the validity
|
||||||
|
ngModel.onChange({});
|
||||||
|
|
||||||
|
expect(ngModel.control.valid).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue