perf(forms): avoid direct references to the `Validators` class (#41189)
Currently the `Validators` class contains a number of static methods that represent different validators as well as some helper methods. Since class methods are not tree-shakable, any reference to the `Validator` class retains all of its methods (even if you've used just one). This commit refactors the code to extract the logic into standalone functions and use these functions in the code instead of referencing them via `Validators` class. That should make the code more tree-shakable. The `Validators` class still retains its structure and calls these standalone methods internally to keep this change backwards-compatible. PR Close #41189
This commit is contained in:
parent
1644d64398
commit
3bd1992218
|
@ -728,6 +728,12 @@
|
||||||
{
|
{
|
||||||
"name": "collectStylingFromTAttrs"
|
"name": "collectStylingFromTAttrs"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "compose"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "composeAsync"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "composeAsyncValidators"
|
"name": "composeAsyncValidators"
|
||||||
},
|
},
|
||||||
|
@ -1343,6 +1349,9 @@
|
||||||
{
|
{
|
||||||
"name": "notFoundValueOrThrow"
|
"name": "notFoundValueOrThrow"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "nullValidator"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "observable"
|
"name": "observable"
|
||||||
},
|
},
|
||||||
|
|
|
@ -167,9 +167,6 @@
|
||||||
{
|
{
|
||||||
"name": "DomSharedStylesHost"
|
"name": "DomSharedStylesHost"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "EMAIL_REGEXP"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "EMPTY_ARRAY"
|
"name": "EMPTY_ARRAY"
|
||||||
},
|
},
|
||||||
|
@ -563,9 +560,6 @@
|
||||||
{
|
{
|
||||||
"name": "VE_ViewContainerRef"
|
"name": "VE_ViewContainerRef"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "Validators"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "Version"
|
"name": "Version"
|
||||||
},
|
},
|
||||||
|
@ -1046,9 +1040,6 @@
|
||||||
{
|
{
|
||||||
"name": "hasTagAndTypeMatch"
|
"name": "hasTagAndTypeMatch"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "hasValidLength"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "hostReportError"
|
"name": "hostReportError"
|
||||||
},
|
},
|
||||||
|
@ -1130,9 +1121,6 @@
|
||||||
{
|
{
|
||||||
"name": "isDirectiveHost"
|
"name": "isDirectiveHost"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "isEmptyInputValue"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "isForwardRef"
|
"name": "isForwardRef"
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {Directive, forwardRef, Input, OnChanges, SimpleChanges, StaticProvider}
|
||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
|
|
||||||
import {AbstractControl} from '../model';
|
import {AbstractControl} from '../model';
|
||||||
import {NG_VALIDATORS, Validators} from '../validators';
|
import {emailValidator, maxLengthValidator, maxValidator, minLengthValidator, minValidator, NG_VALIDATORS, nullValidator, patternValidator, requiredTrueValidator, requiredValidator} from '../validators';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,7 +77,7 @@ export interface Validator {
|
||||||
*/
|
*/
|
||||||
@Directive()
|
@Directive()
|
||||||
abstract class AbstractValidatorDirective implements Validator {
|
abstract class AbstractValidatorDirective implements Validator {
|
||||||
private _validator: ValidatorFn = Validators.nullValidator;
|
private _validator: ValidatorFn = nullValidator;
|
||||||
private _onChange!: () => void;
|
private _onChange!: () => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -180,7 +180,7 @@ export class MaxValidator extends AbstractValidatorDirective implements OnChange
|
||||||
/** @internal */
|
/** @internal */
|
||||||
normalizeInput = (input: string): number => parseInt(input, 10);
|
normalizeInput = (input: string): number => parseInt(input, 10);
|
||||||
/** @internal */
|
/** @internal */
|
||||||
createValidator = (max: number): ValidatorFn => Validators.max(max);
|
createValidator = (max: number): ValidatorFn => maxValidator(max);
|
||||||
/**
|
/**
|
||||||
* Declare `ngOnChanges` lifecycle hook at the main directive level (vs keeping it in base class)
|
* Declare `ngOnChanges` lifecycle hook at the main directive level (vs keeping it in base class)
|
||||||
* to avoid differences in handling inheritance of lifecycle hooks between Ivy and ViewEngine in
|
* to avoid differences in handling inheritance of lifecycle hooks between Ivy and ViewEngine in
|
||||||
|
@ -240,7 +240,7 @@ export class MinValidator extends AbstractValidatorDirective implements OnChange
|
||||||
/** @internal */
|
/** @internal */
|
||||||
normalizeInput = (input: string): number => parseInt(input, 10);
|
normalizeInput = (input: string): number => parseInt(input, 10);
|
||||||
/** @internal */
|
/** @internal */
|
||||||
createValidator = (min: number): ValidatorFn => Validators.min(min);
|
createValidator = (min: number): ValidatorFn => minValidator(min);
|
||||||
/**
|
/**
|
||||||
* Declare `ngOnChanges` lifecycle hook at the main directive level (vs keeping it in base class)
|
* Declare `ngOnChanges` lifecycle hook at the main directive level (vs keeping it in base class)
|
||||||
* to avoid differences in handling inheritance of lifecycle hooks between Ivy and ViewEngine in
|
* to avoid differences in handling inheritance of lifecycle hooks between Ivy and ViewEngine in
|
||||||
|
@ -364,7 +364,7 @@ export class RequiredValidator implements Validator {
|
||||||
* @nodoc
|
* @nodoc
|
||||||
*/
|
*/
|
||||||
validate(control: AbstractControl): ValidationErrors|null {
|
validate(control: AbstractControl): ValidationErrors|null {
|
||||||
return this.required ? Validators.required(control) : null;
|
return this.required ? requiredValidator(control) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -411,7 +411,7 @@ export class CheckboxRequiredValidator extends RequiredValidator {
|
||||||
* @nodoc
|
* @nodoc
|
||||||
*/
|
*/
|
||||||
validate(control: AbstractControl): ValidationErrors|null {
|
validate(control: AbstractControl): ValidationErrors|null {
|
||||||
return this.required ? Validators.requiredTrue(control) : null;
|
return this.required ? requiredTrueValidator(control) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -472,7 +472,7 @@ export class EmailValidator implements Validator {
|
||||||
* @nodoc
|
* @nodoc
|
||||||
*/
|
*/
|
||||||
validate(control: AbstractControl): ValidationErrors|null {
|
validate(control: AbstractControl): ValidationErrors|null {
|
||||||
return this._enabled ? Validators.email(control) : null;
|
return this._enabled ? emailValidator(control) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -543,7 +543,7 @@ export const MIN_LENGTH_VALIDATOR: any = {
|
||||||
host: {'[attr.minlength]': 'minlength ? minlength : null'}
|
host: {'[attr.minlength]': 'minlength ? minlength : null'}
|
||||||
})
|
})
|
||||||
export class MinLengthValidator implements Validator, OnChanges {
|
export class MinLengthValidator implements Validator, OnChanges {
|
||||||
private _validator: ValidatorFn = Validators.nullValidator;
|
private _validator: ValidatorFn = nullValidator;
|
||||||
private _onChange?: () => void;
|
private _onChange?: () => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -579,7 +579,7 @@ export class MinLengthValidator implements Validator, OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createValidator(): void {
|
private _createValidator(): void {
|
||||||
this._validator = Validators.minLength(
|
this._validator = minLengthValidator(
|
||||||
typeof this.minlength === 'number' ? this.minlength : parseInt(this.minlength, 10));
|
typeof this.minlength === 'number' ? this.minlength : parseInt(this.minlength, 10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -621,7 +621,7 @@ export const MAX_LENGTH_VALIDATOR: any = {
|
||||||
host: {'[attr.maxlength]': 'maxlength ? maxlength : null'}
|
host: {'[attr.maxlength]': 'maxlength ? maxlength : null'}
|
||||||
})
|
})
|
||||||
export class MaxLengthValidator implements Validator, OnChanges {
|
export class MaxLengthValidator implements Validator, OnChanges {
|
||||||
private _validator: ValidatorFn = Validators.nullValidator;
|
private _validator: ValidatorFn = nullValidator;
|
||||||
private _onChange?: () => void;
|
private _onChange?: () => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -656,7 +656,7 @@ export class MaxLengthValidator implements Validator, OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createValidator(): void {
|
private _createValidator(): void {
|
||||||
this._validator = Validators.maxLength(
|
this._validator = maxLengthValidator(
|
||||||
typeof this.maxlength === 'number' ? this.maxlength : parseInt(this.maxlength, 10));
|
typeof this.maxlength === 'number' ? this.maxlength : parseInt(this.maxlength, 10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -701,7 +701,7 @@ export const PATTERN_VALIDATOR: any = {
|
||||||
host: {'[attr.pattern]': 'pattern ? pattern : null'}
|
host: {'[attr.pattern]': 'pattern ? pattern : null'}
|
||||||
})
|
})
|
||||||
export class PatternValidator implements Validator, OnChanges {
|
export class PatternValidator implements Validator, OnChanges {
|
||||||
private _validator: ValidatorFn = Validators.nullValidator;
|
private _validator: ValidatorFn = nullValidator;
|
||||||
private _onChange?: () => void;
|
private _onChange?: () => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -736,6 +736,6 @@ export class PatternValidator implements Validator, OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createValidator(): void {
|
private _createValidator(): void {
|
||||||
this._validator = Validators.pattern(this.pattern);
|
this._validator = patternValidator(this.pattern);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,6 @@ export class Validators {
|
||||||
/**
|
/**
|
||||||
* @description
|
* @description
|
||||||
* Validator that requires the control's value to be greater than or equal to the provided number.
|
* Validator that requires the control's value to be greater than or equal to the provided number.
|
||||||
* The validator exists only as a function and not as a directive.
|
|
||||||
*
|
*
|
||||||
* @usageNotes
|
* @usageNotes
|
||||||
*
|
*
|
||||||
|
@ -132,21 +131,12 @@ export class Validators {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static min(min: number): ValidatorFn {
|
static min(min: number): ValidatorFn {
|
||||||
return (control: AbstractControl): ValidationErrors|null => {
|
return minValidator(min);
|
||||||
if (isEmptyInputValue(control.value) || isEmptyInputValue(min)) {
|
|
||||||
return null; // don't validate empty values to allow optional controls
|
|
||||||
}
|
|
||||||
const value = parseFloat(control.value);
|
|
||||||
// Controls with NaN values after parsing should be treated as not having a
|
|
||||||
// minimum, per the HTML forms spec: https://www.w3.org/TR/html5/forms.html#attr-input-min
|
|
||||||
return !isNaN(value) && value < min ? {'min': {'min': min, 'actual': control.value}} : null;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description
|
* @description
|
||||||
* Validator that requires the control's value to be less than or equal to the provided number.
|
* Validator that requires the control's value to be less than or equal to the provided number.
|
||||||
* The validator exists only as a function and not as a directive.
|
|
||||||
*
|
*
|
||||||
* @usageNotes
|
* @usageNotes
|
||||||
*
|
*
|
||||||
|
@ -165,15 +155,7 @@ export class Validators {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static max(max: number): ValidatorFn {
|
static max(max: number): ValidatorFn {
|
||||||
return (control: AbstractControl): ValidationErrors|null => {
|
return maxValidator(max);
|
||||||
if (isEmptyInputValue(control.value) || isEmptyInputValue(max)) {
|
|
||||||
return null; // don't validate empty values to allow optional controls
|
|
||||||
}
|
|
||||||
const value = parseFloat(control.value);
|
|
||||||
// Controls with NaN values after parsing should be treated as not having a
|
|
||||||
// maximum, per the HTML forms spec: https://www.w3.org/TR/html5/forms.html#attr-input-max
|
|
||||||
return !isNaN(value) && value > max ? {'max': {'max': max, 'actual': control.value}} : null;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -197,7 +179,7 @@ export class Validators {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static required(control: AbstractControl): ValidationErrors|null {
|
static required(control: AbstractControl): ValidationErrors|null {
|
||||||
return isEmptyInputValue(control.value) ? {'required': true} : null;
|
return requiredValidator(control);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -222,7 +204,7 @@ export class Validators {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static requiredTrue(control: AbstractControl): ValidationErrors|null {
|
static requiredTrue(control: AbstractControl): ValidationErrors|null {
|
||||||
return control.value === true ? null : {'required': true};
|
return requiredTrueValidator(control);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -262,10 +244,7 @@ export class Validators {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static email(control: AbstractControl): ValidationErrors|null {
|
static email(control: AbstractControl): ValidationErrors|null {
|
||||||
if (isEmptyInputValue(control.value)) {
|
return emailValidator(control);
|
||||||
return null; // don't validate empty values to allow optional controls
|
|
||||||
}
|
|
||||||
return EMAIL_REGEXP.test(control.value) ? null : {'email': true};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -299,17 +278,7 @@ export class Validators {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static minLength(minLength: number): ValidatorFn {
|
static minLength(minLength: number): ValidatorFn {
|
||||||
return (control: AbstractControl): ValidationErrors|null => {
|
return minLengthValidator(minLength);
|
||||||
if (isEmptyInputValue(control.value) || !hasValidLength(control.value)) {
|
|
||||||
// don't validate empty values to allow optional controls
|
|
||||||
// don't validate values without `length` property
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return control.value.length < minLength ?
|
|
||||||
{'minlength': {'requiredLength': minLength, 'actualLength': control.value.length}} :
|
|
||||||
null;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -340,11 +309,7 @@ export class Validators {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static maxLength(maxLength: number): ValidatorFn {
|
static maxLength(maxLength: number): ValidatorFn {
|
||||||
return (control: AbstractControl): ValidationErrors|null => {
|
return maxLengthValidator(maxLength);
|
||||||
return hasValidLength(control.value) && control.value.length > maxLength ?
|
|
||||||
{'maxlength': {'requiredLength': maxLength, 'actualLength': control.value.length}} :
|
|
||||||
null;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -397,7 +362,149 @@ export class Validators {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static pattern(pattern: string|RegExp): ValidatorFn {
|
static pattern(pattern: string|RegExp): ValidatorFn {
|
||||||
if (!pattern) return Validators.nullValidator;
|
return patternValidator(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description
|
||||||
|
* Validator that performs no operation.
|
||||||
|
*
|
||||||
|
* @see `updateValueAndValidity()`
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static nullValidator(control: AbstractControl): ValidationErrors|null {
|
||||||
|
return nullValidator(control);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description
|
||||||
|
* Compose multiple validators into a single function that returns the union
|
||||||
|
* of the individual error maps for the provided control.
|
||||||
|
*
|
||||||
|
* @returns A validator function that returns an error map with the
|
||||||
|
* merged error maps of the validators if the validation check fails, otherwise `null`.
|
||||||
|
*
|
||||||
|
* @see `updateValueAndValidity()`
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static compose(validators: null): null;
|
||||||
|
static compose(validators: (ValidatorFn|null|undefined)[]): ValidatorFn|null;
|
||||||
|
static compose(validators: (ValidatorFn|null|undefined)[]|null): ValidatorFn|null {
|
||||||
|
return compose(validators);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description
|
||||||
|
* Compose multiple async validators into a single function that returns the union
|
||||||
|
* of the individual error objects for the provided control.
|
||||||
|
*
|
||||||
|
* @returns A validator function that returns an error map with the
|
||||||
|
* merged error objects of the async validators if the validation check fails, otherwise `null`.
|
||||||
|
*
|
||||||
|
* @see `updateValueAndValidity()`
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static composeAsync(validators: (AsyncValidatorFn|null)[]): AsyncValidatorFn|null {
|
||||||
|
return composeAsync(validators);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validator that requires the control's value to be greater than or equal to the provided number.
|
||||||
|
* See `Validators.min` for additional information.
|
||||||
|
*/
|
||||||
|
export function minValidator(min: number): ValidatorFn {
|
||||||
|
return (control: AbstractControl): ValidationErrors|null => {
|
||||||
|
if (isEmptyInputValue(control.value) || isEmptyInputValue(min)) {
|
||||||
|
return null; // don't validate empty values to allow optional controls
|
||||||
|
}
|
||||||
|
const value = parseFloat(control.value);
|
||||||
|
// Controls with NaN values after parsing should be treated as not having a
|
||||||
|
// minimum, per the HTML forms spec: https://www.w3.org/TR/html5/forms.html#attr-input-min
|
||||||
|
return !isNaN(value) && value < min ? {'min': {'min': min, 'actual': control.value}} : null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validator that requires the control's value to be less than or equal to the provided number.
|
||||||
|
* See `Validators.max` for additional information.
|
||||||
|
*/
|
||||||
|
export function maxValidator(max: number): ValidatorFn {
|
||||||
|
return (control: AbstractControl): ValidationErrors|null => {
|
||||||
|
if (isEmptyInputValue(control.value) || isEmptyInputValue(max)) {
|
||||||
|
return null; // don't validate empty values to allow optional controls
|
||||||
|
}
|
||||||
|
const value = parseFloat(control.value);
|
||||||
|
// Controls with NaN values after parsing should be treated as not having a
|
||||||
|
// maximum, per the HTML forms spec: https://www.w3.org/TR/html5/forms.html#attr-input-max
|
||||||
|
return !isNaN(value) && value > max ? {'max': {'max': max, 'actual': control.value}} : null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validator that requires the control have a non-empty value.
|
||||||
|
* See `Validators.required` for additional information.
|
||||||
|
*/
|
||||||
|
export function requiredValidator(control: AbstractControl): ValidationErrors|null {
|
||||||
|
return isEmptyInputValue(control.value) ? {'required': true} : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validator that requires the control's value be true. This validator is commonly
|
||||||
|
* used for required checkboxes.
|
||||||
|
* See `Validators.requiredTrue` for additional information.
|
||||||
|
*/
|
||||||
|
export function requiredTrueValidator(control: AbstractControl): ValidationErrors|null {
|
||||||
|
return control.value === true ? null : {'required': true};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validator that requires the control's value pass an email validation test.
|
||||||
|
* See `Validators.email` for additional information.
|
||||||
|
*/
|
||||||
|
export function emailValidator(control: AbstractControl): ValidationErrors|null {
|
||||||
|
if (isEmptyInputValue(control.value)) {
|
||||||
|
return null; // don't validate empty values to allow optional controls
|
||||||
|
}
|
||||||
|
return EMAIL_REGEXP.test(control.value) ? null : {'email': true};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validator that requires the length of the control's value to be greater than or equal
|
||||||
|
* to the provided minimum length. See `Validators.minLength` for additional information.
|
||||||
|
*/
|
||||||
|
export function minLengthValidator(minLength: number): ValidatorFn {
|
||||||
|
return (control: AbstractControl): ValidationErrors|null => {
|
||||||
|
if (isEmptyInputValue(control.value) || !hasValidLength(control.value)) {
|
||||||
|
// don't validate empty values to allow optional controls
|
||||||
|
// don't validate values without `length` property
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return control.value.length < minLength ?
|
||||||
|
{'minlength': {'requiredLength': minLength, 'actualLength': control.value.length}} :
|
||||||
|
null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validator that requires the length of the control's value to be less than or equal
|
||||||
|
* to the provided maximum length. See `Validators.maxLength` for additional information.
|
||||||
|
*/
|
||||||
|
export function maxLengthValidator(maxLength: number): ValidatorFn {
|
||||||
|
return (control: AbstractControl): ValidationErrors|null => {
|
||||||
|
return hasValidLength(control.value) && control.value.length > maxLength ?
|
||||||
|
{'maxlength': {'requiredLength': maxLength, 'actualLength': control.value.length}} :
|
||||||
|
null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validator that requires the control's value to match a regex pattern.
|
||||||
|
* See `Validators.pattern` for additional information.
|
||||||
|
*/
|
||||||
|
export function patternValidator(pattern: string|RegExp): ValidatorFn {
|
||||||
|
if (!pattern) return nullValidator;
|
||||||
let regex: RegExp;
|
let regex: RegExp;
|
||||||
let regexStr: string;
|
let regexStr: string;
|
||||||
if (typeof pattern === 'string') {
|
if (typeof pattern === 'string') {
|
||||||
|
@ -425,63 +532,12 @@ export class Validators {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description
|
* Function that has `ValidatorFn` shape, but performs no operation.
|
||||||
* Validator that performs no operation.
|
|
||||||
*
|
|
||||||
* @see `updateValueAndValidity()`
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
static nullValidator(control: AbstractControl): ValidationErrors|null {
|
export function nullValidator(control: AbstractControl): ValidationErrors|null {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @description
|
|
||||||
* Compose multiple validators into a single function that returns the union
|
|
||||||
* of the individual error maps for the provided control.
|
|
||||||
*
|
|
||||||
* @returns A validator function that returns an error map with the
|
|
||||||
* merged error maps of the validators if the validation check fails, otherwise `null`.
|
|
||||||
*
|
|
||||||
* @see `updateValueAndValidity()`
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static compose(validators: null): null;
|
|
||||||
static compose(validators: (ValidatorFn|null|undefined)[]): ValidatorFn|null;
|
|
||||||
static compose(validators: (ValidatorFn|null|undefined)[]|null): ValidatorFn|null {
|
|
||||||
if (!validators) return null;
|
|
||||||
const presentValidators: ValidatorFn[] = validators.filter(isPresent) as any;
|
|
||||||
if (presentValidators.length == 0) return null;
|
|
||||||
|
|
||||||
return function(control: AbstractControl) {
|
|
||||||
return mergeErrors(executeValidators<ValidatorFn>(control, presentValidators));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description
|
|
||||||
* Compose multiple async validators into a single function that returns the union
|
|
||||||
* of the individual error objects for the provided control.
|
|
||||||
*
|
|
||||||
* @returns A validator function that returns an error map with the
|
|
||||||
* merged error objects of the async validators if the validation check fails, otherwise `null`.
|
|
||||||
*
|
|
||||||
* @see `updateValueAndValidity()`
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static composeAsync(validators: (AsyncValidatorFn|null)[]): AsyncValidatorFn|null {
|
|
||||||
if (!validators) return null;
|
|
||||||
const presentValidators: AsyncValidatorFn[] = validators.filter(isPresent) as any;
|
|
||||||
if (presentValidators.length == 0) return null;
|
|
||||||
|
|
||||||
return function(control: AbstractControl) {
|
|
||||||
const observables =
|
|
||||||
executeValidators<AsyncValidatorFn>(control, presentValidators).map(toObservable);
|
|
||||||
return forkJoin(observables).pipe(map(mergeErrors));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isPresent(o: any): boolean {
|
function isPresent(o: any): boolean {
|
||||||
return o != null;
|
return o != null;
|
||||||
}
|
}
|
||||||
|
@ -534,22 +590,52 @@ export function normalizeValidators<V>(validators: (V|Validator|AsyncValidator)[
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges synchronous validators into a single validator function (combined using
|
* Merges synchronous validators into a single validator function.
|
||||||
* `Validators.compose`).
|
* See `Validators.compose` for additional information.
|
||||||
*/
|
*/
|
||||||
export function composeValidators(validators: Array<Validator|ValidatorFn>): ValidatorFn|null {
|
function compose(validators: (ValidatorFn|null|undefined)[]|null): ValidatorFn|null {
|
||||||
return validators != null ? Validators.compose(normalizeValidators<ValidatorFn>(validators)) :
|
if (!validators) return null;
|
||||||
null;
|
const presentValidators: ValidatorFn[] = validators.filter(isPresent) as any;
|
||||||
|
if (presentValidators.length == 0) return null;
|
||||||
|
|
||||||
|
return function(control: AbstractControl) {
|
||||||
|
return mergeErrors(executeValidators<ValidatorFn>(control, presentValidators));
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges asynchronous validators into a single validator function (combined using
|
* Accepts a list of validators of different possible shapes (`Validator` and `ValidatorFn`),
|
||||||
* `Validators.composeAsync`).
|
* normalizes the list (converts everything to `ValidatorFn`) and merges them into a single
|
||||||
|
* validator function.
|
||||||
|
*/
|
||||||
|
export function composeValidators(validators: Array<Validator|ValidatorFn>): ValidatorFn|null {
|
||||||
|
return validators != null ? compose(normalizeValidators<ValidatorFn>(validators)) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges asynchronous validators into a single validator function.
|
||||||
|
* See `Validators.composeAsync` for additional information.
|
||||||
|
*/
|
||||||
|
function composeAsync(validators: (AsyncValidatorFn|null)[]): AsyncValidatorFn|null {
|
||||||
|
if (!validators) return null;
|
||||||
|
const presentValidators: AsyncValidatorFn[] = validators.filter(isPresent) as any;
|
||||||
|
if (presentValidators.length == 0) return null;
|
||||||
|
|
||||||
|
return function(control: AbstractControl) {
|
||||||
|
const observables =
|
||||||
|
executeValidators<AsyncValidatorFn>(control, presentValidators).map(toObservable);
|
||||||
|
return forkJoin(observables).pipe(map(mergeErrors));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts a list of async validators of different possible shapes (`AsyncValidator` and
|
||||||
|
* `AsyncValidatorFn`), normalizes the list (converts everything to `AsyncValidatorFn`) and merges
|
||||||
|
* them into a single validator function.
|
||||||
*/
|
*/
|
||||||
export function composeAsyncValidators(validators: Array<AsyncValidator|AsyncValidatorFn>):
|
export function composeAsyncValidators(validators: Array<AsyncValidator|AsyncValidatorFn>):
|
||||||
AsyncValidatorFn|null {
|
AsyncValidatorFn|null {
|
||||||
return validators != null ?
|
return validators != null ? composeAsync(normalizeValidators<AsyncValidatorFn>(validators)) :
|
||||||
Validators.composeAsync(normalizeValidators<AsyncValidatorFn>(validators)) :
|
|
||||||
null;
|
null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue