2

I am having a reactive form with the dynamic input field. I generated these fields with FormArray. Now, I am trying to prepopulate the form with data on the edit screen but couldn't able to do. I tried to populate using setControl method I got this error Cannot find control with path: 'books -> 0 -> name'

These are the sources I took for reference

  1. https://alligator.io/angular/reactive-forms-formarray-dynamic-fields/
  2. https://stackoverflow.com/a/44988197/9715025
  3. https://stackoverflow.com/a/42599327/9715025

Component

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, FormArray } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export interface ServerResponse {
  books: any;
}

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

  formGroup: FormGroup;
  books: FormArray;

  constructor(
    private http: HttpClient,
    private formBuilder: FormBuilder,
  ) {

  }

  createForm() {
    this.formGroup = this.formBuilder.group({
      books: this.formBuilder.array([this.createBookForm()]),
    });
  }

  createBookForm() {
    return this.formBuilder.group({
      name: [],
      author: [],
    });
  }

  addBook() {
    this.books = this.formGroup.get('books') as FormArray;
    this.books.push(this.createBookForm());
  }

  getData(): Observable<ServerResponse> {
    return this.http.get<ServerResponse>('https://demo0331989.mockable.io/library/data/book');
  }

  populateData() {
    this.getData().subscribe(data => {
      this.formGroup.setControl('books', this.formBuilder.array(data.books || []));
    });
  }

  ngOnInit() {
    this.createForm();
    this.populateData();
  }
}

HTML

<form [formGroup]="formGroup">
  <div formArrayName="books" *ngFor="let book of formGroup.get('books')['controls']; let i = index;">
    <div class="row" [formGroupName]="i">
      <input type="text" autocomplete="off" placeholder="*Name"
      formControlName="name">
      <input type="text" autocomplete="off" placeholder="*Author"
      formControlName="author">
      <button type="button" class="btn btn-success" (click)="addBook();">Add</button>
    </div>
  </div>
</form>

I am also sharing the code in Stackblitz. Sample/mock data to be prepopulated. I am not sure what I'm missing here!

3 Answers 3

3

disclamer this answer is complementary with the @bryan60 answer,

I like my function createBookForm has as argument "data", so

createBookForm(data:any) {
    data=data||{name:null,author:null}
    return this.formBuilder.group({
      name: data.name,
      author: data.author,
    });
  }

This allow us write

createBookForm(null) //create a FormGroup with name and author and values null
//or 
createBookForm({name:"Linus Administration",author:"Roger"}) //the formgroup has values

Our function populateData becomes more easy

  populateData() {
    this.getData().subscribe(data => {
      const books = this.formGroup.get('books') as FormArray;
      books.clear();
      data.books.forEach(b => {
        books.push(this.createBookForm(b))
      });
    });
  }

NOTE: I can't see the necesity of declare a formGroup with a unique propertie that is a FormArray, see my answer among others in stackoverflow question

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

1 Comment

Got it working! Well, clean explanation. Thank you.
3

when populating a form array, you need to populate it with filled form groups, so it'd look something like this:

populateData() {
  this.getData().subscribe(data => {
    const books = this.formGroup.get('books') as FormArray; // get the array
    books.clear(); // clear the array 
    data.books.forEach(b => { // iterate data
      let fg = this.createBookForm(); // create the group
      fg.reset(b); // fill the data
      books.push(fg); // add to array
    });
    this.addBook(); // add blank form to end if desired
  });
}

the clearing the form part / adding the blank at the end may or may not be needed.

here is a fixed blitz: https://stackblitz.com/edit/angular-9ts4yv?file=src/app/app.component.ts

Comments

1

I did a fork from your stackblitz example, so take a look. When you want to pre populate your form you need to create BookForm and pass it in setControl, not just array.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.