Angular FormArray display validation errors

别等时光非礼了梦想. 提交于 2019-12-04 06:27:37

I don't really think this would be possible completely on the template. That's because to access the FormArray's control's state in your template, you'll have to access this.formGroup.get('features').controls[i].invalid. But since get returns an instance of type AbstractControl, you won't have access to the controls array on it. For that, you'll have to typecast whatever is returned from this.formGroup.get('features') into a FormArray. And I don't really think that would be possible on the template.

You'll have to create a method in your class that would return the validity of the control based on the index.

So if we continue to refer to your stackblitz eg, here's how:

<form [formGroup]="formGroup">
  <div formArrayName="features">
    <div 
      class="row no-gutters form-group" 
      *ngFor="let feature of features.controls; let index = index; let last = last">
      <input 
        type="text" 
        class="form-control px-2 col" 
        [formControlName]="index" 
        title="feature" 
        required>

        IS Invalid - {{ getValidity(index) }}

      <div class="col-3 col-md-2 row no-gutters">
        <button 
          class="col btn btn-outline-danger" 
          (click)="removeFeature(index)">
          -
        </button>
        <button 
          class="col btn btn-success" 
          *ngIf="last" 
          (click)="addFeature()">
          +
        </button>
      </div>
    </div>
  </div>
</form>

And in your class:

import { Component } from '@angular/core';
import { FormArray, FormBuilder, FormControl, Validators } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

  constructor(
    private fb: FormBuilder,
  ) {}

  formGroup = this.fb.group({
    features: this.fb.array([this.fb.control('', Validators.required)])
  });

  get features(): FormArray {
    return this.formGroup.get('features') as FormArray;
  }

  addFeature(): void {
    this.features.push(this.fb.control('', Validators.required));
  }

  getValidity(i) {
    return (<FormArray>this.formGroup.get('features')).controls[i].invalid;
  }

  removeFeature(index): void {
    this.features.removeAt(index);
    console.log(this.features);
  }

}

UPDATE

A few months back I realized that calling a method in one of the data-binding syntaxes(i.e. String Interpolation - {{ ... }}, Property Binding - [propertyName]="methodName()", or Attribute Binding - [class.class-name]="methodName()" OR [style.styleName]="methodName()") is extremely costly as far as performance is concerned.

So, you should do it using:

{{ formGroup.controls['features'].controls[index].invalid }}

Instead of:

{{ getValidity(index) }}

Here's an Updated Working Sample StackBlitz for your ref.

In the ngFor statement, you are defining the variable "feature" to use for each control of the form array.

*ngFor="let feature of features.controls; let index = index; let last = last"

You can use that variable to get the invalid status of that control.

feature.invalid 

Here is the Stackblitz


Additionally

You don't need the required attribute in the HTML when you are using reactive forms.

So this

<input type="text" class="form-control px-2 col" [formControlName]="index" title="feature" required>

Can be

<input type="text" class="form-control px-2 col" [formControlName]="index" title="feature">

The FormGroup class has a get method which returns the abstractControl for the given key. The one you use in formControlName.

Here the links for both Api docs:
AbstractControl
FormGroup

<form [formGroup]="form">
  <input formControlName="name" type="text" />
  <div *ngIf="form.get('name').invalid">
    <p><Message you like to show/p>
  </div>
</form>

You Should use form controls to achieve that sharply.

<div *ngIf="formGroup.get('features').controls[i].controls.index.invalid && (formGroup.get('features').controls[i].controls.index.dirty || formGroup.get('features').controls[i].controls.index.touched)"                                          class="text-center error">
<p [hidden]="!formGroup.get('features').controls[i].controls.index.errors.required">Index is required.</p>
</div>

As above answer using

feature.invalid from features.controls

let you validate all elements inside that controlat once.

However if you want to validate a specific element can you following code:

> feature.controls.controlname.invalid

Note: I'm using feature not features

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!