2

I am trying to figure out how to access this FormArray in a nested FormGroup in order to default fill and dynamically create the form groups. I want to create my form where its value's data structure will mimic the incoming data. I am currently getting the following error:

ERROR Error: Cannot find control with path: 'costs -> 0'

I have tried "flattening" the nested FormGroup, but then I do not get the correct output value as it is missing the nested "items" object. I have commented that code in the below StackBlitz.

StackBlitz: https://stackblitz.com/edit/angular-ivy-6lvano

Data

{
    "type": "Transportation",
    "costs": {
      "items": [
        {
          "category": "Land",
          "name": "Taxi",
          "amount": 50
        },
        {
          "category": "Land",
          "name": "Train",
          "amount": 500
        },
        {
          "category": "Air",
          "name": "Plane",
          "amount": 500
        },
      ]
    }
  }

Component

export class AppComponent  implements OnInit {
  invoiceForm: FormGroup;
  get costs(): FormArray {
    // return this.invoiceForm.get('costs') as FormArray;
    return this.invoiceForm.get('costs.items') as FormArray;
  }
  
  data =  {
    "type": "Transportation",
    "costs": {
      "items": [
        {
          "category": "Land",
          "name": "Taxi",
          "amount": 50
        },
        {
          "category": "Land",
          "name": "Train",
          "amount": 500
        },
        {
          "category": "Air",
          "name": "Plane",
          "amount": 500
        },
      ]
    }
  };

  constructor(private fb: FormBuilder) {
  }

  ngOnInit() {
    let items = FormArray[10] = [];
    if (this.data) {
      for (const cost of this.data.costs.items) {
        items.push(this.buildExpenseItem(cost.category, cost.name, cost.amount));
      }
    } else {
      items = [ this.buildExpenseItem() ];
    }
    
    this.invoiceForm = this.fb.group({
      type: [''],
      // costs: this.fb.array(items)
      costs: this.fb.group({ items: this.fb.array(items) })
    });
  }

  buildExpenseItem(category?: string, name?: string, amount?: number): FormGroup {
    return this.fb.group({
      category: [category],
      name: [name],
      amount: [amount]
    });
  }

  addExpenseItem() {
    this.costs.push(this.buildExpenseItem());
  }

  displayOutput() {
    console.log(this.invoiceForm.value);
  }
}

HTML

<h1>Invoices</h1>

<form [formGroup]="invoiceForm">
  <ng-container formArrayName="costs">
    <div *ngFor="let cost of costs.controls; let i = index">
      <div [formGroupName]="i">
        {{ i }}
        <input formControlName="category" placeHolder="Category">
        <input formControlName="name" placeHolder="Name">
        <input formControlName="amount" placeHolder="Amount">
      </div>
    </div>
  </ng-container>

  <button (click)="addExpenseItem()">Add</button>
</form>

<button (click)="displayOutput()">Display</button>

1 Answer 1

1
<form [formGroup]="invoiceForm">
  <ng-container formArrayName="costs">
    <form formGroupName="items">
    <div *ngFor="let cost of costs.controls; let i = index">
      <div [formGroupName]="i">
        {{ i }}
        <input formControlName="category" placeHolder="Category">
        <input formControlName="name" placeHolder="Name">
        <input formControlName="amount" placeHolder="Amount">
      </div>
    </div>
    </form>
  </ng-container>

  <button (click)="addExpenseItem()">Add</button>
</form>

url: https://stackblitz.com/edit/angular-ivy-rvracc

Here we having two formGroup: invoiceForm and items

Hence, added: <form formGroupName="items">

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.