33

I am building an app using Angular 4.0.2. How can I add a button to a form to add a new row of input and a delete button for a particular row to delete? I mean that I want a form something like this. I want my form to look something like this:

Form Image.

Here is my code:

add-invoice.component.html

    <h3 class="page-header">Add Invoice</h3>
    <form [formGroup]="invoiceForm">
      <div formArrayName="itemRows">
        <div *ngFor="let itemrow of itemRows.controls; let i=index" [formGroupName]="i">
          <h4>Invoice Row #{{ i + 1 }}</h4>
          <div class="form-group">
            <label>Item Name</label>
            <input formControlName="itemname" class="form-control">
          </div>
          <div class="form-group">
            <label>Quantity</label>
            <input formControlName="itemqty" class="form-control">
          </div>
          <div class="form-group">
             <label>Unit Cost</label>
             <input formControlName="itemrate" class="form-control">
          </div>
          <div class="form-group">
            <label>Tax</label>
            <input formControlName="itemtax" class="form-control">
          </div>
          <div class="form-group">
            <label>Amount</label>
            <input formControlName="amount" class="form-control">
          </div>
          <button *ngIf="i > 1" (click)="deleteRow(i)" class="btn btn-danger">Delete Button</button>
        </div>
      </div>
      <button type="button" (click)="addNewRow()" class="btn btn-primary">Add new Row</button>
    </form>
    <p>{{invoiceForm.value | json}}</p>

Here is my code for add-invoice.component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormArray, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-add-invoice',
  templateUrl: './add-invoice.component.html',
  styleUrls: ['./add-invoice.component.css']
})

export class AddInvoiceComponent implements OnInit {
  invoiceForm: FormGroup;

  constructor(
    private _fb: FormBuilder
  ) {
    this.createForm();
  }

  createForm(){
    this.invoiceForm = this._fb.group({
      itemRows: this._fb.array([])
    });
    this.invoiceForm.setControl('itemRows', this._fb.array([]));
  }

  get itemRows(): FormArray {
    return this.invoiceForm.get('itemRows') as FormArray;
  }

  addNewRow(){
    this.itemRows.push(this._fb.group(itemrow));
  }

  ngOnInit(){

  }
}
halfer
  • 19,824
  • 17
  • 99
  • 186
Climb Tree
  • 490
  • 1
  • 7
  • 15

1 Answers1

67

Here's a shortened version of your code:

When you init your form, you can add one empty formgroup inside your formArray:

ngOnInit() {
  this.invoiceForm = this._fb.group({
    itemRows: this._fb.array([this.initItemRows()])
  });
}

get formArr() {
  return this.invoiceForm.get('itemRows') as FormArray;
}

Then the function:

initItemRows() {
  return this._fb.group({
    // list all your form controls here, which belongs to your form array
    itemname: ['']
  });
}

Here is the addNewRow and deleteRow functions:

addNewRow() {
  this.formArr.push(this.initItemRows());
}

deleteRow(index: number) {
  this.formArr.removeAt(index);
}

Your form should look like this:

<form [formGroup]="invoiceForm">
  <div formArrayName="itemRows">
    <div *ngFor="let itemrow of formArr.controls; let i=index"  [formGroupName]="i">
      <h4>Invoice Row #{{ i + 1 }}</h4>
      <div>
        <label>Item Name</label>
        <input formControlName="itemname">
      </div>
      <button type="button" (click)="deleteRow(i)" *ngIf="formArr.controls.length > 1" >
        Delete
      </button>
    </div>
  </div>
  <button type="button" (click)="addNewRow()">Add new Row</button>
</form>

Here's a

DEMO

AT82
  • 71,416
  • 24
  • 140
  • 167
  • Is there any way to do this in template-driven form. – Climb Tree Apr 20 '17 at 19:06
  • In this case I would though suggest you use model-driven form, it is much cleaner this way in my opinion. – AT82 Apr 20 '17 at 19:12
  • I am posting another question just now. Please help me on that also. I will be really thankful to you. – Climb Tree Apr 20 '17 at 19:16
  • Sure, I can take a look :) – AT82 Apr 20 '17 at 19:17
  • It was just a going through the button. Bro, I am really thankful to you for this answer. I will surely buy you a beer when my first Salary will come. By the way, please go through http://stackoverflow.com/q/43528200/7882245 – Climb Tree Apr 20 '17 at 19:25
  • Can you explain a little about what's going on in the code? I am not getting it. – Climb Tree May 23 '17 at 09:43
  • don't forget to call `formArray.markAsDirty()` in order to make the form dirty – moi_meme Dec 21 '17 at 15:03
  • @AJT_82 please suggest me any solution https://stackoverflow.com/questions/50550246/initialize-form-array-with-existing-array-in-angular-reactive-form/50550296#50550296 – naila naseem May 27 '18 at 10:24
  • @AJT_82 I am hanged in a position like this. wondering if you can help me: https://stackoverflow.com/questions/50798049/angular-4-add-dynamic-row-and-get-data-in-controller – SK. Jun 11 '18 at 13:50
  • @AJT_82 thank you for this answer. I am stuck in one problem that how I can get value from an input of any particular index.Is there any way to fetch the value of desired input – Arjun May 31 '19 at 07:14