refactor(forms): use multibindings instead of query to get a list of validators
BREAKING CHANGE Before: @Directive({selector: '[credit-card]', bindings: [new Binding(NgValidator, {toAlias: forwardRef(() => CreditCardValidator)})]}) class CreditCardValidator { get validator() { return CreditCardValidator.validate; } static validate(c): StringMap<string, boolean> {...} } After: function creditCardValidator(c): StringMap<string, boolean> {...} @Directive({selector: '[credit-card]', bindings: [new Binding(NG_VALIDATORS, {toValue: creditCardValidator, multi: true})]}) class CreditCardValidator {}
This commit is contained in:
parent
7736964a37
commit
79994b2abf
|
@ -32,8 +32,8 @@ export {
|
||||||
SelectControlValueAccessor
|
SelectControlValueAccessor
|
||||||
} from './src/forms/directives/select_control_value_accessor';
|
} from './src/forms/directives/select_control_value_accessor';
|
||||||
export {FORM_DIRECTIVES} from './src/forms/directives';
|
export {FORM_DIRECTIVES} from './src/forms/directives';
|
||||||
export {Validators} from './src/forms/validators';
|
export {NG_VALIDATORS, Validators} from './src/forms/validators';
|
||||||
export {NgValidator, NgRequiredValidator} from './src/forms/directives/validators';
|
export {DefaultValidators} from './src/forms/directives/validators';
|
||||||
export {FormBuilder} from './src/forms/form_builder';
|
export {FormBuilder} from './src/forms/form_builder';
|
||||||
|
|
||||||
import {FormBuilder} from './src/forms/form_builder';
|
import {FormBuilder} from './src/forms/form_builder';
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
SelectControlValueAccessor,
|
SelectControlValueAccessor,
|
||||||
NgSelectOption
|
NgSelectOption
|
||||||
} from './directives/select_control_value_accessor';
|
} from './directives/select_control_value_accessor';
|
||||||
import {NgRequiredValidator} from './directives/validators';
|
import {DefaultValidators} from './directives/validators';
|
||||||
|
|
||||||
export {NgControlName} from './directives/ng_control_name';
|
export {NgControlName} from './directives/ng_control_name';
|
||||||
export {NgFormControl} from './directives/ng_form_control';
|
export {NgFormControl} from './directives/ng_form_control';
|
||||||
|
@ -27,7 +27,7 @@ export {
|
||||||
SelectControlValueAccessor,
|
SelectControlValueAccessor,
|
||||||
NgSelectOption
|
NgSelectOption
|
||||||
} from './directives/select_control_value_accessor';
|
} from './directives/select_control_value_accessor';
|
||||||
export {NgValidator, NgRequiredValidator} from './directives/validators';
|
export {DefaultValidators} from './directives/validators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -49,5 +49,5 @@ export const FORM_DIRECTIVES: Type[] = CONST_EXPR([
|
||||||
CheckboxControlValueAccessor,
|
CheckboxControlValueAccessor,
|
||||||
SelectControlValueAccessor,
|
SelectControlValueAccessor,
|
||||||
|
|
||||||
NgRequiredValidator
|
DefaultValidators
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -4,13 +4,14 @@ import {StringMap} from 'angular2/src/core/facade/collection';
|
||||||
|
|
||||||
import {QueryList} from 'angular2/core';
|
import {QueryList} from 'angular2/core';
|
||||||
import {Query, Directive, LifecycleEvent} from 'angular2/metadata';
|
import {Query, Directive, LifecycleEvent} from 'angular2/metadata';
|
||||||
import {forwardRef, Host, SkipSelf, Binding, Inject} from 'angular2/di';
|
import {forwardRef, Host, SkipSelf, Binding, Inject, Optional} from 'angular2/di';
|
||||||
|
|
||||||
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 {controlPath, isPropertyUpdated} from './shared';
|
||||||
import {controlPath, composeNgValidator, isPropertyUpdated} from './shared';
|
|
||||||
import {Control} from '../model';
|
import {Control} from '../model';
|
||||||
|
import {Validators, NG_VALIDATORS} from '../validators';
|
||||||
|
|
||||||
|
|
||||||
const controlNameBinding =
|
const controlNameBinding =
|
||||||
CONST_EXPR(new Binding(NgControl, {toAlias: forwardRef(() => NgControlName)}));
|
CONST_EXPR(new Binding(NgControl, {toAlias: forwardRef(() => NgControlName)}));
|
||||||
|
@ -84,15 +85,15 @@ export class NgControlName extends NgControl {
|
||||||
update = new EventEmitter();
|
update = new EventEmitter();
|
||||||
model: any;
|
model: any;
|
||||||
viewModel: any;
|
viewModel: any;
|
||||||
ngValidators: QueryList<NgValidator>;
|
validators: Function[];
|
||||||
_added = false;
|
_added = false;
|
||||||
|
|
||||||
// Scope the query once https://github.com/angular/angular/issues/2603 is fixed
|
// Scope the query once https://github.com/angular/angular/issues/2603 is fixed
|
||||||
constructor(@Host() @SkipSelf() parent: ControlContainer,
|
constructor(@Host() @SkipSelf() parent: ControlContainer,
|
||||||
@Query(NgValidator) ngValidators: QueryList<NgValidator>) {
|
@Optional() @Inject(NG_VALIDATORS) validators: Function[]) {
|
||||||
super();
|
super();
|
||||||
this._parent = parent;
|
this._parent = parent;
|
||||||
this.ngValidators = ngValidators;
|
this.validators = validators;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChanges(c: StringMap<string, any>) {
|
onChanges(c: StringMap<string, any>) {
|
||||||
|
@ -119,5 +120,5 @@ export class NgControlName extends NgControl {
|
||||||
|
|
||||||
get control(): Control { return this.formDirective.getControl(this); }
|
get control(): Control { return this.formDirective.getControl(this); }
|
||||||
|
|
||||||
get validator(): Function { return composeNgValidator(this.ngValidators); }
|
get validator(): Function { return Validators.compose(this.validators); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,12 @@ import {EventEmitter, ObservableWrapper} from 'angular2/src/core/facade/async';
|
||||||
|
|
||||||
import {QueryList} from 'angular2/core';
|
import {QueryList} from 'angular2/core';
|
||||||
import {Query, Directive, LifecycleEvent} from 'angular2/metadata';
|
import {Query, Directive, LifecycleEvent} from 'angular2/metadata';
|
||||||
import {forwardRef, Binding} from 'angular2/di';
|
import {forwardRef, Binding, Inject, Optional} from 'angular2/di';
|
||||||
|
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
import {Control} from '../model';
|
import {Control} from '../model';
|
||||||
import {NgValidator} from './validators';
|
import {Validators, NG_VALIDATORS} from '../validators';
|
||||||
import {setUpControl, composeNgValidator, isPropertyUpdated} from './shared';
|
import {setUpControl, isPropertyUpdated} from './shared';
|
||||||
|
|
||||||
const formControlBinding =
|
const formControlBinding =
|
||||||
CONST_EXPR(new Binding(NgControl, {toAlias: forwardRef(() => NgFormControl)}));
|
CONST_EXPR(new Binding(NgControl, {toAlias: forwardRef(() => NgFormControl)}));
|
||||||
|
@ -72,12 +72,11 @@ export class NgFormControl extends NgControl {
|
||||||
_added = false;
|
_added = false;
|
||||||
model: any;
|
model: any;
|
||||||
viewModel: any;
|
viewModel: any;
|
||||||
ngValidators: QueryList<NgValidator>;
|
validators: Function[];
|
||||||
|
|
||||||
// Scope the query once https://github.com/angular/angular/issues/2603 is fixed
|
constructor(@Optional() @Inject(NG_VALIDATORS) validators: Function[]) {
|
||||||
constructor(@Query(NgValidator) ngValidators: QueryList<NgValidator>) {
|
|
||||||
super();
|
super();
|
||||||
this.ngValidators = ngValidators;
|
this.validators = validators;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChanges(c: StringMap<string, any>) {
|
onChanges(c: StringMap<string, any>) {
|
||||||
|
@ -95,7 +94,7 @@ export class NgFormControl extends NgControl {
|
||||||
|
|
||||||
get control(): Control { return this.form; }
|
get control(): Control { return this.form; }
|
||||||
|
|
||||||
get validator(): Function { return composeNgValidator(this.ngValidators); }
|
get validator(): Function { return Validators.compose(this.validators); }
|
||||||
|
|
||||||
viewToModelUpdate(newValue: any): void {
|
viewToModelUpdate(newValue: any): void {
|
||||||
this.viewModel = newValue;
|
this.viewModel = newValue;
|
||||||
|
|
|
@ -3,12 +3,12 @@ import {EventEmitter, ObservableWrapper} from 'angular2/src/core/facade/async';
|
||||||
|
|
||||||
import {QueryList} from 'angular2/core';
|
import {QueryList} from 'angular2/core';
|
||||||
import {Query, Directive, LifecycleEvent} from 'angular2/metadata';
|
import {Query, Directive, LifecycleEvent} from 'angular2/metadata';
|
||||||
import {forwardRef, Binding} from 'angular2/di';
|
import {forwardRef, Binding, Inject, Optional} from 'angular2/di';
|
||||||
|
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
import {Control} from '../model';
|
import {Control} from '../model';
|
||||||
import {NgValidator} from './validators';
|
import {Validators, NG_VALIDATORS} from '../validators';
|
||||||
import {setUpControl, composeNgValidator, isPropertyUpdated} from './shared';
|
import {setUpControl, isPropertyUpdated} from './shared';
|
||||||
|
|
||||||
const formControlBinding = CONST_EXPR(new Binding(NgControl, {toAlias: forwardRef(() => NgModel)}));
|
const formControlBinding = CONST_EXPR(new Binding(NgControl, {toAlias: forwardRef(() => NgModel)}));
|
||||||
|
|
||||||
|
@ -42,12 +42,11 @@ export class NgModel extends NgControl {
|
||||||
update = new EventEmitter();
|
update = new EventEmitter();
|
||||||
model: any;
|
model: any;
|
||||||
viewModel: any;
|
viewModel: any;
|
||||||
ngValidators: QueryList<NgValidator>;
|
validators: Function[];
|
||||||
|
|
||||||
// Scope the query once https://github.com/angular/angular/issues/2603 is fixed
|
constructor(@Optional() @Inject(NG_VALIDATORS) validators: Function[]) {
|
||||||
constructor(@Query(NgValidator) ngValidators: QueryList<NgValidator>) {
|
|
||||||
super();
|
super();
|
||||||
this.ngValidators = ngValidators;
|
this.validators = validators;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChanges(c: StringMap<string, any>) {
|
onChanges(c: StringMap<string, any>) {
|
||||||
|
@ -66,7 +65,7 @@ export class NgModel extends NgControl {
|
||||||
|
|
||||||
get path(): string[] { return []; }
|
get path(): string[] { return []; }
|
||||||
|
|
||||||
get validator(): Function { return composeNgValidator(this.ngValidators); }
|
get validator(): Function { return Validators.compose(this.validators); }
|
||||||
|
|
||||||
viewToModelUpdate(newValue: any): void {
|
viewToModelUpdate(newValue: any): void {
|
||||||
this.viewModel = newValue;
|
this.viewModel = newValue;
|
||||||
|
|
|
@ -3,7 +3,6 @@ import {isBlank, BaseException, looseIdentical} from 'angular2/src/core/facade/l
|
||||||
|
|
||||||
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} from 'angular2/render';
|
import {Renderer} from 'angular2/render';
|
||||||
|
@ -37,11 +36,6 @@ 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(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,19 +1,14 @@
|
||||||
import {forwardRef, Binding} from 'angular2/di';
|
import {forwardRef, OpaqueToken, Binding} from 'angular2/di';
|
||||||
import {CONST_EXPR} from 'angular2/src/core/facade/lang';
|
import {CONST_EXPR} from 'angular2/src/core/facade/lang';
|
||||||
import {Directive} from 'angular2/metadata';
|
import {Directive} from 'angular2/metadata';
|
||||||
import {Validators} from '../validators';
|
import {Validators, NG_VALIDATORS} from '../validators';
|
||||||
|
|
||||||
export class NgValidator {
|
const DEFAULT_VALIDATORS =
|
||||||
get validator(): Function { throw "Is not implemented"; }
|
CONST_EXPR(new Binding(NG_VALIDATORS, {toValue: Validators.required, multi: true}));
|
||||||
}
|
|
||||||
|
|
||||||
const requiredValidatorBinding =
|
|
||||||
CONST_EXPR(new Binding(NgValidator, {toAlias: forwardRef(() => NgRequiredValidator)}));
|
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[required][ng-control],[required][ng-form-control],[required][ng-model]',
|
selector: '[required][ng-control],[required][ng-form-control],[required][ng-model]',
|
||||||
bindings: [requiredValidatorBinding]
|
bindings: [DEFAULT_VALIDATORS]
|
||||||
})
|
})
|
||||||
export class NgRequiredValidator extends NgValidator {
|
export class DefaultValidators {
|
||||||
get validator(): Function { return Validators.required; }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
import {isBlank, isPresent} from 'angular2/src/core/facade/lang';
|
import {isBlank, isPresent} from 'angular2/src/core/facade/lang';
|
||||||
|
import {CONST_EXPR} from 'angular2/src/core/facade/lang';
|
||||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
import {ListWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
||||||
|
import {OpaqueToken} from 'angular2/di';
|
||||||
|
|
||||||
import * as modelModule from './model';
|
import * as modelModule from './model';
|
||||||
|
|
||||||
|
export const NG_VALIDATORS = CONST_EXPR(new OpaqueToken("NgValidators"));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a set of validators used by form controls.
|
* Provides a set of validators used by form controls.
|
||||||
*
|
*
|
||||||
|
@ -20,6 +24,8 @@ export class Validators {
|
||||||
static nullValidator(c: any): StringMap<string, boolean> { return null; }
|
static nullValidator(c: any): StringMap<string, boolean> { return null; }
|
||||||
|
|
||||||
static compose(validators: Function[]): Function {
|
static compose(validators: Function[]): Function {
|
||||||
|
if (isBlank(validators)) return Validators.nullValidator;
|
||||||
|
|
||||||
return function(c: modelModule.Control) {
|
return function(c: modelModule.Control) {
|
||||||
var res = ListWrapper.reduce(validators, (res, validator) => {
|
var res = ListWrapper.reduce(validators, (res, validator) => {
|
||||||
var errors = validator(c);
|
var errors = validator(c);
|
||||||
|
|
|
@ -14,8 +14,6 @@ import {
|
||||||
inject
|
inject
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {QueryList} from 'angular2/angular2';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ControlGroup,
|
ControlGroup,
|
||||||
Control,
|
Control,
|
||||||
|
@ -27,7 +25,7 @@ import {
|
||||||
NgForm,
|
NgForm,
|
||||||
NgModel,
|
NgModel,
|
||||||
NgFormControl,
|
NgFormControl,
|
||||||
NgRequiredValidator
|
DefaultValidators
|
||||||
} from 'angular2/forms';
|
} from 'angular2/forms';
|
||||||
|
|
||||||
class DummyControlValueAccessor implements ControlValueAccessor {
|
class DummyControlValueAccessor implements ControlValueAccessor {
|
||||||
|
@ -51,7 +49,7 @@ 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, new QueryList<any>());
|
loginControlDir = new NgControlName(form, []);
|
||||||
loginControlDir.name = "login";
|
loginControlDir.name = "login";
|
||||||
loginControlDir.valueAccessor = new DummyControlValueAccessor();
|
loginControlDir.valueAccessor = new DummyControlValueAccessor();
|
||||||
});
|
});
|
||||||
|
@ -85,7 +83,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set up validator", () => {
|
it("should set up validator", () => {
|
||||||
loginControlDir.ngValidators.reset([new NgRequiredValidator()]);
|
loginControlDir.validators = [Validators.required];
|
||||||
|
|
||||||
expect(formModel.find(["login"]).valid).toBe(true);
|
expect(formModel.find(["login"]).valid).toBe(true);
|
||||||
|
|
||||||
|
@ -220,7 +218,7 @@ export function main() {
|
||||||
var control;
|
var control;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
controlDir = new NgFormControl(new QueryList<any>());
|
controlDir = new NgFormControl([]);
|
||||||
controlDir.valueAccessor = new DummyControlValueAccessor();
|
controlDir.valueAccessor = new DummyControlValueAccessor();
|
||||||
|
|
||||||
control = new Control(null);
|
control = new Control(null);
|
||||||
|
@ -239,7 +237,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set up validator", () => {
|
it("should set up validator", () => {
|
||||||
controlDir.ngValidators.reset([new NgRequiredValidator()]);
|
controlDir.validators = [Validators.required];
|
||||||
|
|
||||||
expect(control.valid).toBe(true);
|
expect(control.valid).toBe(true);
|
||||||
|
|
||||||
|
@ -254,7 +252,7 @@ export function main() {
|
||||||
var ngModel;
|
var ngModel;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
ngModel = new NgModel(new QueryList<any>());
|
ngModel = new NgModel([]);
|
||||||
ngModel.valueAccessor = new DummyControlValueAccessor();
|
ngModel.valueAccessor = new DummyControlValueAccessor();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -271,7 +269,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set up validator", () => {
|
it("should set up validator", () => {
|
||||||
ngModel.ngValidators.reset([new NgRequiredValidator()]);
|
ngModel.validators = [Validators.required];
|
||||||
|
|
||||||
expect(ngModel.control.valid).toBe(true);
|
expect(ngModel.control.valid).toBe(true);
|
||||||
|
|
||||||
|
@ -291,7 +289,7 @@ export function main() {
|
||||||
|
|
||||||
var parent = new NgFormModel();
|
var parent = new NgFormModel();
|
||||||
parent.form = new ControlGroup({"name": formModel});
|
parent.form = new ControlGroup({"name": formModel});
|
||||||
controlNameDir = new NgControlName(parent, new QueryList<any>());
|
controlNameDir = new NgControlName(parent, []);
|
||||||
controlNameDir.name = "name";
|
controlNameDir.name = "name";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,9 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("compose", () => {
|
describe("compose", () => {
|
||||||
|
it("should return a null validator when given null",
|
||||||
|
() => { expect(Validators.compose(null)).toBe(Validators.nullValidator); });
|
||||||
|
|
||||||
it("should collect errors from all the validators", () => {
|
it("should collect errors from all the validators", () => {
|
||||||
var c = Validators.compose([validator("a", true), validator("b", true)]);
|
var c = Validators.compose([validator("a", true), validator("b", true)]);
|
||||||
expect(c(new Control(""))).toEqual({"a": true, "b": true});
|
expect(c(new Control(""))).toEqual({"a": true, "b": true});
|
||||||
|
|
Loading…
Reference in New Issue