0

I'm trying to create a table using reactive forms in Angular Material. The problem is how can i implement a functionality that when you click the button "add row", it will automatically add a row, where you can input on a field.

Please see this stackblitz link

createForm() {
  this.form = this.fb.group({
    products: this.fb.array([]),
  });
}

addRow() {
  const rows = this.form.get('products') as FormArray;
  rows.push(
    this.fb.group({
      product_id: [null],
    })
  );
}

1 Answer 1

3

you can make it as follow

  1. inputs in FormArray should have unique id
  2. data-table needs trackBy to avoid row manipulation bug
  3. to update data-table use table.renderRows();

example

    <form [formGroup]="form">

      <mat-card class="mat-elevation-z8">

        <mat-table
          #table
          class="mat-elevation-z0"
          [dataSource]="dataSource" [trackBy]="trackRows">

          <!-- Product Column -->
          <ng-container matColumnDef="product">
            <!-- <mat-header-cell *matHeaderCellDef>Product</mat-header-cell> -->
            <mat-cell *matCellDef="let row; let i = index"> 
              <ng-container [formGroup]="row">
                <mat-form-field>
                  <mat-label>Product</mat-label>
                  <input matInput placeholder="Product"
                    id="product_id_{{i}}"
                    name="product_id_{{i}}"
                    formControlName="product_id" required />
                </mat-form-field>
              </ng-container>
            </mat-cell>
          </ng-container>

          <!-- Unit Column -->
          <ng-container matColumnDef="unit">
            <!-- <mat-header-cell *matHeaderCellDef>Unit</mat-header-cell> -->
            <mat-cell *matCellDef="let row; let i = index"> 
              <ng-container [formGroup]="row">
                <mat-form-field>
                  <mat-label>Unit</mat-label>
                  <input matInput placeholder="Unit"
                    id="unit_{{i}}"
                    name="unit_{{i}}"
                    formControlName="unit" required />
                </mat-form-field>
              </ng-container>
            </mat-cell>
          </ng-container>

          <!-- Header Declarations -->
          <!-- 
          <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
          -->

          <!-- Row Declarations -->
          <mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
        </mat-table>

        <mat-card-actions>
          <button mat-raised-button color="primary"
            (click)="createRow()">
            <mat-icon>add</mat-icon>
            Add New
          </button>
        </mat-card-actions>

      </mat-card>
    </form>

    import { Component, ViewChild } from '@angular/core';
    import { FormGroup, FormBuilder, Validators, FormArray, AbstractControl } from '@angular/forms';
    import { MatTable, MatTableDataSource } from '@angular/material/table';

    @Component({
      selector: 'table-basic-example',
      styleUrls: ['table-basic-example.css'],
      templateUrl: 'table-basic-example.html',
    })
    export class TableBasicExample {
      form: FormGroup;
      private formSubmitAttempt: boolean;
      private uid = 0;

      @ViewChild('table') table: MatTable<any>;

      displayedColumns = ['product', 'unit'];
      dataSource: MatTableDataSource<AbstractControl>;

      get productControlArray() {
        return this.form.get('products') as FormArray;
      }

      constructor(private fb: FormBuilder) {
        this.createForm();
        this.addRow();
        this.dataSource = new MatTableDataSource(
          this.productControlArray.controls);
      }

      createForm() {
        this.form = this.fb.group({
          products: this.fb.array([]),
        });
      }

      trackRows(index: number, row: AbstractControl) {
        return row.value.uid;
      }

      private addRow() {
        const rows = this.productControlArray;
        rows.push(
          this.fb.group({
            uid: this.nextUid(),
            product_id: [undefined, Validators.required],
            unit: [0, Validators.required]
          })
        );
      }

      createRow() {
        this.addRow();
        this.table.renderRows();
      }

      private nextUid() {
        ++this.uid
        return this.uid;
      }
    }
Sign up to request clarification or add additional context in comments.

7 Comments

Great work. But can you do on a table with columns? I have to add may fields from there thats why. Thanks
@Robert you can add many columns into displayedColumns and template for each column should be provided as Product Column
Can you add a Unit column as input there? and also can you remove the <mat-header-cell *matHeaderCellDef>Product</mat-header-cell>
@Robert i update example to remove header need to remove mat-header-row
@Robert Unit column it is pretty same template as Product
|

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.