parent
6e2abcd5fc
commit
81925fa66d
|
@ -24,7 +24,7 @@ import {FormGroupDirective} from './directives/reactive_directives/form_group_di
|
||||||
import {FormArrayName, FormGroupName} from './directives/reactive_directives/form_group_name';
|
import {FormArrayName, FormGroupName} from './directives/reactive_directives/form_group_name';
|
||||||
import {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor';
|
import {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor';
|
||||||
import {NgSelectMultipleOption, SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor';
|
import {NgSelectMultipleOption, SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor';
|
||||||
import {CheckboxRequiredValidator, EmailValidator, MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator} from './directives/validators';
|
import {CheckboxRequiredValidator, EmailValidator, MaxLengthValidator, MaxValidator, MinLengthValidator, MinValidator, PatternValidator, RequiredValidator} from './directives/validators';
|
||||||
|
|
||||||
export {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
|
export {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
|
||||||
export {ControlValueAccessor} from './directives/control_value_accessor';
|
export {ControlValueAccessor} from './directives/control_value_accessor';
|
||||||
|
@ -58,7 +58,9 @@ export const SHARED_FORM_DIRECTIVES: Type<any>[] = [
|
||||||
NgControlStatus,
|
NgControlStatus,
|
||||||
NgControlStatusGroup,
|
NgControlStatusGroup,
|
||||||
RequiredValidator,
|
RequiredValidator,
|
||||||
|
MinValidator,
|
||||||
MinLengthValidator,
|
MinLengthValidator,
|
||||||
|
MaxValidator,
|
||||||
MaxLengthValidator,
|
MaxLengthValidator,
|
||||||
PatternValidator,
|
PatternValidator,
|
||||||
CheckboxRequiredValidator,
|
CheckboxRequiredValidator,
|
||||||
|
|
|
@ -57,6 +57,7 @@ export const CHECKBOX_REQUIRED_VALIDATOR: Provider = {
|
||||||
multi: true
|
multi: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Directive that adds the `required` validator to any controls marked with the
|
* A Directive that adds the `required` validator to any controls marked with the
|
||||||
* `required` attribute, via the {@link NG_VALIDATORS} binding.
|
* `required` attribute, via the {@link NG_VALIDATORS} binding.
|
||||||
|
@ -94,6 +95,84 @@ export class RequiredValidator implements Validator {
|
||||||
registerOnValidatorChange(fn: () => void): void { this._onChange = fn; }
|
registerOnValidatorChange(fn: () => void): void { this._onChange = fn; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const MIN_VALIDATOR: Provider = {
|
||||||
|
provide: NG_VALIDATORS,
|
||||||
|
useExisting: forwardRef(() => MinValidator),
|
||||||
|
multi: true
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A directive which installs the {@link MinValidator} for any `formControlName`,
|
||||||
|
* `formControl`, or control with `ngModel` that also has a `min` attribute.
|
||||||
|
*
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
@Directive({
|
||||||
|
selector: '[min][formControlName],[min][formControl],[min][ngModel]',
|
||||||
|
providers: [MIN_VALIDATOR],
|
||||||
|
host: {'[attr.min]': 'min ? min : null'}
|
||||||
|
})
|
||||||
|
export class MinValidator implements Validator,
|
||||||
|
OnChanges {
|
||||||
|
private _validator: ValidatorFn;
|
||||||
|
private _onChange: () => void;
|
||||||
|
|
||||||
|
@Input() min: string;
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if ('min' in changes) {
|
||||||
|
this._createValidator();
|
||||||
|
if (this._onChange) this._onChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(c: AbstractControl): ValidationErrors|null { return this._validator(c); }
|
||||||
|
|
||||||
|
registerOnValidatorChange(fn: () => void): void { this._onChange = fn; }
|
||||||
|
|
||||||
|
private _createValidator(): void { this._validator = Validators.min(parseInt(this.min, 10)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const MAX_VALIDATOR: Provider = {
|
||||||
|
provide: NG_VALIDATORS,
|
||||||
|
useExisting: forwardRef(() => MaxValidator),
|
||||||
|
multi: true
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A directive which installs the {@link MaxValidator} for any `formControlName`,
|
||||||
|
* `formControl`, or control with `ngModel` that also has a `min` attribute.
|
||||||
|
*
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
@Directive({
|
||||||
|
selector: '[max][formControlName],[max][formControl],[max][ngModel]',
|
||||||
|
providers: [MAX_VALIDATOR],
|
||||||
|
host: {'[attr.max]': 'max ? max : null'}
|
||||||
|
})
|
||||||
|
export class MaxValidator implements Validator,
|
||||||
|
OnChanges {
|
||||||
|
private _validator: ValidatorFn;
|
||||||
|
private _onChange: () => void;
|
||||||
|
|
||||||
|
@Input() max: string;
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if ('max' in changes) {
|
||||||
|
this._createValidator();
|
||||||
|
if (this._onChange) this._onChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(c: AbstractControl): ValidationErrors|null { return this._validator(c); }
|
||||||
|
|
||||||
|
registerOnValidatorChange(fn: () => void): void { this._onChange = fn; }
|
||||||
|
|
||||||
|
private _createValidator(): void { this._validator = Validators.max(parseInt(this.max, 10)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Directive that adds the `required` validator to checkbox controls marked with the
|
* A Directive that adds the `required` validator to checkbox controls marked with the
|
||||||
* `required` attribute, via the {@link NG_VALIDATORS} binding.
|
* `required` attribute, via the {@link NG_VALIDATORS} binding.
|
||||||
|
|
|
@ -38,7 +38,7 @@ export {FormArrayName} from './directives/reactive_directives/form_group_name';
|
||||||
export {FormGroupName} from './directives/reactive_directives/form_group_name';
|
export {FormGroupName} from './directives/reactive_directives/form_group_name';
|
||||||
export {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor';
|
export {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor';
|
||||||
export {SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor';
|
export {SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor';
|
||||||
export {AsyncValidator, AsyncValidatorFn, CheckboxRequiredValidator, EmailValidator, MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator, ValidationErrors, Validator, ValidatorFn} from './directives/validators';
|
export {AsyncValidator, AsyncValidatorFn, CheckboxRequiredValidator, EmailValidator, MaxLengthValidator, MaxValidator, MinLengthValidator, MinValidator, PatternValidator, RequiredValidator, ValidationErrors, Validator, ValidatorFn} from './directives/validators';
|
||||||
export {FormBuilder} from './form_builder';
|
export {FormBuilder} from './form_builder';
|
||||||
export {AbstractControl, FormArray, FormControl, FormGroup} from './model';
|
export {AbstractControl, FormArray, FormControl, FormGroup} from './model';
|
||||||
export {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from './validators';
|
export {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from './validators';
|
||||||
|
|
|
@ -62,6 +62,32 @@ const EMAIL_REGEXP =
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export class Validators {
|
export class Validators {
|
||||||
|
/**
|
||||||
|
* Validator that requires controls to have a value greater than a number.
|
||||||
|
*/
|
||||||
|
static min(min: number): ValidatorFn {
|
||||||
|
return (control: AbstractControl): ValidationErrors | null => {
|
||||||
|
if (isEmptyInputValue(control.value)) {
|
||||||
|
return null; // don't validate empty values to allow optional controls
|
||||||
|
}
|
||||||
|
const value = parseFloat(control.value);
|
||||||
|
return isNaN(value) || value < min ? {'min': {'min': min, 'actual': control.value}} : null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validator that requires controls to have a value less than a number.
|
||||||
|
*/
|
||||||
|
static max(max: number): ValidatorFn {
|
||||||
|
return (control: AbstractControl): ValidationErrors | null => {
|
||||||
|
if (isEmptyInputValue(control.value)) {
|
||||||
|
return null; // don't validate empty values to allow optional controls
|
||||||
|
}
|
||||||
|
const value = parseFloat(control.value);
|
||||||
|
return isNaN(value) || value > max ? {'max': {'max': max, 'actual': control.value}} : null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validator that requires controls to have a non-empty value.
|
* Validator that requires controls to have a non-empty value.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -879,6 +879,97 @@ export function main() {
|
||||||
|
|
||||||
describe('validation directives', () => {
|
describe('validation directives', () => {
|
||||||
|
|
||||||
|
it('should should validate max', fakeAsync(() => {
|
||||||
|
const fixture = initTest(NgModelMaxValidator);
|
||||||
|
fixture.componentInstance.max = 10;
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
const max = fixture.debugElement.query(By.css('input'));
|
||||||
|
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||||
|
|
||||||
|
max.nativeElement.value = '';
|
||||||
|
dispatchEvent(max.nativeElement, 'input');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(form.valid).toEqual(true);
|
||||||
|
|
||||||
|
max.nativeElement.value = 11;
|
||||||
|
dispatchEvent(max.nativeElement, 'input');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(form.valid).toEqual(false);
|
||||||
|
|
||||||
|
max.nativeElement.value = 9;
|
||||||
|
dispatchEvent(max.nativeElement, 'input');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(form.valid).toEqual(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should validate max for strings', fakeAsync(() => {
|
||||||
|
const fixture = initTest(NgModelMaxValidator);
|
||||||
|
fixture.componentInstance.max = 10;
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
const max = fixture.debugElement.query(By.css('input'));
|
||||||
|
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||||
|
|
||||||
|
max.nativeElement.value = '11';
|
||||||
|
dispatchEvent(max.nativeElement, 'input');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(form.valid).toEqual(false);
|
||||||
|
|
||||||
|
max.nativeElement.value = '9';
|
||||||
|
dispatchEvent(max.nativeElement, 'input');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(form.valid).toEqual(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should should validate min', fakeAsync(() => {
|
||||||
|
const fixture = initTest(NgModelMinValidator);
|
||||||
|
fixture.componentInstance.min = 10;
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
const min = fixture.debugElement.query(By.css('input'));
|
||||||
|
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||||
|
|
||||||
|
min.nativeElement.value = '';
|
||||||
|
dispatchEvent(min.nativeElement, 'input');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(form.valid).toEqual(true);
|
||||||
|
|
||||||
|
min.nativeElement.value = 11;
|
||||||
|
dispatchEvent(min.nativeElement, 'input');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(form.valid).toEqual(true);
|
||||||
|
|
||||||
|
min.nativeElement.value = 9;
|
||||||
|
dispatchEvent(min.nativeElement, 'input');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(form.valid).toEqual(false);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should should validate min for strings', fakeAsync(() => {
|
||||||
|
const fixture = initTest(NgModelMinValidator);
|
||||||
|
fixture.componentInstance.min = 10;
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
const min = fixture.debugElement.query(By.css('input'));
|
||||||
|
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||||
|
|
||||||
|
min.nativeElement.value = '11';
|
||||||
|
dispatchEvent(min.nativeElement, 'input');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(form.valid).toEqual(true);
|
||||||
|
|
||||||
|
min.nativeElement.value = '9';
|
||||||
|
dispatchEvent(min.nativeElement, 'input');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(form.valid).toEqual(false);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
it('required validator should validate checkbox', fakeAsync(() => {
|
it('required validator should validate checkbox', fakeAsync(() => {
|
||||||
const fixture = initTest(NgModelCheckboxRequiredValidator);
|
const fixture = initTest(NgModelCheckboxRequiredValidator);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -1539,6 +1630,22 @@ class NgModelMultipleValidators {
|
||||||
pattern: string|RegExp;
|
pattern: string|RegExp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ng-model-max',
|
||||||
|
template: `<form><input name="tovalidate" type="number" ngModel [max]="max"></form>`
|
||||||
|
})
|
||||||
|
class NgModelMaxValidator {
|
||||||
|
max: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ng-model-min',
|
||||||
|
template: `<form><input name="tovalidate" type="number" ngModel [min]="min"></form>`
|
||||||
|
})
|
||||||
|
class NgModelMinValidator {
|
||||||
|
min: number;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ng-model-checkbox-validator',
|
selector: 'ng-model-checkbox-validator',
|
||||||
template:
|
template:
|
||||||
|
|
|
@ -39,6 +39,65 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Validators', () => {
|
describe('Validators', () => {
|
||||||
|
describe('min', () => {
|
||||||
|
it('should not error on an empty string',
|
||||||
|
() => { expect(Validators.min(2)(new FormControl(''))).toBeNull(); });
|
||||||
|
|
||||||
|
it('should not error on null',
|
||||||
|
() => { expect(Validators.min(2)(new FormControl(null))).toBeNull(); });
|
||||||
|
|
||||||
|
it('should not error on undefined',
|
||||||
|
() => { expect(Validators.min(2)(new FormControl(undefined))).toBeNull(); });
|
||||||
|
|
||||||
|
it('should error on non numbers', () => {
|
||||||
|
expect(Validators.min(2)(new FormControl('a'))).toEqual({'min': {'min': 2, 'actual': 'a'}});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error on small values', () => {
|
||||||
|
expect(Validators.min(2)(new FormControl(1))).toEqual({'min': {'min': 2, 'actual': 1}});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not error on big values',
|
||||||
|
() => { expect(Validators.min(2)(new FormControl(3))).toBeNull(); });
|
||||||
|
|
||||||
|
it('should not error on equal values',
|
||||||
|
() => { expect(Validators.min(2)(new FormControl(2))).toBeNull(); });
|
||||||
|
|
||||||
|
it('should not error on equal values when value is string',
|
||||||
|
() => { expect(Validators.min(2)(new FormControl('2'))).toBeNull(); });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('max', () => {
|
||||||
|
it('should not error on an empty string',
|
||||||
|
() => { expect(Validators.max(2)(new FormControl(''))).toBeNull(); });
|
||||||
|
|
||||||
|
it('should not error on null',
|
||||||
|
() => { expect(Validators.max(2)(new FormControl(null))).toBeNull(); });
|
||||||
|
|
||||||
|
it('should not error on undefined',
|
||||||
|
() => { expect(Validators.max(2)(new FormControl(undefined))).toBeNull(); });
|
||||||
|
|
||||||
|
it('should error on non numbers', () => {
|
||||||
|
expect(Validators.max(2)(new FormControl('aaa'))).toEqual({
|
||||||
|
'max': {'max': 2, 'actual': 'aaa'}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error on big values', () => {
|
||||||
|
expect(Validators.max(2)(new FormControl(3))).toEqual({'max': {'max': 2, 'actual': 3}});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not error on small values',
|
||||||
|
() => { expect(Validators.max(2)(new FormControl(1))).toBeNull(); });
|
||||||
|
|
||||||
|
it('should not error on equal values',
|
||||||
|
() => { expect(Validators.max(2)(new FormControl(2))).toBeNull(); });
|
||||||
|
|
||||||
|
it('should not error on equal values when value is string',
|
||||||
|
() => { expect(Validators.max(2)(new FormControl('2'))).toBeNull(); });
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('required', () => {
|
describe('required', () => {
|
||||||
it('should error on an empty string',
|
it('should error on an empty string',
|
||||||
() => { expect(Validators.required(new FormControl(''))).toEqual({'required': true}); });
|
() => { expect(Validators.required(new FormControl(''))).toEqual({'required': true}); });
|
||||||
|
|
|
@ -352,6 +352,14 @@ export declare class MaxLengthValidator implements Validator, OnChanges {
|
||||||
validate(c: AbstractControl): ValidationErrors | null;
|
validate(c: AbstractControl): ValidationErrors | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @experimental */
|
||||||
|
export declare class MaxValidator implements Validator, OnChanges {
|
||||||
|
max: string;
|
||||||
|
ngOnChanges(changes: SimpleChanges): void;
|
||||||
|
registerOnValidatorChange(fn: () => void): void;
|
||||||
|
validate(c: AbstractControl): ValidationErrors | null;
|
||||||
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare class MinLengthValidator implements Validator, OnChanges {
|
export declare class MinLengthValidator implements Validator, OnChanges {
|
||||||
minlength: string;
|
minlength: string;
|
||||||
|
@ -360,6 +368,14 @@ export declare class MinLengthValidator implements Validator, OnChanges {
|
||||||
validate(c: AbstractControl): ValidationErrors | null;
|
validate(c: AbstractControl): ValidationErrors | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @experimental */
|
||||||
|
export declare class MinValidator implements Validator, OnChanges {
|
||||||
|
min: string;
|
||||||
|
ngOnChanges(changes: SimpleChanges): void;
|
||||||
|
registerOnValidatorChange(fn: () => void): void;
|
||||||
|
validate(c: AbstractControl): ValidationErrors | null;
|
||||||
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare const NG_ASYNC_VALIDATORS: InjectionToken<(Function | Validator)[]>;
|
export declare const NG_ASYNC_VALIDATORS: InjectionToken<(Function | Validator)[]>;
|
||||||
|
|
||||||
|
@ -536,7 +552,9 @@ export declare class Validators {
|
||||||
static compose(validators: (ValidatorFn | null | undefined)[]): ValidatorFn | null;
|
static compose(validators: (ValidatorFn | null | undefined)[]): ValidatorFn | null;
|
||||||
static composeAsync(validators: (AsyncValidatorFn | null)[]): AsyncValidatorFn | null;
|
static composeAsync(validators: (AsyncValidatorFn | null)[]): AsyncValidatorFn | null;
|
||||||
static email(control: AbstractControl): ValidationErrors | null;
|
static email(control: AbstractControl): ValidationErrors | null;
|
||||||
|
static max(max: number): ValidatorFn;
|
||||||
static maxLength(maxLength: number): ValidatorFn;
|
static maxLength(maxLength: number): ValidatorFn;
|
||||||
|
static min(min: number): ValidatorFn;
|
||||||
static minLength(minLength: number): ValidatorFn;
|
static minLength(minLength: number): ValidatorFn;
|
||||||
static nullValidator(c: AbstractControl): ValidationErrors | null;
|
static nullValidator(c: AbstractControl): ValidationErrors | null;
|
||||||
static pattern(pattern: string | RegExp): ValidatorFn;
|
static pattern(pattern: string | RegExp): ValidatorFn;
|
||||||
|
|
Loading…
Reference in New Issue