按下按键时重置角度指令错误

时间:2019-06-28 14:21:44

标签: forms angular6

我一直试图通过附加到字段的指令来添加表单验证。该指令会调用keyup和模糊。当前,当满足错误标准时,设置了一个错误,但是我遇到了一个问题,即输入另一个密钥会导致错误消息闪烁(消失并重新出现)。

观看控制台中的元素。我可以看到在按下键(按下/按住)时,我的指令尚未被调用,但是一些如何将ngvalid类添加到输入中并清除了错误消息。松开键会触发我的指令并重置错误。我在html中的错误切换上尝试了不同的逻辑,但最终结果相同。如果我要删除keyup侦听器并严格执行模糊操作,则会得到类似的结果:输入错误的值->输入时单击不足会导致错误->重新聚焦和keydown会导致错误消失。

组件HTML

<div [formGroup]="group">
    <label for="item?.keyName" class="input-label">{{item?.titleText}}</label>
    <input
        id="item?.keyName"
        name="name"
        type="text"
        data-id="textInput"
        [placeholder]="item?.placeHolder"
        zipCodeValidator
        [formControlName]="item?.keyName"
        required>

    <span [hidden]="!group.get(item?.keyName).dirty && !group.get(item?.keyName).errors?.zipCode">
          {{group.get(item?.keyName).errors?.zipCode}}
    </span>
</div>

指令

import {Directive, HostListener} from '@angular/core';
import {AbstractControl, NgControl} from '@angular/forms';

@Directive({
    selector: '[zipCodeValidator]',
})

export class ZipCodeValidator {
    private regex: RegExp = new RegExp(/^(?:\d{5})?$/);
    private errorMessages = {
        badRegex: 'Enter a valid 5-digit US zip code. E.g. 48226.',
        emptyValue: 'Please enter a value.',
    };

    constructor(private control: NgControl) {}

    @HostListener('keyup')
    public onKeyUp(): void {
        this.validateZipCode();
    }

    @HostListener('blur')
    public onBlur(): void {
        this.validateZipCode();
    }

    private validateZipCode(): void {
        const formControl: AbstractControl = this.control.control;
        let errorMessage: string = null;

        if (!formControl.value) {
            errorMessage = this.errorMessages.emptyValue;
        }

        if (!this.regex.test(formControl.value)) {
            errorMessage = this.errorMessages.badRegex;
        }

        formControl.setErrors({ zipCode: errorMessage });
    }
}

预期结果是,当设置了错误时,错误消息应该保留,并且除了伪指令外,其他任何事件都不能清除错误消息。

2 个答案:

答案 0 :(得分:1)

我不知道为什么keyup事件导致了这种现象。但是使用input事件而不是keyup会给您所需的行为。

@HostListener('input')
public onKeyUp(): void {
    this.validateZipCode();
}

还有一件事;

邮政编码有效时,

validateZipCode()函数将在{ zipCode: null }上设置formControl.erros。但是,这仍将导致formControl无效。 邮政编码有效时,validateZipCode()不应设置任何内容。

  private validateZipCode(): void {
    const formControl: AbstractControl = this.control.control;
    let errorMessage: string = null;

    if (!formControl.value) {
      errorMessage = this.errorMessages.emptyValue;
    }

    if (!this.regex.test(formControl.value)) {
      errorMessage = this.errorMessages.badRegex;
    }

    if (!errorMessage) return;

    /** if there are other errors we preserve them */
    if(formControl.errors)
      formControl.setErrors({...formControl.errors, zipCode: errorMessage });
    else
      formControl.setErrors({ zipCode: errorMessage });
  }

这是一个有效的演示https://stackblitz.com/edit/angular-ydsheq

除此之外,我强烈建议您使用Custom Validators,这是Angular处理表单验证的方式,它将使您远离此类副作用。而且它将需要更少的代码。例如;

  zipCodeValidator: ValidatorFn = (control: FormControl): ValidationErrors | null => {
    const regex: RegExp = new RegExp(/^(?:\d{5})?$/);
    if (!control.value) return { zipCode: "Please enter a value." };
    if(!regex.test(control.value)) return { zipCode: 'Enter a valid 5-digit US zip code. E.g. 48226.' };
    return null;
  }

  formCtrl = new FormControl("", [Validators.required, this.zipCodeValidator]);

这是自定义表单验证器https://stackblitz.com/edit/angular-u7xryz

的有效演示

答案 1 :(得分:0)

所以我相信我找到了问题。因此,使用指令手动设置错误而不实际分配验证器将导致更改输入时删除错误,因为表单更新和角度重新运行了验证检查。设置验证器并将现有验证逻辑移入该方法即可解决此问题。可以严格地清理此代码,但我认为这很重要。因此,@ ysf在推荐自定义验证程序时是正确的。非常感谢您的帮助!

@Directive({
    selector: '[zipCodeValidator]',
})

export class ZipCodeValidator {
    constructor(private control: NgControl) {}

    @HostListener('keyup')
    public onKeyUp(): void {
        this.validateZipCode();
    }

    @HostListener('blur')
    public onBlur(): void {
        this.validateZipCode();
    }

    private validateZipCode(): void {
        const formControl: AbstractControl = this.control.control;
        formControl.setValidators([this.testVal]);
    }

    private testVal(control: AbstractControl): { [key: string]: string } | null {
        const regex: RegExp = new RegExp(/^(?:\d{5})?$/);
        const errorMessages = {
            badRegex: 'Enter a valid 5-digit US zip code. E.g. 48226.',
            emptyValue: 'Please enter a value.',
        };

        let errorMessage: string = null;

        if (!control.value) {
            errorMessage = errorMessages.emptyValue;
        }

        if (!regex.test(control.value)) {
            errorMessage = errorMessages.badRegex;
        }

        return { 'zipCode': errorMessage };
    }
}