Angular 反应式表单:从控件中删除特定验证器
Angular Reactive Form: Remove Specific Validator from Control
我正在使用 Angular 反应形式。
在我的场景中,验证器是动态添加到我的控件中的。
按如下方式完成:
const myControl = myFormGroup.get('myControl');
if (myControl.validator) {
myControl.setValidators([ myControl.validator, Validators.required ]); //preserve existing validators
} else {
myControl.setValidators([ Validators.required ]);
}
这很好用。
问题
我需要一种方法来删除特定验证器,同时保留其他验证器。
myControl.validators
只是 returns 一个函数,而不是一组验证器,这使得无法在保留其他验证器的同时选择一个要删除。
我知道有一些解决方法,比如跟踪其他地方的现有验证器等。
然而,我的问题是:这可以直接在 AbstractControl
上实现吗?
Github 上的以下问题讨论了该问题:
https://github.com/angular/angular/issues/13461
那里提供的解决方案似乎对我不起作用,因为我不仅要处理所需的验证器,还要处理大量自定义验证器。
在此先感谢您对此的任何提示或解决方案!
干杯,迈克
由于没有优雅的解决方案,我硬着头皮写了自己的 RequiredIf 验证器。
它做了两件事:
- 缓存应用它的控件的原始验证器。如果不再需要 属性,它将恢复原始验证器(同时删除所需的验证器)。
- 它在 属性 上添加 valueChange 侦听器,验证取决于。如果需要或不再需要 属性,它将验证控件以立即显示新的验证结果。
这是我的验证器代码(我目前正在使用它,没有时间优化它)。
import { FormGroup, ValidationErrors, Validators } from '@angular/forms';
import { Subscription } from 'rxjs';
export class ValidationSubscriptionCache {
static observedPropertyCombinations = new Array<string>();
static subscriptions = new Array<Subscription>();
static unsubscribeAll() {
for (let subscription of ValidationSubscriptionCache.subscriptions) {
subscription.unsubscribe();
}
}
}
export function CombineProperties(property: string, otherProperty: string) {
return `${ property }.${ otherProperty }`;
}
export class ValidatorCache {
static cache = new Array<ValidatorsOfProperty>();
static currentlyRequiredProperties = new Array<string>();
static isCurrentlyRequired(property: string) {
return ValidatorCache.currentlyRequiredProperties.indexOf(property) > -1;
}
static removeFromCurrentlyRequiredProperties(property: string) {
const index = ValidatorCache.currentlyRequiredProperties.indexOf(property, 0);
if (index > -1) {
ValidatorCache.currentlyRequiredProperties.splice(index, 1);
}
}
static isInCache(property: string) {
const found = this.cache.find(x => x.property === property);
if (found) {
return true;
}
return false;
}
static addToCache(property: string, validators: any) {
const toAdd = new ValidatorsOfProperty();
toAdd.property = property;
toAdd.validators = validators;
ValidatorCache.cache.push(toAdd);
}
static popFromCache(property: string): ValidatorsOfProperty {
const item = ValidatorCache.cache.find(x => x.property === property);
const index = ValidatorCache.cache.indexOf(item, 0);
ValidatorCache.cache.splice(index, 1);
return item;
}
}
export class ValidatorsOfProperty {
property: string;
validators: any;
}
export function ValidateRequiredIf(property: string, otherProperty: string, valueThatMakesRequired) {
return (formGroup: FormGroup): ValidationErrors | null => {
const propertyCombination = CombineProperties(property, otherProperty);
const otherValue = formGroup.get(otherProperty).value;
//register changes of other property and refresh validation if it changes
if (ValidationSubscriptionCache.observedPropertyCombinations.indexOf(propertyCombination) < 0) {
ValidationSubscriptionCache.observedPropertyCombinations.push(propertyCombination);
const valueChangeSubscription =
formGroup.get(otherProperty).valueChanges
.subscribe(() => {
formGroup.get(property).markAsTouched();
formGroup.get(property).updateValueAndValidity();
});
ValidationSubscriptionCache.subscriptions.push(valueChangeSubscription);
}
//set or remove the required validator based on the depending property
const isRequired =
otherValue !== null &&
otherValue !== undefined &&
otherValue.toString() === valueThatMakesRequired.toString();
if (isRequired && !ValidatorCache.isCurrentlyRequired(property)) {
ValidatorCache.currentlyRequiredProperties.push(property);
ValidatorCache.addToCache(property, formGroup.get(property).validator);
if (formGroup.get(property).validator) {
formGroup.get(property).setValidators([ formGroup.get(property).validator, Validators.required ]);
} else {
formGroup.get(property).setValidators(Validators.required);
}
}
if (!isRequired && ValidatorCache.isCurrentlyRequired(property)) {
ValidatorCache.removeFromCurrentlyRequiredProperties(property);
formGroup.get(property).setValidators(ValidatorCache.popFromCache(property).validators);
}
return null;
};
}
这是申请表格:
this.myForm.setValidators(
[
//city is required if the country is switzerland
ValidateRequiredIf('city', 'nationality', 'switzerland'),
//[add more]
]);
此外,您必须在包含表单的组件的 ngOnDestroy()
中调用 ValidationSubscriptionCache.unsubscribeAll();
。
干杯,迈克
我正在使用 Angular 反应形式。 在我的场景中,验证器是动态添加到我的控件中的。
按如下方式完成:
const myControl = myFormGroup.get('myControl');
if (myControl.validator) {
myControl.setValidators([ myControl.validator, Validators.required ]); //preserve existing validators
} else {
myControl.setValidators([ Validators.required ]);
}
这很好用。
问题
我需要一种方法来删除特定验证器,同时保留其他验证器。
myControl.validators
只是 returns 一个函数,而不是一组验证器,这使得无法在保留其他验证器的同时选择一个要删除。
我知道有一些解决方法,比如跟踪其他地方的现有验证器等。
然而,我的问题是:这可以直接在 AbstractControl
上实现吗?
Github 上的以下问题讨论了该问题: https://github.com/angular/angular/issues/13461
那里提供的解决方案似乎对我不起作用,因为我不仅要处理所需的验证器,还要处理大量自定义验证器。
在此先感谢您对此的任何提示或解决方案!
干杯,迈克
由于没有优雅的解决方案,我硬着头皮写了自己的 RequiredIf 验证器。
它做了两件事:
- 缓存应用它的控件的原始验证器。如果不再需要 属性,它将恢复原始验证器(同时删除所需的验证器)。
- 它在 属性 上添加 valueChange 侦听器,验证取决于。如果需要或不再需要 属性,它将验证控件以立即显示新的验证结果。
这是我的验证器代码(我目前正在使用它,没有时间优化它)。
import { FormGroup, ValidationErrors, Validators } from '@angular/forms';
import { Subscription } from 'rxjs';
export class ValidationSubscriptionCache {
static observedPropertyCombinations = new Array<string>();
static subscriptions = new Array<Subscription>();
static unsubscribeAll() {
for (let subscription of ValidationSubscriptionCache.subscriptions) {
subscription.unsubscribe();
}
}
}
export function CombineProperties(property: string, otherProperty: string) {
return `${ property }.${ otherProperty }`;
}
export class ValidatorCache {
static cache = new Array<ValidatorsOfProperty>();
static currentlyRequiredProperties = new Array<string>();
static isCurrentlyRequired(property: string) {
return ValidatorCache.currentlyRequiredProperties.indexOf(property) > -1;
}
static removeFromCurrentlyRequiredProperties(property: string) {
const index = ValidatorCache.currentlyRequiredProperties.indexOf(property, 0);
if (index > -1) {
ValidatorCache.currentlyRequiredProperties.splice(index, 1);
}
}
static isInCache(property: string) {
const found = this.cache.find(x => x.property === property);
if (found) {
return true;
}
return false;
}
static addToCache(property: string, validators: any) {
const toAdd = new ValidatorsOfProperty();
toAdd.property = property;
toAdd.validators = validators;
ValidatorCache.cache.push(toAdd);
}
static popFromCache(property: string): ValidatorsOfProperty {
const item = ValidatorCache.cache.find(x => x.property === property);
const index = ValidatorCache.cache.indexOf(item, 0);
ValidatorCache.cache.splice(index, 1);
return item;
}
}
export class ValidatorsOfProperty {
property: string;
validators: any;
}
export function ValidateRequiredIf(property: string, otherProperty: string, valueThatMakesRequired) {
return (formGroup: FormGroup): ValidationErrors | null => {
const propertyCombination = CombineProperties(property, otherProperty);
const otherValue = formGroup.get(otherProperty).value;
//register changes of other property and refresh validation if it changes
if (ValidationSubscriptionCache.observedPropertyCombinations.indexOf(propertyCombination) < 0) {
ValidationSubscriptionCache.observedPropertyCombinations.push(propertyCombination);
const valueChangeSubscription =
formGroup.get(otherProperty).valueChanges
.subscribe(() => {
formGroup.get(property).markAsTouched();
formGroup.get(property).updateValueAndValidity();
});
ValidationSubscriptionCache.subscriptions.push(valueChangeSubscription);
}
//set or remove the required validator based on the depending property
const isRequired =
otherValue !== null &&
otherValue !== undefined &&
otherValue.toString() === valueThatMakesRequired.toString();
if (isRequired && !ValidatorCache.isCurrentlyRequired(property)) {
ValidatorCache.currentlyRequiredProperties.push(property);
ValidatorCache.addToCache(property, formGroup.get(property).validator);
if (formGroup.get(property).validator) {
formGroup.get(property).setValidators([ formGroup.get(property).validator, Validators.required ]);
} else {
formGroup.get(property).setValidators(Validators.required);
}
}
if (!isRequired && ValidatorCache.isCurrentlyRequired(property)) {
ValidatorCache.removeFromCurrentlyRequiredProperties(property);
formGroup.get(property).setValidators(ValidatorCache.popFromCache(property).validators);
}
return null;
};
}
这是申请表格:
this.myForm.setValidators(
[
//city is required if the country is switzerland
ValidateRequiredIf('city', 'nationality', 'switzerland'),
//[add more]
]);
此外,您必须在包含表单的组件的 ngOnDestroy()
中调用 ValidationSubscriptionCache.unsubscribeAll();
。
干杯,迈克