获取 FormGroup/FormControl 中的验证者

Get validators present in FormGroup/FormControl

我在我的应用程序中使用 Material 2,但在这个问题中我想解决一个问题,特别是 Input

正如您在 API 参考资料 中看到的那样,有一个名为 required 的 属性 绑定,它在占位符中显示为星号。

所以,我想知道是否有一种方法可以检查表单控件是否在 Angular 中具有特定的验证器,因为我真的不想为每个输入手动设置 [required]="true/false"

我阅读了 AbstractControl 文档,但没有找到任何相关信息。我遇到了hasError 方法具有讽刺意味的是无处中没有记录...既不在 FormGroup 中,也不在 FormControl 中,也不在 AbstractControl 中),但这不是我要找的。它只是检查表单控件是否有错误,但正如您可能已经阅读的那样,我想检查控件是否有一些特定的验证器...

一些代码:

<md-input-container>
  <input placeholder="Placeholder" 
         mdInput [formControl]="anyCtrl" 
         [required]="anyCtrl.hasValidator('required')"> <!-- something like this -->
</md-input-container>

我希望问题足够清楚。提前致谢。

Angular 并没有真正提供一个很好的、干净的方法来做到这一点,但这是可能的。我认为验证器存储在注入到 FormBuilder(NG_VALIDATORS) 的服务中,我将研究劫持该服务或将其注入组件,但现在这可行:

docs 和来源显示 AbstractControl 上的 validator 成员键入 ValidatorFnValidatorFn 不幸的是只有一个 null 类型,所以我们看不到发生了什么。然而,在查看生成的源代码并探测一个应用程序之后,似乎我们可以向这个 validators 方法传递一个 control 参数,这将 return 该控件上存在的所有验证器的对象,不管是否通过。

奇怪的是,这个适用于FormControl本身而不是FormGroup(在FormGroupvalidators 成员不是函数,在我的测试中总是 null)。编译后的 JS 说这个函数需要一个 control 参数;我已经尝试传递 FormControl 引用,但据我所知,只要此参数不为 null,它只会 return 控件上的验证器。

在 FormControl 上获取验证器

// in the constructor
this.myForm = this.formBuilder.group({
  'anyCtrl': ['', Validators.required],
  'anotherCtrl': ['', Validators.compose([Validators.required, Validators.email])]
});

// later on 
let theValidators = this.myForm.controls['anyCtrl'].validator('');
console.log(theValidators) // -> {required: true};

let otherValidators = this.myForm.controls['anotherCtrl'].validator('');
console.log(otherValidators); // -> {required: true, email: true}

更容易抓取:

public hasValidator(control: string, validator: string): boolean {
  return !!this.myForm.controls[control].validator(control).hasOwnProperty(validator);
 // returns true if control has the validator
}

并在您的标记中:

<md-input-container>
  <input placeholder="Placeholder" 
         mdInput [formControl]="anyCtrl" 
         [required]="hasValidator('anyCtrl', 'email')">
</md-input-container>

Validators.required

的特例

required 验证器有一个快捷方式。 [required] 绑定实际上是 RequiredValidator 指令 的一个实例(source/forms.js 的第 5022 行)。该指令实际上会将 required 验证程序添加到它所在的 FormControl 中。它相当于在初始化时将 Validators.required 添加到 FormGroup。因此,将绑定 属性 设置为 false 将从该控件中删除 required 验证器,反之亦然......无论哪种方式,该指令都会影响 FormControl.required 值,因此将其绑定到 属性 它的改变不会有太大作用。

唯一的区别是 [required] 指令将星号添加到占位符,而 Validators.required 则没有。

我将继续研究 NG_VALIDATORS,但我希望这对现在有所帮助!

此答案是 的延续。他们写道:

public hasValidator(control: string, validator: string): boolean {
  return !!this.myForm.controls[control].validators(control).hasOwnProperty(validator);
 // returns true if control has the validator
}

但是没有AbstractControls.validators()方法。我假设 AbstractControls.validator() 是有意的。

hasValidator() 方法仅适用于 'fail' 的验证器(例如,控件上值为“”(空)的必需验证器)。因为如果他们通过了他们 return null。解决此问题的一种方法是设置该值,使其始终失败并在之后恢复。

public hasValidator(control: string, validator: string): boolean {
    let control: AbstractControl = this.myForm.controls[control];
    let lastValue: any = control.value;
    switch(validator) {
        case 'required':
            control.setValue('');  // as is appropriate for the control
        case 'pattern':
            control.setValue('3'); // given you have knowledge of what the pattern is - say its '\d\d\d'
        ....
    }
    let hasValidator: boolean = !!control.validator(control).hasOwnProperty(validator);

    control.setValue(lastValue);
    return hasValidator;
}

这太可怕了。这就引出了一个问题——为什么没有AbstractControl.getValidators(): ValidatorFn[]|null?

隐藏这个的动机是什么?也许他们担心有人可能会输入他们的代码:

...
secretPassword: ['', [Validators.pattern('fjdfjafj734738&UERUEIOJDFDJj')]
...

我将 joh04667 和 HankCa 的代码调整为:

export const hasValidator = (form: FormGroup, controlPath: string, validator: string): boolean => {
  const control = form.get(controlPath);
  const validators = control.validator(control);
  return !!(validators && validators.hasOwnProperty(validator));
};

我将其存储在一个名为 util.ts 的文件中,并导入到包含如下形式的组件中:

import * as util from '@app/shared/util';

并在 class 中定义 util:

public util = util;

像这样将指令添加到您的输入组件:

[required]="util.hasValidator(myForm, 'path.to.control', 'required')"

根据 mtinner 的推荐 https://github.com/angular/angular/issues/13461#issuecomment-340368046 我们构建了自己的指令来相应地标记必填字段。

@Directive({
  selector: '[mandatoryField]'
})
export class MandatoryFieldDirective implements OnInit {

  hasRequiredField(abstractControl: AbstractControl) {
    if (abstractControl.validator) {
      const validator = abstractControl.validator({} as AbstractControl);
      if (validator && validator.required) {
        return true;
      }
    }
    return false;
  }

  ngOnInit() {
    const required = this.hasRequiredField(this.ngControl.control);
    if (required) {
      this.renderer.setAttribute(this.elementRef.nativeElement, 'required', '');

      if (this.parentFormField && this.parentFormField._elementRef) { // required for Angular Material form-fields
        this.renderer.setAttribute(this.parentFormField._elementRef.nativeElement, 'required', '');
      }
    }
  }

  constructor(
    private ngControl: NgControl, @Optional() private parentFormField: MatFormField,
    public renderer: Renderer2, public elementRef: ElementRef
  ) { }

}

该指令设置了一个 'required' 属性。该属性可以通过 CSS 寻址。该指令适用于正常的 HTML 输入标签以及 Angular Material 表单字段。要使用 Angular Material,我们必须添加一些变通方法,因为必须在封闭的表单字段标签上设置 'required' 属性;不仅在实际的输入字段上。因此,父组件传递给指令构造函数。

<mat-form-field class="date-picker-form">
  <input matInput class="label-value" [formControlName]="controlName" mandatoryField [matDatepicker]="picker">
  <mat-datepicker #picker class="calendar"></mat-datepicker>
</mat-form-field>

没有直接或干净的方法可以做到这一点。这是我遇到的最干净的方法,有效。使用最新版本 Angular v10.2.0 进行测试(截至今天)

导入这些

import {AbstractControl, FormControl, Validators} from '@angular/forms';

定义您的控件

anyCtrl = new FormControl('', [Validators.required]);

添加此方法

  public hasRequiredField = (abstractControl: AbstractControl): boolean => {
    if (abstractControl.validator) {
      const validator = abstractControl.validator({}as AbstractControl);
      if (validator && validator.required) {
        return true;
      }
    }
    return false;
  }

如何从 HTML

中调用此方法
<input placeholder="Placeholder" [formControl]="anyCtrl" [required]="hasRequiredField(anyCtrl)">

从构造函数或 ngOnInit 中的 Typescript 文件(逻辑)调用它

constructor() {
  console.log(this.hasRequiredField(this.anyCtrl)); // true, false if Validators array does not contain Validators.required
}

请务必牢记,使用 setValidator 方法将覆盖您现有的验证器,因此您需要为要重置的控件包含所有 need/want 验证器。

    control.setValidators([myCustomValidator(owner)]);

没有这么干净的方法,但你总是可以通过

让验证器出现在任何表单组或表单控件中
   this.form.controls[key].validator

然后添加您的自定义控件,我们可以这样做

control.setValidators([control.validator, myCustomValidator(owner)]);

这将使我们能够重用现有的验证器以及我们新的自定义验证器

Angular v12.2 开始 AbstractControl 应该有 a new method:

hasValidator(validator: ValidatorFn): boolean;

Angular v12.2 引入 hasValidator(): https://github.com/angular/angular/pull/42838

示例:this.formGroup.controls['anyCtrl'].hasValidator(Validators.required)

您也可以稍后以编程方式添加或删除验证器。请参阅 addValidators()removeValidators() 和其他内容。

https://angular.io/api/forms/AbstractControl#hasValidator

使用最新的 Angular 版本 v12.2 和 hasValidator,您可以像这样创建管道:

@Pipe({
  name: 'hasRequiredValidator',
})
export class HasRequiredValidatorPipe implements PipeTransform {

  transform(value: AbstractControl | null, controlName?: string): boolean {
    const control = controlName ? value?.get(controlName) : value;
    return !!control?.hasValidator(Validators.required);
  }

}

并在 html 模板中

<input [required]="form | hasRequiredValidator: 'controlName'">

或者

<input [required]="form.get('controlName') | hasRequiredValidator">

如果你想检查一个controller是否有特定的validators,对于angular 12.2+

来说非常简单

例如,

let controller = new FormControl('', [Validators.required])
console.log(controller.hasValidator(Validators.required))//true

但是,这不适用于 Validators.minLength()Validators.maxLength()Validators.min()Validators.max 或任何需要参数的验证器。

要使其正常工作,您需要为该验证器创建一个引用,并将该引用添加到表单控制器验证器和 hasValidator 函数中。

例如,

const minLengthValidator = Validators.minLength(60);
let controller = new FormControl('', [minLengthValidator])
console.log(controller.hasValidator(minLengthValidator)) //true

但是如果你执行以下操作,你会得到 false:

let controller = new FormControl('', [ Validators.minLength(60)])
console.log(controller.hasValidator( Validators.minLength(60))) //false