diff --git a/modules/angular2/src/core/forms.ts b/modules/angular2/src/core/forms.ts index 31201aa37d..65b4cf0be6 100644 --- a/modules/angular2/src/core/forms.ts +++ b/modules/angular2/src/core/forms.ts @@ -34,7 +34,11 @@ export { } from './forms/directives/select_control_value_accessor'; export {FORM_DIRECTIVES} from './forms/directives'; export {NG_VALIDATORS, Validators} from './forms/validators'; -export {DefaultValidators} from './forms/directives/validators'; +export { + RequiredValidator, + MinLengthValidator, + MaxLengthValidator +} from './forms/directives/validators'; export {FormBuilder} from './forms/form_builder'; import {FormBuilder} from './forms/form_builder'; diff --git a/modules/angular2/src/core/forms/directives.ts b/modules/angular2/src/core/forms/directives.ts index f543619976..d1507894d2 100644 --- a/modules/angular2/src/core/forms/directives.ts +++ b/modules/angular2/src/core/forms/directives.ts @@ -12,7 +12,7 @@ import { SelectControlValueAccessor, NgSelectOption } from './directives/select_control_value_accessor'; -import {DefaultValidators} from './directives/validators'; +import {RequiredValidator, MinLengthValidator, MaxLengthValidator} from './directives/validators'; export {NgControlName} from './directives/ng_control_name'; export {NgFormControl} from './directives/ng_form_control'; @@ -28,7 +28,7 @@ export { SelectControlValueAccessor, NgSelectOption } from './directives/select_control_value_accessor'; -export {DefaultValidators} from './directives/validators'; +export {RequiredValidator, MinLengthValidator, MaxLengthValidator} from './directives/validators'; export {NgControlStatus} from './directives/ng_control_status'; /** @@ -62,5 +62,7 @@ export const FORM_DIRECTIVES: Type[] = CONST_EXPR([ SelectControlValueAccessor, NgControlStatus, - DefaultValidators + RequiredValidator, + MinLengthValidator, + MaxLengthValidator ]); diff --git a/modules/angular2/src/core/forms/directives/validators.ts b/modules/angular2/src/core/forms/directives/validators.ts index d80446cede..8cb69da8c8 100644 --- a/modules/angular2/src/core/forms/directives/validators.ts +++ b/modules/angular2/src/core/forms/directives/validators.ts @@ -1,14 +1,53 @@ import {forwardRef, Provider, OpaqueToken} from 'angular2/src/core/di'; import {CONST_EXPR} from 'angular2/src/core/facade/lang'; -import {Directive} from 'angular2/src/core/metadata'; +import {Attribute, Directive} from 'angular2/src/core/metadata'; import {Validators, NG_VALIDATORS} from '../validators'; +import {NumberWrapper} from "angular2/src/core/facade/lang"; -const DEFAULT_VALIDATORS = +const REQUIRED_VALIDATOR = CONST_EXPR(new Provider(NG_VALIDATORS, {useValue: Validators.required, multi: true})); @Directive({ selector: '[required][ng-control],[required][ng-form-control],[required][ng-model]', - bindings: [DEFAULT_VALIDATORS] + providers: [REQUIRED_VALIDATOR] }) -export class DefaultValidators { +export class RequiredValidator { } + +function createMinLengthValidator(dir): any { + return Validators.minLength(dir.minLength); +} +const MIN_LENGTH_VALIDATOR = CONST_EXPR(new Provider(NG_VALIDATORS, { + useFactory: createMinLengthValidator, + deps: [forwardRef(() => MinLengthValidator)], + multi: true +})); +@Directive({ + selector: '[minlength][ng-control],[minlength][ng-form-control],[minlength][ng-model]', + providers: [MIN_LENGTH_VALIDATOR] +}) +export class MinLengthValidator { + minLength: number; + constructor(@Attribute("minlength") minLength: string) { + this.minLength = NumberWrapper.parseInt(minLength, 10); + } +} + +function createMaxLengthValidator(dir): any { + return Validators.maxLength(dir.maxLength); +} +const MAX_LENGTH_VALIDATOR = CONST_EXPR(new Provider(NG_VALIDATORS, { + useFactory: createMaxLengthValidator, + deps: [forwardRef(() => MaxLengthValidator)], + multi: true +})); +@Directive({ + selector: '[maxlength][ng-control],[maxlength][ng-form-control],[maxlength][ng-model]', + providers: [MAX_LENGTH_VALIDATOR] +}) +export class MaxLengthValidator { + maxLength: number; + constructor(@Attribute("maxlength") maxLength: string) { + this.maxLength = NumberWrapper.parseInt(maxLength, 10); + } +} \ No newline at end of file diff --git a/modules/angular2/src/core/forms/validators.ts b/modules/angular2/src/core/forms/validators.ts index 08a738fdff..e1b75bfea6 100644 --- a/modules/angular2/src/core/forms/validators.ts +++ b/modules/angular2/src/core/forms/validators.ts @@ -21,6 +21,26 @@ export class Validators { return isBlank(control.value) || control.value == "" ? {"required": true} : null; } + static minLength(minLength: number): Function { + return (control: modelModule.Control): {[key: string]: any} => { + if (isPresent(Validators.required(control))) return null; + var v: string = control.value; + return v.length < minLength ? + {"minlength": {"requiredLength": minLength, "actualLength": v.length}} : + null; + }; + } + + static maxLength(maxLength: number): Function { + return (control: modelModule.Control): {[key: string]: any} => { + if (isPresent(Validators.required(control))) return null; + var v: string = control.value; + return v.length > maxLength ? + {"maxlength": {"requiredLength": maxLength, "actualLength": v.length}} : + null; + }; + } + static nullValidator(c: any): {[key: string]: boolean} { return null; } static compose(validators: Function[]): Function { diff --git a/modules/angular2/test/core/forms/directives_spec.ts b/modules/angular2/test/core/forms/directives_spec.ts index dd489555ff..e53d8e5551 100644 --- a/modules/angular2/test/core/forms/directives_spec.ts +++ b/modules/angular2/test/core/forms/directives_spec.ts @@ -28,7 +28,6 @@ import { NgForm, NgModel, NgFormControl, - DefaultValidators, NgControl, DefaultValueAccessor, CheckboxControlValueAccessor, diff --git a/modules/angular2/test/core/forms/integration_spec.ts b/modules/angular2/test/core/forms/integration_spec.ts index 9e68a21f24..d8aa08787a 100644 --- a/modules/angular2/test/core/forms/integration_spec.ts +++ b/modules/angular2/test/core/forms/integration_spec.ts @@ -349,23 +349,43 @@ export function main() { describe("validations", () => { it("should use validators defined in html", inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { - var form = new ControlGroup({"login": new Control("aa")}); + var form = new ControlGroup( + {"login": new Control(""), "min": new Control(""), "max": new Control("")}); var t = `
+ +
`; tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => { rootTC.debugElement.componentInstance.form = form; rootTC.detectChanges(); + + var required = rootTC.debugElement.query(By.css("[required]")); + var minLength = rootTC.debugElement.query(By.css("[minlength]")); + var maxLength = rootTC.debugElement.query(By.css("[maxlength]")); + + required.nativeElement.value = ""; + minLength.nativeElement.value = "1"; + maxLength.nativeElement.value = "1234"; + dispatchEvent(required.nativeElement, "change"); + dispatchEvent(minLength.nativeElement, "change"); + dispatchEvent(maxLength.nativeElement, "change"); + + expect(form.hasError("required", ["login"])).toEqual(true); + expect(form.hasError("minlength", ["min"])).toEqual(true); + expect(form.hasError("maxlength", ["max"])).toEqual(true); + + required.nativeElement.value = "1"; + minLength.nativeElement.value = "123"; + maxLength.nativeElement.value = "123"; + dispatchEvent(required.nativeElement, "change"); + dispatchEvent(minLength.nativeElement, "change"); + dispatchEvent(maxLength.nativeElement, "change"); + expect(form.valid).toEqual(true); - var input = rootTC.debugElement.query(By.css("input")); - - input.nativeElement.value = ""; - dispatchEvent(input.nativeElement, "change"); - - expect(form.valid).toEqual(false); async.done(); }); })); diff --git a/modules/angular2/test/core/forms/validators_spec.ts b/modules/angular2/test/core/forms/validators_spec.ts index 5101388c46..c65d59030a 100644 --- a/modules/angular2/test/core/forms/validators_spec.ts +++ b/modules/angular2/test/core/forms/validators_spec.ts @@ -32,6 +32,38 @@ export function main() { () => { expect(Validators.required(new Control("not empty"))).toEqual(null); }); }); + describe("minLength", () => { + it("should not error on an empty string", + () => { expect(Validators.minLength(2)(new Control(""))).toEqual(null); }); + + it("should not error on null", + () => { expect(Validators.minLength(2)(new Control(null))).toEqual(null); }); + + it("should not error on valid strings", + () => { expect(Validators.minLength(2)(new Control("aa"))).toEqual(null); }); + + it("should error on short strings", () => { + expect(Validators.minLength(2)(new Control("a"))) + .toEqual({"minlength": {"requiredLength": 2, "actualLength": 1}}); + }); + }); + + describe("maxLength", () => { + it("should not error on an empty string", + () => { expect(Validators.maxLength(2)(new Control(""))).toEqual(null); }); + + it("should not error on null", + () => { expect(Validators.maxLength(2)(new Control(null))).toEqual(null); }); + + it("should not error on valid strings", + () => { expect(Validators.maxLength(2)(new Control("aa"))).toEqual(null); }); + + it("should error on short strings", () => { + expect(Validators.maxLength(2)(new Control("aaa"))) + .toEqual({"maxlength": {"requiredLength": 2, "actualLength": 3}}); + }); + }); + describe("compose", () => { it("should return a null validator when given null", () => { expect(Validators.compose(null)).toBe(Validators.nullValidator); }); diff --git a/modules/angular2/test/public_api_spec.ts b/modules/angular2/test/public_api_spec.ts index c11540a5db..1f2051b59c 100644 --- a/modules/angular2/test/public_api_spec.ts +++ b/modules/angular2/test/public_api_spec.ts @@ -396,7 +396,13 @@ var NG_API = [ 'DebugElement.queryAll()', 'DecimalPipe', 'DecimalPipe.transform()', - 'DefaultValidators', + 'RequiredValidator', + 'MinLengthValidator', + 'MinLengthValidator.minLength', + 'MinLengthValidator.minLength=', + 'MaxLengthValidator', + 'MaxLengthValidator.maxLength', + 'MaxLengthValidator.maxLength=', 'DefaultValueAccessor', 'DefaultValueAccessor.onChange', 'DefaultValueAccessor.onChange=', @@ -995,6 +1001,8 @@ var NG_API = [ 'Validators#group()', 'Validators#nullValidator()', 'Validators#required()', + 'Validators#minLength()', + 'Validators#maxLength()', 'Validators', 'View', 'View.directives',