【问题标题】:Angular Reactive Form: Remove Specific Validator from ControlAngular Reactive Form:从控件中删除特定的验证器
【发布时间】:2020-06-05 21:53:16
【问题描述】:

我正在使用 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 只返回一个函数,而不是一个验证器数组,这使得无法选择一个删除而留下其他验证器。

我知道有一些变通方法,例如跟踪其他地方的现有验证器等。 然而我的问题是:这可以直接在AbstractControl 上实现吗?

Github 上的以下问题讨论了该问题: https://github.com/angular/angular/issues/13461

那里提供的解决方案似乎对我不起作用,因为我不仅要处理所需的验证器,还要处理许多自定义验证器。

提前感谢您对此的任何提示或解决方案!

干杯,迈克

【问题讨论】:

  • 我认为跟踪验证器并在不需要的情况下清除 + 添加所有是更简单的方法。
  • 感谢 Antoniosss 对问题的评估。这肯定会奏效。但我正在寻找一个仅适用于AbstractControl 的“干净”解决方案,因为在我的代码中,验证器从不同来源添加到不同位置。例如。验证器是根据加载的业务对象的 json 模式异步添加的,其他验证器是动态添加的,自定义验证器是手动添加到表单上的。因此,跟踪所有这些来源实际上是不可能的。 Angular 让我失望了……
  • 你找到解决办法了吗?
  • 很遗憾没有。我认为这是 Angular 的一个限制,只能通过改变框架来彻底解决。目前(我们在 Angular 9 上),这还没有解决。我们已经实施了一种解决方法(随着时间的推移跟踪控件上的所有验证器,并使用这些“快照”重新配置验证)。

标签: angular validation customvalidator formgroups angular-abstract-control


【解决方案1】:

由于没有优雅的解决方案,我咬紧牙关编写自己的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();

干杯,迈克

【讨论】:

    猜你喜欢
    • 2018-07-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-30
    • 2021-03-08
    • 2018-10-12
    • 1970-01-01
    • 1970-01-01
    • 2020-02-20
    相关资源
    最近更新 更多