javascriptangulartypescriptangular6angular-material-6

Angular 6 material table adds row(form field) with last entered value


I am trying to add form fields and displaying it in the form of material table. But when I try to add a row(form field) it displays form field with last entered value(which is 1 2 3 in this case). I don't know where I have gone wrong. Hope someone could help me. Thanks in advance

Below is the screenshot of the problemImage displaying last entered field

Below is my HTML Code

<button mat-raised-button color="primary" (click)="createNew()"> Add </button>
<form [formGroup]="fields" class="example-form">
<mat-table [dataSource]="dataSource">
    <ng-container matColumnDef="fieldName">
      <mat-header-cell *matHeaderCellDef> Field Name </mat-header-cell>
      <mat-cell *matCellDef="let row">
        <mat-form-field>
          <input formControlName="fieldName" placeholder="Field Name" matInput>
        </mat-form-field>
      </mat-cell>
    </ng-container>
    <ng-container matColumnDef="fieldType">
      <mat-header-cell *matHeaderCellDef> Field Type </mat-header-cell>
      <mat-cell *matCellDef="let row">
        <mat-form-field>
            <mat-select placeholder="Field Type">
                <mat-option *ngFor="let ft of fieldTypes" [value]="ft.value">
                  {{ft.value}}
                </mat-option>
              </mat-select>
        </mat-form-field>
      </mat-cell>
    </ng-container>
    <ng-container matColumnDef="fieldSize">
        <mat-header-cell *matHeaderCellDef> Field Size </mat-header-cell>
        <mat-cell *matCellDef="let row">
          <mat-form-field>
            <input formControlName="fieldSize" type="number" placeholder="Field Size" matInput>
          </mat-form-field>
        </mat-cell>
      </ng-container> 
    <ng-container matColumnDef="fieldOrder">
        <mat-header-cell *matHeaderCellDef> Field Order </mat-header-cell>
        <mat-cell *matCellDef="let row">
          <mat-form-field>
            <input formControlName="fieldOrder" type="number" placeholder="Field Order" matInput>
          </mat-form-field>
        </mat-cell>
      </ng-container>  
      <ng-container matColumnDef="actions">
        <mat-header-cell *matHeaderCellDef> Actions </mat-header-cell>
        <mat-cell *matCellDef="let row; let i = index">
          <button mat-raised-button color="warn" (click)="deleteRow(i)"> Delete </button>
        </mat-cell>
      </ng-container> 
    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
  </mat-table>
  <div class="buttonDiv">
    <button type="submit" mat-raised-button color="primary">Save</button>
  </div>  
  </form>

And Below is my TS Code

export class AppComponent {

  constructor(private fb: FormBuilder){}
  displayedColumns = ['fieldName', 'fieldType', 'fieldSize', 'fieldOrder', 'actions'];
  dataSource = new MatTableDataSource<Element>();
  element: Element; 

  fieldTypes = [
    {value: 'Date'},
    {value: 'Text'},
    {value: 'Radio'},
    {value: 'CheckBox'},
  ];
  createNew(){

    this.element= new Element();
    this.dataSource.data.push(this.element);
    this.dataSource._updateChangeSubscription();
  }

EDIT - 1

Stackblitz link - https://angular-ejtxrn.stackblitz.io/


Solution

  • Except for the material table HTML, you can see how formArray and formGroup work (as per Angular.io guide)...

    Add button will add rows and upon save, you will see the contents of the array; Kindly replacing your HTML and TS file by the following 2 codes...

    component.html

    <button mat-raised-button color="primary" (click)="createNew()"> Add </button>
    <form [formGroup]="fields" class="example-form" (submit)="showData()">
    
      <div class='' formArrayName='elementArray' *ngFor="let item of fields.get('elementArray').controls; let i = index">
        <div [formGroupName]="i">
          <input type="text" formControlName="fieldName" placeholder="fieldName">
    
          <select formControlName="fieldType" placeholder="fieldType">
            <option *ngFor="let ft of fieldTypes" [value]="ft.value">
              {{ft.value}}
            </option>
          </select>
    
          <input type="number" formControlName="fieldSize" placeholder="fieldSizer">
          <input type="number" formControlName="fieldOrder" placeholder="fieldOrder">
    
    
          <!--
      <mat-table [dataSource]="dataSource">
          <ng-container matColumnDef="fieldName">
            <mat-header-cell *matHeaderCellDef> Field Name </mat-header-cell>
            <mat-cell *matCellDef="let row">
              <mat-form-field>
                <input formControlName="fieldName" placeholder="Field Name" matInput>
              </mat-form-field>
            </mat-cell>
          </ng-container>
          <ng-container matColumnDef="fieldType">
            <mat-header-cell *matHeaderCellDef> Field Type </mat-header-cell>
            <mat-cell *matCellDef="let row">
              <mat-form-field>
                  <mat-select placeholder="Field Type">
                      <mat-option *ngFor="let ft of fieldTypes" [value]="ft.value">
                        {{ft.value}}
                      </mat-option>
                    </mat-select>
              </mat-form-field>
            </mat-cell>
          </ng-container>
          <ng-container matColumnDef="fieldSize">
              <mat-header-cell *matHeaderCellDef> Field Size </mat-header-cell>
              <mat-cell *matCellDef="let row">
                <mat-form-field>
                  <input formControlName="fieldSize" type="number" placeholder="Field Size" matInput>
                </mat-form-field>
              </mat-cell>
            </ng-container> 
          <ng-container matColumnDef="fieldOrder">
              <mat-header-cell *matHeaderCellDef> Field Order </mat-header-cell>
              <mat-cell *matCellDef="let row">
                <mat-form-field>
                  <input formControlName="fieldOrder" type="number" placeholder="Field Order" matInput>
                </mat-form-field>
              </mat-cell>
            </ng-container>  
            <ng-container matColumnDef="actions">
              <mat-header-cell *matHeaderCellDef> Actions </mat-header-cell>
              <mat-cell *matCellDef="let row; let i = index">
                <button mat-raised-button color="warn" (click)="deleteRow(i)"> Delete </button>
              </mat-cell>
            </ng-container> 
          <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
          <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
        </mat-table>
        -->
    
        </div>
      </div>
      <div class="buttonDiv">
        <button type="submit" mat-raised-button color="primary">Save</button>
    
      </div>
    </form>
    
    <hr/>
    <span *ngIf='messageText'>
        <h3>{{messageText}}</h3>
        <p *ngFor="let item of fields.value.elementArray">
          {{item.fieldName}} | {{item.fieldType}} | {{item.fieldSize}} | {{item.fieldOrder}}
        </p>
      </span>

    component.ts

    import { Component } from '@angular/core';
    import { MatTableDataSource } from '@angular/material/table';
    import { FormBuilder, FormGroup, FormArray } from '@angular/forms';
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: [ './app.component.css' ]
    })
    export class AppComponent  {
      title = 'OnboardingApp';
      messageText = '';
      constructor(private fb: FormBuilder){}
      displayedColumns = ['fieldName', 'fieldType', 'fieldSize', 'fieldOrder', 'actions'];
      dataSource = new MatTableDataSource<Element>();
      element: Element; 
      fields = this.fb.group({
        elementArray: this.fb.array([this.createElementData()])
      });
      fieldTypes = [
        {value: 'Date'},
        {value: 'Text'},
        {value: 'Radio'},
        {value: 'CheckBox'},
      ];
    
        createNew(){
          const newRow = this.createElementData();
          this.elementArray.push(newRow);
        }
    
      get elementArray(): FormArray{
        return this.fields.get("elementArray") as FormArray;
      }
    
    createElementData():FormGroup{
            return this.fb.group({
                fieldName: [''],
                fieldType: [''],
                fieldSize: [''],
                fieldOrder: ['']
            });
        }
    
      deleteRow(index){
        /* console.log(index);
        const data = this.dataSource.data;
        console.log(data.splice(index,1));
        this.dataSource.data = data; */
        this.dataSource.data.splice(index,1);
        this.dataSource._updateChangeSubscription();
      }
      showData(){
        if (this.fields.value.elementArray.length>0){
          console.log(this.fields.value.elementArray);
          this.messageText = 'check console.log for the array or below';
        }
      }
    }
    
    export class Element{
      fieldName: string;
      fieldType: [];
      fieldSize: number;
      fieldOrder: number;
    }