0

I am working on an Angular 4.X project and I am creating some HTML input fields (mostly of type text) on button click. The creation of Input Boxes dynamically is working fine but I am not able to implement validation to those fields. I am getting the following error.

Cannot read property 'invalid' of null(…)

I have created a plunk for the same. Following is the link for the plunk that I have created --

https://plnkr.co/edit/PCFD43GK91zo2ivQ9lf7?p=preview For easy reference please find the code below --

//Root app component
import {Component, NgModule} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import { FormBuilder, FormGroup, Validators, FormArray, FormControl } from '@angular/forms';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

@Component({
  selector: 'my-app',
  template: `<hr>
              <div>  
                <form [formGroup]="orderForm" (ngSubmit)="OnSubmit(orderForm.value)">
                  <div>
                    <input type="text" formControlName="customerName"/>
                    <input type="text" formControlName="email"/>
                  </div>
                  <div formArrayName="items" *ngFor="let item of items.controls; let i = index;">
                    <div [formGroupName]="i">
                      <input type="text" formControlName="name" placeholder="Item name"/>
                      <small *ngIf="IsValidField('name')" class="text-danger">
                        Name is required
                      </small>
                      <input type="text" formControlName="description" placeholder="Item description"/>
                      <small *ngIf="IsValidField('description')" class="text-danger">
                        Description is required
                      </small>
                      <input type="text" formControlName="price" placeholder="Item price"/>
                      <small *ngIf="IsValidField('price')" class="text-danger">
                        Price is required
                      </small>
                    </div>
                    Chosen name: {{ orderForm.controls.items.controls[i].controls.name.value }}
                  </div>
                  <button type="submit">Save</button>
                  <button type="button" (click)="addItem()">Add More</button>
                </form>
              <div>`,
})

export class App {

  constructor(private formBuilder: FormBuilder) {  }

  public orderForm: FormGroup;



  ngOnInit() {
      this.orderForm = this.formBuilder.group({
        customerName: '',
        email: '',
        items: this.formBuilder.array([ this.createItem()])
      });
  }

 createItem(): FormGroup {
    return this.formBuilder.group({
      name: ['',[Validators.required,Validators.maxLength(10)]],
      description: '',
      price: ['',[Validators.required,Validators.pattern("[(0-9)*]")]]
    });
  }

   get items(): FormArray {
    return this.orderForm.get('items') as FormArray;
   };

  addItem(): void {
  this.items.push(this.createItem());
  }

  public OnSubmit(formValue: any) {
        console.log(JSON.stringify(formValue));
    }

    public IsValidField(field: string) {
        return (this.orderForm.get(field).invalid && this.orderForm.get(field).touched) || (this.orderForm.get(field).invalid && this.orderForm);
    }

}

@NgModule({
  imports: [ BrowserModule, FormsModule, ReactiveFormsModule ],
  declarations: [ App ],
  bootstrap: [ App ]
})
export class AppModule {}

Any help would be appreciated.

1 Answer 1

1

You're searching for dynamically added form fields inside orderForm, which isn't available there. You should query form correctly before accessing its value. I draw below figure which will help you to understand how the dynamically created form architecture.

orderForm (FormGroup)
  |
  - Items (FormArray)
    |
     - 0 (FormGroup)
       - name 
       - description
       - price
    |
     - 1 (FormGroup)
       - name
       - description
       - price
    |
    |.........

So IsValidField function should get the index of formArray element, and field name. By which you can easily query form element.

public IsValidField(i: number, field: any) {
    var f = this.orderForm
            .get('items') //retrieve items FormArray
            .get(i.toString()) //retrieve items FormGroup
            .get(field); //retrieve items form field
    return (f.invalid && f.touched) || (f.invalid && this.orderForm);
}

Then change IsValidField function call on *ngIf accordingly.

*ngIf="IsValidField(i, 'name')"
*ngIf="IsValidField(i, 'description')"
*ngIf="IsValidField(i, 'price')"

Demo Plunker

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks Pankaj it really helped. Could you please tell me how do I populate these field with data, I am trying to do that but its not working. Here is the Plunker for that link
@AmitAnand use .patchValue to fill values
But how would I do it for the multiple fields and that too dynamically. Could you please look into the Plunker and provide me a solution.
@AmitAnand I'd request you to open new question, so that other people also can jump in.
I have created one. Please find it here link Thanks in advance

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.