alflashy
alflashy

Reputation: 81

Angular nested form Array

Spent almost a day now and learned a lot about Angular Reactive forms but still my question unanswered I am simply building a list of items with edit button on each items. Once edit button clicked that item name and description will be able to edit and submit. Simple and works fine. Now I want to add sub item to that item so I have add button which will add form array to that item form group and add sub item. That works but problem is when I click on other Item same subItem is added there too. Looks like I am not specifically adding sub item to the item which I click the edit button on. Here how I am adding sub item

buildItemsForm() {
 this.itemForm = this.fb.group({
  Description: [''],
  ItemCode: ['']
  Variations: this.fb.array([])
 });
}

addVariationToItem() {
  const variationsControl = this.itemForm.get('Variations') as FormArray; // here i need to specify the specific item
  variationsControl.push(this.itemVariation());
}

itemVariation() {
 return this.fb.group({
   color: ['']
})}

This is the result

 {
 Description: 'this is description'
 ItemCode: '123'
 Variations:[
  color:'red' ] // Added Here
 }
{
 Description: 'this is description2'
 ItemCode: '789'
 Variations:[
  color:'red' ] // But Adding it here too
 }

Update: Here is the html. I am getting items from backend response which is looped to render list of items then within the items each form is rendered so I don't think I need to change my top form to FromArray unless I have design my code wrong.

<div *ngFor="let item of items; let i = index" class="card">
 <div class="item-info" *ngIf="!closeInEditItem(item)">
  <div>Description : {{item.Description}}</div>
  <div>Item Code : {{item.ItemCode}}</div>
  <div>Variation:</div>
  <button (click)="editItem(item)">Edit</button></div>
  <div class="form-group" *ngIf="editSate && itemToEdit.Id == item.Id">

  <form (ngSubmit)="updateItem(item)" [formGroup]="itemForm">
  <textarea matInput type="text" class="form-control" placeholder="Edit Description"
    formControlName="Description"></textarea>
  <input matInput type="text" class="form-control" placeholder="Edit Item Code" formControlName="ItemCode">
  <div formArrayName="Variations">
    <div *ngFor="let variationItem of itemForm.controls['Variations'].controls; let i=index">
      <input type="text" placeholder="Add Color Variation" formControlName="color">
    </div>
  </div>
 </form>

 </div>
</div>

Upvotes: 0

Views: 1596

Answers (3)

Anand Bhushan
Anand Bhushan

Reputation: 783

In your case there needs to be another formArray at the top, and inside that you can have your formGroup, so the coed should be like this:-

  buildItemsFormGroup() {
    this.itemForm = this.fb.group({ itemArray: this.fb.array([]) });
    this.buildItemFormArray();
  }

  buildItemFormArray() {  // this method needs to be called for total number of items
    const itemArray = this.itemForm.get('itemArray') as FormArray;
    itemArray.push(this.fb.group({
      Description: [''],
      ItemCode: [''],
      Variations: this.fb.array([])
    }))
  }

  addVariationToItem(index: number) { // item index needs to be passed in which subitem has to be added.
    const itemArray = this.itemForm.get('itemArray') as FormArray;
    const variationsControl = itemArray.get(String(index)).get('Variations') as FormArray;
    variationsControl.push(this.itemVariation()); // other controls can be pushed here.
  }

  itemVariation() {
    return this.fb.group({
      color: ['']
    });
  }




 <form [formGroup]="itemForm">
        <div *ngFor="let itemDetail of itemForm.get('itemArray')['controls']; let i=index" formArrayName="itemArray">
            <div [formGroupName]="i">
                <div class="item-info">
                    <input type= "text"  formControlName="Description"> <br>
                    <input type="text" formControlName="ItemCode">
                    <div *ngFor="let variation of itemDetail.get('Variations')['controls']; let inIndex=index"
                        formArrayName="Variations">
                        <div [formGroupName]="inIndex">
                            <input type="text" formControlName = "color"> <!--All other controls to be added here-->
                </div>
                        </div>

                    </div>
                </div>
                <button (click)="addVariationToItem(i)">Add Color Variation</button>
            </div>
    </form>

Upvotes: 2

Eliseo
Eliseo

Reputation: 57919

If you push an object, the object must be different. You code musk work, but I put an example that not work

**NOT work**
model=this.fb.group({
   color: ['']
}
addVariationToItem() {
  const variationsControl = this.itemForm.get('Variations') as FormArray; 
  //we are adding the same object
  variationsControl.push(this.model);
}

But, repeat, your code must work, because you use a function that return a FormGroup, see your code in stackblitz, check the .html

Upvotes: 0

Gergő &#201;les
Gergő &#201;les

Reputation: 119

You are using one form group and reference it at more than one place. You will need another form array at the top level. It would look like “descriptions” which is a FromArray containing description form groups. And you can locate them based on id to add for each sub array. This article might help:https://alligator.io/angular/reactive-forms-formarray-dynamic-fields/

Upvotes: 1

Related Questions