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();

干杯,迈克