Angular反应形式:从控件中删除特定的验证器

问题描述 投票:0回答:1

我正在使用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

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

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

干杯,迈克

angular validation customvalidator formgroups angular-abstract-control
1个回答
0
投票

由于对此没有完美的解决方案,所以我咬牙切齿地编写了自己的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]
                    ]);

另外,您必须在保存表单的组件的ValidationSubscriptionCache.unsubscribeAll();中调用ngOnDestroy()

干杯,迈克

© www.soinside.com 2019 - 2024. All rights reserved.