refactor(forms): get rid of duplicate functions (#38371)
This commit performs minor refactoring in Forms package to get rid of duplicate functions. It looks like the functions were duplicated due to a slightly different type signatures, but their logic is completely identical. The logic in retained functions remains the same and now these function also accept a generic type to achieve the same level of type safety. PR Close #38371
This commit is contained in:
parent
354e66efad
commit
856db56cca
|
@ -1855,11 +1855,6 @@
|
||||||
"packages/forms/src/directives/ng_model.ts",
|
"packages/forms/src/directives/ng_model.ts",
|
||||||
"packages/forms/src/directives/ng_model_group.ts"
|
"packages/forms/src/directives/ng_model_group.ts"
|
||||||
],
|
],
|
||||||
[
|
|
||||||
"packages/forms/src/directives/normalize_validator.ts",
|
|
||||||
"packages/forms/src/model.ts",
|
|
||||||
"packages/forms/src/directives/shared.ts"
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
"packages/forms/src/directives/reactive_directives/form_control_directive.ts",
|
"packages/forms/src/directives/reactive_directives/form_control_directive.ts",
|
||||||
"packages/forms/src/directives/shared.ts",
|
"packages/forms/src/directives/shared.ts",
|
||||||
|
|
|
@ -701,9 +701,6 @@
|
||||||
{
|
{
|
||||||
"name": "_keyMap"
|
"name": "_keyMap"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "_mergeErrors"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "_noControlError"
|
"name": "_noControlError"
|
||||||
},
|
},
|
||||||
|
@ -914,6 +911,9 @@
|
||||||
{
|
{
|
||||||
"name": "executeTemplate"
|
"name": "executeTemplate"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "executeValidators"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "executeViewQueryFn"
|
"name": "executeViewQueryFn"
|
||||||
},
|
},
|
||||||
|
@ -1346,6 +1346,9 @@
|
||||||
{
|
{
|
||||||
"name": "mergeAll"
|
"name": "mergeAll"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "mergeErrors"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mergeHostAttribute"
|
"name": "mergeHostAttribute"
|
||||||
},
|
},
|
||||||
|
@ -1401,10 +1404,7 @@
|
||||||
"name": "noop"
|
"name": "noop"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "normalizeAsyncValidator"
|
"name": "normalizeValidators"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "normalizeValidator"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "observable"
|
"name": "observable"
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google LLC All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {AbstractControl} from '../model';
|
|
||||||
import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from './validators';
|
|
||||||
|
|
||||||
export function normalizeValidator(validator: ValidatorFn|Validator): ValidatorFn {
|
|
||||||
if (!!(<Validator>validator).validate) {
|
|
||||||
return (c: AbstractControl) => (<Validator>validator).validate(c);
|
|
||||||
} else {
|
|
||||||
return <ValidatorFn>validator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeAsyncValidator(validator: AsyncValidatorFn|
|
|
||||||
AsyncValidator): AsyncValidatorFn {
|
|
||||||
if (!!(<AsyncValidator>validator).validate) {
|
|
||||||
return (c: AbstractControl) => (<AsyncValidator>validator).validate(c);
|
|
||||||
} else {
|
|
||||||
return <AsyncValidatorFn>validator;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,7 +9,8 @@
|
||||||
import {isDevMode} from '@angular/core';
|
import {isDevMode} from '@angular/core';
|
||||||
|
|
||||||
import {FormArray, FormControl, FormGroup} from '../model';
|
import {FormArray, FormControl, FormGroup} from '../model';
|
||||||
import {Validators} from '../validators';
|
import {normalizeValidators, Validators} from '../validators';
|
||||||
|
|
||||||
import {AbstractControlDirective} from './abstract_control_directive';
|
import {AbstractControlDirective} from './abstract_control_directive';
|
||||||
import {AbstractFormGroupDirective} from './abstract_form_group_directive';
|
import {AbstractFormGroupDirective} from './abstract_form_group_directive';
|
||||||
import {CheckboxControlValueAccessor} from './checkbox_value_accessor';
|
import {CheckboxControlValueAccessor} from './checkbox_value_accessor';
|
||||||
|
@ -17,7 +18,6 @@ import {ControlContainer} from './control_container';
|
||||||
import {ControlValueAccessor} from './control_value_accessor';
|
import {ControlValueAccessor} from './control_value_accessor';
|
||||||
import {DefaultValueAccessor} from './default_value_accessor';
|
import {DefaultValueAccessor} from './default_value_accessor';
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
import {normalizeAsyncValidator, normalizeValidator} from './normalize_validator';
|
|
||||||
import {NumberValueAccessor} from './number_value_accessor';
|
import {NumberValueAccessor} from './number_value_accessor';
|
||||||
import {RadioControlValueAccessor} from './radio_control_value_accessor';
|
import {RadioControlValueAccessor} from './radio_control_value_accessor';
|
||||||
import {RangeValueAccessor} from './range_value_accessor';
|
import {RangeValueAccessor} from './range_value_accessor';
|
||||||
|
@ -142,12 +142,14 @@ function _throwError(dir: AbstractControlDirective, message: string): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function composeValidators(validators: Array<Validator|ValidatorFn>): ValidatorFn|null {
|
export function composeValidators(validators: Array<Validator|ValidatorFn>): ValidatorFn|null {
|
||||||
return validators != null ? Validators.compose(validators.map(normalizeValidator)) : null;
|
return validators != null ? Validators.compose(normalizeValidators<ValidatorFn>(validators)) :
|
||||||
|
null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function composeAsyncValidators(validators: Array<AsyncValidator|AsyncValidatorFn>):
|
export function composeAsyncValidators(validators: Array<AsyncValidator|AsyncValidatorFn>):
|
||||||
AsyncValidatorFn|null {
|
AsyncValidatorFn|null {
|
||||||
return validators != null ? Validators.composeAsync(validators.map(normalizeAsyncValidator)) :
|
return validators != null ?
|
||||||
|
Validators.composeAsync(normalizeValidators<AsyncValidatorFn>(validators)) :
|
||||||
null;
|
null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {InjectionToken, ɵisObservable as isObservable, ɵisPromise as isPromise
|
||||||
import {forkJoin, from, Observable} from 'rxjs';
|
import {forkJoin, from, Observable} from 'rxjs';
|
||||||
import {map} from 'rxjs/operators';
|
import {map} from 'rxjs/operators';
|
||||||
|
|
||||||
import {AsyncValidatorFn, ValidationErrors, Validator, ValidatorFn} from './directives/validators';
|
import {AsyncValidator, AsyncValidatorFn, ValidationErrors, Validator, ValidatorFn} from './directives/validators';
|
||||||
import {AbstractControl} from './model';
|
import {AbstractControl} from './model';
|
||||||
|
|
||||||
function isEmptyInputValue(value: any): boolean {
|
function isEmptyInputValue(value: any): boolean {
|
||||||
|
@ -435,7 +435,7 @@ export class Validators {
|
||||||
if (presentValidators.length == 0) return null;
|
if (presentValidators.length == 0) return null;
|
||||||
|
|
||||||
return function(control: AbstractControl) {
|
return function(control: AbstractControl) {
|
||||||
return _mergeErrors(_executeValidators(control, presentValidators));
|
return mergeErrors(executeValidators<ValidatorFn>(control, presentValidators));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,8 +456,9 @@ export class Validators {
|
||||||
if (presentValidators.length == 0) return null;
|
if (presentValidators.length == 0) return null;
|
||||||
|
|
||||||
return function(control: AbstractControl) {
|
return function(control: AbstractControl) {
|
||||||
const observables = _executeAsyncValidators(control, presentValidators).map(toObservable);
|
const observables =
|
||||||
return forkJoin(observables).pipe(map(_mergeErrors));
|
executeValidators<AsyncValidatorFn>(control, presentValidators).map(toObservable);
|
||||||
|
return forkJoin(observables).pipe(map(mergeErrors));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -474,15 +475,7 @@ export function toObservable(r: any): Observable<any> {
|
||||||
return obs;
|
return obs;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _executeValidators(control: AbstractControl, validators: ValidatorFn[]): any[] {
|
function mergeErrors(arrayOfErrors: (ValidationErrors|null)[]): ValidationErrors|null {
|
||||||
return validators.map(v => v(control));
|
|
||||||
}
|
|
||||||
|
|
||||||
function _executeAsyncValidators(control: AbstractControl, validators: AsyncValidatorFn[]): any[] {
|
|
||||||
return validators.map(v => v(control));
|
|
||||||
}
|
|
||||||
|
|
||||||
function _mergeErrors(arrayOfErrors: ValidationErrors[]): ValidationErrors|null {
|
|
||||||
let res: {[key: string]: any} = {};
|
let res: {[key: string]: any} = {};
|
||||||
|
|
||||||
// Not using Array.reduce here due to a Chrome 80 bug
|
// Not using Array.reduce here due to a Chrome 80 bug
|
||||||
|
@ -493,3 +486,30 @@ function _mergeErrors(arrayOfErrors: ValidationErrors[]): ValidationErrors|null
|
||||||
|
|
||||||
return Object.keys(res).length === 0 ? null : res;
|
return Object.keys(res).length === 0 ? null : res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GenericValidatorFn = (control: AbstractControl) => any;
|
||||||
|
|
||||||
|
function executeValidators<V extends GenericValidatorFn>(
|
||||||
|
control: AbstractControl, validators: V[]): ReturnType<V>[] {
|
||||||
|
return validators.map(validator => validator(control));
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidatorFn<V>(validator: V|Validator|AsyncValidator): validator is V {
|
||||||
|
return !(validator as Validator).validate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given the list of validators that may contain both functions as well as classes, return the list
|
||||||
|
* of validator functions (convert validator classes into validator functions). This is needed to
|
||||||
|
* have consistent structure in validators list before composing them.
|
||||||
|
*
|
||||||
|
* @param validators The set of validators that may contain validators both in plain function form
|
||||||
|
* as well as represented as a validator class.
|
||||||
|
*/
|
||||||
|
export function normalizeValidators<V>(validators: (V|Validator|AsyncValidator)[]): V[] {
|
||||||
|
return validators.map(validator => {
|
||||||
|
return isValidatorFn<V>(validator) ?
|
||||||
|
validator :
|
||||||
|
((c: AbstractControl) => validator.validate(c)) as unknown as V;
|
||||||
|
});
|
||||||
|
}
|
|
@ -8,12 +8,12 @@
|
||||||
|
|
||||||
import {fakeAsync, tick} from '@angular/core/testing';
|
import {fakeAsync, tick} from '@angular/core/testing';
|
||||||
import {describe, expect, it} from '@angular/core/testing/src/testing_internal';
|
import {describe, expect, it} from '@angular/core/testing/src/testing_internal';
|
||||||
import {AbstractControl, AsyncValidatorFn, FormArray, FormControl, Validators} from '@angular/forms';
|
import {AbstractControl, AsyncValidator, AsyncValidatorFn, FormArray, FormControl, ValidationErrors, ValidatorFn, Validators} from '@angular/forms';
|
||||||
import {normalizeAsyncValidator} from '@angular/forms/src/directives/normalize_validator';
|
|
||||||
import {AsyncValidator, ValidationErrors, ValidatorFn} from '@angular/forms/src/directives/validators';
|
|
||||||
import {Observable, of, timer} from 'rxjs';
|
import {Observable, of, timer} from 'rxjs';
|
||||||
import {first, map} from 'rxjs/operators';
|
import {first, map} from 'rxjs/operators';
|
||||||
|
|
||||||
|
import {normalizeValidators} from '../src/validators';
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
function validator(key: string, error: any): ValidatorFn {
|
function validator(key: string, error: any): ValidatorFn {
|
||||||
return (c: AbstractControl) => {
|
return (c: AbstractControl) => {
|
||||||
|
@ -413,11 +413,12 @@ describe('Validators', () => {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should normalize and evaluate async validator-directives correctly', fakeAsync(() => {
|
it('should normalize and evaluate async validator-directives correctly', fakeAsync(() => {
|
||||||
const v = Validators.composeAsync(
|
const normalizedValidators = normalizeValidators<AsyncValidatorFn>(
|
||||||
[normalizeAsyncValidator(new AsyncValidatorDirective('expected', {'one': true}))])!;
|
[new AsyncValidatorDirective('expected', {'one': true})]);
|
||||||
|
const validatorFn = Validators.composeAsync(normalizedValidators)!;
|
||||||
|
|
||||||
let errorMap: {[key: string]: any}|null = undefined!;
|
let errorMap: {[key: string]: any}|null = undefined!;
|
||||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
(validatorFn(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((errors: {[key: string]: any}|null) => errorMap = errors);
|
.subscribe((errors: {[key: string]: any}|null) => errorMap = errors);
|
||||||
tick();
|
tick();
|
||||||
|
@ -475,11 +476,12 @@ describe('Validators', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should normalize and evaluate async validator-directives correctly', () => {
|
it('should normalize and evaluate async validator-directives correctly', () => {
|
||||||
const v = Validators.composeAsync(
|
const normalizedValidators = normalizeValidators<AsyncValidatorFn>(
|
||||||
[normalizeAsyncValidator(new AsyncValidatorDirective('expected', {'one': true}))])!;
|
[new AsyncValidatorDirective('expected', {'one': true})]);
|
||||||
|
const validatorFn = Validators.composeAsync(normalizedValidators)!;
|
||||||
|
|
||||||
let errorMap: {[key: string]: any}|null = undefined!;
|
let errorMap: {[key: string]: any}|null = undefined!;
|
||||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
(validatorFn(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((errors: {[key: string]: any}|null) => errorMap = errors)!;
|
.subscribe((errors: {[key: string]: any}|null) => errorMap = errors)!;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue