如何在 Angular(v2 及更高版本)反应形式中查找无效控件

Posted

技术标签:

【中文标题】如何在 Angular(v2 及更高版本)反应形式中查找无效控件【英文标题】:How to find the invalid controls in Angular(v2 onwards) reactive form 【发布时间】:2017-12-26 11:13:00 【问题描述】:

我在 Angular 中有一个反应形式,如下所示:

this.AddCustomerForm = this.formBuilder.group(
    Firstname: ['', Validators.required],
    Lastname: ['', Validators.required],
    Email: ['', Validators.required, Validators.pattern(this.EMAIL_REGEX)],
    Picture: [''],
    Username: ['', Validators.required],
    Password: ['', Validators.required],
    Address: ['', Validators.required],
    Postcode: ['', Validators.required],
    City: ['', Validators.required],
    Country: ['', Validators.required]
);

createCustomer(currentCustomer: Customer) 

    if (!this.AddCustomerForm.valid)
    
        //some app logic
    

this.AddCustomerForm.valid 返回 false,但一切看起来都不错。

我试图通过检查控件集合中的状态属性来查找。但是我想知道是否有办法找到无效的并显示给用户?

【问题讨论】:

如果你只想显示有错误的字段,你可以使用css来高亮或着色无效的字段。每个无效字段在其类列表中附加一个“ng-invalid”类 这是 Angular 中的一个未解决问题。您可以在此处竖起大拇指以提高优先级:github.com/angular/angular/issues/10530 当formGroup.errors 给出null 而formGroup.valid 给出false 时,可能是因为maxlengthhtml 标签,它们不应该混合使用 【参考方案1】:

您可以简单地遍历每个控件并检查状态:

    public findInvalidControls() 
        const invalid = [];
        const controls = this.AddCustomerForm.controls;
        for (const name in controls) 
            if (controls[name].invalid) 
                invalid.push(name);
            
        
        return invalid;
    

【讨论】:

谢谢你,但我试过了,即使这个没有返回,我的表单仍然无效,这很奇怪。我的意思是这段代码看起来不错,但是为什么 form.valid 返回 false 没有任何意义 findInvalidControls() 给你什么回报? 什么都不返回,invalid 为空。我在调试监视屏幕中一一检查,所有控件都有效但this.AddCustomerForm.valid仍然返回false。 @AngularInDepth.com - 如果其中一个控件是表单组,您的函数将返回无效的表单组,而不是无效的特定表单控件 我遇到了同样的问题,我的表单无效,但所有字段都有效。我通过field.setValue('1234', onlySelf: true) 更新了该字段。一旦我删除 onlySelf:true 它就会按预期工作!【参考方案2】:

我刚刚解决了这个问题:每个表单字段都有效,但表单本身仍然无效。

原来我在 FormArray 上设置了“Validator.required”,其中控件是动态添加/删除的。因此,即使 FormArray 为空,它仍然是必需的,因此表单始终无效,即使每个可见控件都已正确填充。

我没有找到表单的无效部分,因为我的“findInvalidControls”函数只检查了 FormControl 而不是 FormGroup/FormArray。所以我更新了一下:

/* 
   Returns an array of invalid control/group names, or a zero-length array if 
   no invalid controls/groups where found 
*/
public findInvalidControlsRecursive(formToInvestigate:FormGroup|FormArray):string[] 
    var invalidControls:string[] = [];
    let recursiveFunc = (form:FormGroup|FormArray) => 
      Object.keys(form.controls).forEach(field =>  
        const control = form.get(field);
        if (control.invalid) invalidControls.push(field);
        if (control instanceof FormGroup) 
          recursiveFunc(control);
         else if (control instanceof FormArray) 
          recursiveFunc(control);
                
      );
    
    recursiveFunc(formToInvestigate);
    return invalidControls;
  

【讨论】:

哇哦。非常感谢,这为我节省了大量的调查时间!【参考方案3】:

一个无效的 Angular 控件具有名为 'ng-invalid' 的 CSS 类。

在 Chrome 的 DevTools 下,选择控制台选项卡。

在控制台提示符下运行以下命令以获取带有 CSS 类“ng-invalid”的无效 Angular 控件

document.getElementsByClassName('ng-invalid')

输出应该与此类似:

在这种情况下,带下划线的文本用于表单控件listen-address,而圈起来的文本:.ng-invalid表示该控件无效。

注意:在 chrome 中测试

【讨论】:

【参考方案4】:

现在,在 Angular 9 中,您可以使用 markAllAsTouched() 方法来显示无效控件验证器:

this.AddCustomerForm.markAllAsTouched();

【讨论】:

【参考方案5】:

下面的代码将记录所有无效的控件?

for (let el in this.ReactiveForm.controls) 
      if (this.ReactiveForm.controls[el].errors) 
        console.log(el)
      
           

您可以从中创建一个数组或字符串并显示给用户

【讨论】:

【参考方案6】:

表单和所有控件都扩展了 angular 类 AbstractControl。每个实现都有一个验证错误的访问器。

let errors = this.AddCustomerForm.errors
// errors is an instance of ValidatorErrors

api 文档包含所有参考 https://angular.io/api/forms/AbstractControl

编辑

我认为错误访问器是这样工作的,但是这个到 github 的链接表明还有一些其他人的想法和我一样 https://github.com/angular/angular/issues/11530

无论如何,通过使用控件访问器,您可以遍历表单中的所有 formControl。

Object.keys(this.AddCustomerForm.controls)
    .forEach( control => 
        //check each control here
        // if the child is a formGroup or a formArray
        // you may cast it and check it's subcontrols too
     )

【讨论】:

即使有空控件也会返回 null 没有错误时应该返回null。你能发布你的模板吗? 是的,这行不通,在每个表单控件上设置了不同的验证,每个表单控件都包含错误,而表单没有。您需要像 Maximus 给出的答案那样迭代控件。 我可以访问每个单独控件的错误,例如 this.form.controls['Email'].errors @AJT_82 实际上,如果为 formGroup 设置了验证器,则表单本身可能会显示错误(检查有关跨字段验证的文档,这对在组上而不是在控件中进行验证是有意义的) 【参考方案7】:

如果表单中没有太多字段,您可以简单地 F12 并将鼠标悬停在控件上,您将能够看到带有字段原始/已触摸/有效值的弹出窗口- “#fieldname.form-control.ng-untouched.ng-invalid”。

【讨论】:

【参考方案8】:

我冒昧地改进了 AngularInDepth.com-s 的代码,以便它也递归地搜索嵌套形式的无效输入。它是由 FormArray-s 还是 FormGroup-s 嵌套的。只需输入顶层formGroup,它将返回所有无效的FormControl。

如果您将 FormControl 检查和添加到无效数组的功能分离到一个单独的函数中,您可能会略过一些“instanceof”类型检查。这将使函数看起来更简洁,但我需要一个全局的、单一的函数选项来获取所有无效 formControls 的平面数组,这就是解决方案!

findInvalidControls( _input: AbstractControl, _invalidControls: AbstractControl[] ): AbstractControl[] 
    if ( ! _invalidControls ) _invalidControls = [];
    if ( _input instanceof FormControl  ) 
        if ( _input.invalid ) _invalidControls.push( _input );
        return _invalidControls;
    

    if ( ! (_input instanceof FormArray) && ! (_input instanceof FormGroup) ) return _invalidControls;

    const controls = _input.controls;
    for (const name in controls) 
        let control = controls[name];
        switch( control.constructor.name )
        
            case 'AbstractControl':
            case 'FormControl':
                if (control.invalid) _invalidControls.push( control );
                break;

            case 'FormArray':
                (<FormArray> control ).controls.forEach( _control => _invalidControls = findInvalidControls( _control, _invalidControls ) );
                break;

            case 'FormGroup':
                _invalidControls = findInvalidControls( control, _invalidControls );
                break;
        
    

    return _invalidControls;

只为那些需要它的人,所以他们不必自己编码..

编辑#1

要求它也返回无效的 FormArray-s 和 FormGroups,所以如果您也需要,请使用此代码

findInvalidControls( _input: AbstractControl, _invalidControls: AbstractControl[] ): AbstractControl[] 
    if ( ! _invalidControls ) _invalidControls = [];
    if ( _input instanceof FormControl  ) 
        if ( _input.invalid ) _invalidControls.push( _input );
        return _invalidControls;
    

    if ( ! (_input instanceof FormArray) && ! (_input instanceof FormGroup) ) return _invalidControls;

    const controls = _input.controls;
    for (const name in controls) 
        let control = controls[name];
        if (control.invalid) _invalidControls.push( control );
        switch( control.constructor.name )
            
            case 'FormArray':
                (<FormArray> control ).controls.forEach( _control => _invalidControls = findInvalidControls( _control, _invalidControls ) );
                break;

            case 'FormGroup':
                _invalidControls = findInvalidControls( control, _invalidControls );
                break;
        
    

    return _invalidControls;

【讨论】:

我试过了,但没有找到任何无效的 FormGroup 或 FormArray... 只有无效的 FormControl。我犯了同样的错误……看我的回答。 我改进了我的答案,以适应您的用例。【参考方案9】:

您可以记录表单console.log(this.addCustomerForm.value) 的值,它将控制所有控件的值,然后 null 或“”(空)字段表示无效控件

【讨论】:

【参考方案10】:

我认为您应该尝试使用this.form.updateValueAndValidity() 或尝试在每个控件中执行相同的方法。

【讨论】:

【参考方案11】:

试试这个

 findInvalidControls(f: FormGroup) 
    const invalid = [];
    const controls = f.controls;
    for (const name in controls) 
      if (controls[name].invalid) 
        invalid.push(name);
      
    
    return invalid;
  

【讨论】:

【参考方案12】:

就我而言,我禁用了所有表单控件。

这似乎是 Angular 中的一个开放错误:https://github.com/angular/angular/issues/39287

【讨论】:

您的情况,您能否解释一下这是否解决了问题,或者您禁用了表单控件是问题本身。责怪英语是解释性的.. :) 我的问题是表单看起来无效,但是当我试图找到一个无效的表单控件时,它们都不是。 “修复”是将某些字段设置为未禁用,但我不知道它是否适用于每个用例。【参考方案13】:

所以我也反击了这条龙。 和我一样勇敢的骑士,我先聚集my weapons,阅读the maps,然后与这只可怕的野兽战斗。

注意

对于复杂的形式或结构来说,这不是一个可接受的答案,但我发现它适用于没有太多复杂性的简单形式

代码执行以下操作:

以数组形式获取表单控件 循环检查表单控件是否无效 如果无效,过滤器将包含它 如果任何一个无效,结果数组将填充该控件 如果这个结果的长度等于 0,我们可以声明没有控件是无效的并且整个表单都是有效的
isFormValid = () :boolean => 
    Object.values(this.form.controls)
        .filter(c => c.invalid).length === 0

// or single lined
isFormValid = () :boolean => Object.values(this.form.controls).filter(c => c.invalid).length === 0

您可以在您想要的地方、提交按钮、onSubmit 或您自己的最佳位置使用它。

【讨论】:

【参考方案14】:

解决上述问题的更简洁且不可变的递归版本:

P.S:这两种方法你都需要。

Angular 11 之前的工作测试

如果编译器抱怨flatMap,请参考this(Typescript flatMap, flat, flatten doesn't exist on type any[]),不要伪造重启ng serve

findInvalidControls(controls = this.defaultFormGroup.controls) 
    const ctrls = Object.values(controls);
    const names = Object.keys(controls);
    return ctrls.map((a,i) => [a, i])
      .filter(a => (a[0] as FormControl).invalid)
      .flatMap(a => 
        if (a[0] instanceof FormArray) 
          return this.findInvalidArrayControls(a[0].controls);
         else if (a[0] instanceof FormGroup) 
          return this.findInvalidControls(a[0].controls);
         else 
          return names[a[1] as number];
        
      );
  

  findInvalidArrayControls(controls: AbstractControl[]) 
    const ctrls = Object.values(controls);
    const names = Object.keys(controls);
    return ctrls.map((a,i) => [a, i])
      .filter(a => (a[0] as FormControl).invalid)
      .flatMap(a => 
        if (a[0] instanceof FormArray) 
          return this.findInvalidArrayControls(a[0].controls);
         else if (a[0] instanceof FormGroup) 
          return this.findInvalidControls(a[0].controls);
        
         else 
          return names[a[1] as number];
        
     );
  

【讨论】:

【参考方案15】:

检查html页面中的空或空表单控件值

表单控件值:formname.value | json

【讨论】:

以上是关于如何在 Angular(v2 及更高版本)反应形式中查找无效控件的主要内容,如果未能解决你的问题,请参考以下文章

错误:Angular CLI v10.1.0 及更高版本(并且没有 `tsconfig.base.json`)

如何在Safari 10.1.2及更高版本中使用Dexie.js(v 2.0.1)数据库?

如何在 android 2.3 及更高版本中获取当前启动器的包名?

在 Java 11 及更高版本中使用 HttpClient 时如何跟踪 HTTP 303 状态代码?

如何使用支持库 25.0.0 及更高版本获取片段()

如何让 NSScrollView 在 10.9 及更高版本中居中文档视图?