How to use patchValue with FormArray in Angular?

Viewed 1383

I have a FormGroup that has an element like this.

books: this.fb.array([this.buildBookFormGroup()])

fb is the FormBuilder. The buildBookFormGroup() function returns a FromGroup so that I can add/remove multiple books from the same form.

buildBookFormGroup(): FormGroup {
    return this.fb.group({
      author: new FormControl(''),
      price: new FormControl(0),
    });
  }

I have functions for getting the form controls, for adding and for removing formgroups from the 'books' control.

Now, I have added multiple books using this form and persisted them. I want to make an edit page where I want to fetch the existing data and populate the form. For that, I am using 'patchValue'. However, patchValue is not working for FormArray. Looked into multiple questions and answers, and I'm further confused. Here is how I was patching.

if (this.bookshop.books.length > 0) {
      let booksFormGroupArray: FormGroup[] = [];
      this.bookshop.books.forEach((book, index) => {
        booksFormGroupArray.push(this.buildBookFormGroup());
        Object.keys(booksFormGroupArray[index].controls).forEach(key => {
          booksFormGroupArray[index].patchValue({
            [key]: book[key]
          })
        })
      });
      this.bookForm.patchValue({
        books: this.fb.array(booksFormGroupArray)
      });
    }

Only the first book object is getting inserted into the 'books' of the form. Is there any workaround?

1 Answers

When you use patchValue you have to provide value not an array of FormGroups.

So, you patch value directly to dynamically created FormGroups and push the group into the controls of your array, it will work.

So, new patching method will be:

if (this.bookshop.books.length > 0) {
      const bookArray = this.bookForm.get("books") as FormArray;
      bookArray.clear();

      this.bookshop.books.forEach(book => {
        const group = this.buildBookbookFormGroup();
        Object.keys(group.controls).forEach(key => {
          group.patchValue({
            [key]: book[key]
          });
        });
        bookArray.controls.push(group);
      });
    }

And if you want to iterate over FormArray values, then:

get books() {
    return (this.bookForm.get("books") as FormArray).controls.map(m => m.value);
}

HTML:

<div *ngFor="let book of books">
    <p>Author: {{book.author}}, &emsp; Price: {{book.price}}</p>
</div>

Working demo at StackBlitz.

Result:

enter image description here

Related