获取 FormGroup/FormControl 中存在的验证器

Posted

技术标签:

【中文标题】获取 FormGroup/FormControl 中存在的验证器【英文标题】:Get validators present in FormGroup/FormControl 【发布时间】:2017-10-05 21:25:59 【问题描述】:

我在我的应用程序中使用 Material 2,但在这个问题中,我想专门解决 Input 的问题。

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

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

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

一些代码:

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

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

【问题讨论】:

是的,我检查了错误并意识到其他一些问题,所以你最好等待比我聪明的人:P 无论如何,谢谢你的尝试:) @AJT_82 我刚刚开始赏金...如果您有解决方案甚至解决方法,请发布。 是的,我一定会在有时间的时候尝试一下。这个问题开始困扰我:P @AJT_82 这是糟糕的解决方法:) plnkr.co/edit/qUtwnwLYh1YSH4zr8ZK4?p=preview 【参考方案1】:

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

docs 和源在AbstractControl 上显示validator 成员键入ValidatorFnValidatorFn 不幸的是只有 null 打字,所以我们看不到发生了什么。但是,在查看生成的源并探测应用程序之后,似乎我们可以将这个validators 方法传递给control 参数,该参数将返回该控件上存在的所有验证器的对象,无论它是否通过。

奇怪的是,这个适用于 FormControl 本身而不是 FormGroup(在 FormGroup 上,validators 成员不是函数,并且始终是 null我的测试)。编译后的 JS 说这个函数有一个control 参数;我试过传入FormControl 引用,但据我所知,只要此参数不为空,它只会返回控件上的验证器。

在 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 directive 的一个实例(source/forms.js 的第 5022 行)。该指令实际上会将required Validator 添加到它所在的FormControl。相当于在初始化时将Validators.required 添加到FormGroup。因此,将绑定属性设置为 false 将从该控件中删除 required Validator,反之亦然...无论哪种方式,该指令都会影响 FormControl.required 值,因此将其绑定到它更改的属性不会真正起作用很多。

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

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

【讨论】:

您好,@joh04667,首先,我只想说:谢谢。我刚刚测试了您的解决方案here,它似乎有效。但是,值得一提的是,如果您有一个没有验证器的control,它会引发错误,换句话说,validatorsnull。测试它here。话虽如此,多谢一次。 啊,关于你的最后一句话:我知道这就是 差异 只是 星号,这就是我想要它的原因 :) 我希望有一个一天,Material 2 团队实现了一个解决方案来添加星号来寻找响应式表单,而无需在 html 本身中设置 required。 :) AFAIK hasValidator() 方法仅适用于“失败”的验证器(即,控件上的必需验证器,值为“”(emtpy))。因为如果他们通过了,他们会返回 null。解决此问题的一种方法是设置该值,使其始终失败并在之后恢复。我将发布一个答案作为后续,因为很难将代码放入 cmets。 其实没有AbstractControls.validators()方法。如果您的意思是AbstractControls.validators(),我的最后一条评论代表我的回答也会做出这个假设。 这可行,但似乎只表明验证器失败。例如,当控件具有预设值并且是必需的时。它不会这么说使用这个。我使用了一种解决方法来存储值,将其设置为 null,执行此检查并恢复值。【参考方案2】:

这个答案是@joh04667's 的延续。他们写道:

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() 方法仅适用于“失败”的验证器(例如,控件上的必需验证器,值为 ''(空))。因为如果他们通过了,他们会返回 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')]
...

【讨论】:

“为什么没有 AbstractControl.getValidators(): ValidatorFn[]|null?” 我只是关注这个issue(没有任何观点)。跨度> 是的,几个月前我评论过那张票。还没有任何相关人士的消息。 :( !! 在您的hasValidator() 方法中是什么意思?谢谢。 @moreirapontocom ,它被称为双重否定。就我个人而言,我不喜欢它,但它是一种说法,“如果不是空的,那就是真的”首先使其为真或假,然后将其反转,所以“非(非空)”就是你得到的。见***.com/questions/10467475/…【参考方案3】:

没有直接或干净的方式来做到这一点。这是我遇到的工作最干净的方法。使用最新版本的 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

【讨论】:

【参考方案4】:

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

【讨论】:

这是正确的答案。 AbstractControl API 正式支持这一点。 "提供的验证器必须是对所提供的完全相同函数的引用。"这也使它变得毫无用处。如果我设置 Validators.min(0),检查 control.hasValidator(Validators.min(0)) 会返回 false,只有验证相同的确切验证实例才会返回 true。【参考方案5】:

根据 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
  )  


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

<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>

【讨论】:

【参考方案6】:

我将 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';

并在你的类中定义 util:

public util = util;

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

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

【讨论】:

【参考方案7】:

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

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

没有这样干净的方法可以做到这一点,但你总是可以让验证器出现在任何表单组或表单控件中

   this.form.controls[key].validator

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

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

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

【讨论】:

【参考方案8】:

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

hasValidator(validator: ValidatorFn): boolean;

【讨论】:

【参考方案9】:

使用最新的 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">

【讨论】:

以上是关于获取 FormGroup/FormControl 中存在的验证器的主要内容,如果未能解决你的问题,请参考以下文章

在angular2中向FormGroup添加多个验证器

响应式表单-Angular高级编程

如何在角度2的组件之间传递用户输入数据?

如何为combineLatest rxjs Angular编写Jasmine Unit测试用例

如何创建您的第一个 Angular Reactive Form

js如何获取某一个元素,如果获取不到就继续获取,直到获取到后停止获取?