Search code examples
angulartypescriptangular-materialcrudmat-table

New row is not displayed in the form array in mat-table


I use a form array in my mat-table and would like to add a new row to the table by clicking the add button. I wrote the logic for this. But nothing is shown to me. Can you tell me where my mistake is and how I can fix it?

My Code:

// HTML

<!-- Add rows -->
<button type="submit" mat-raised-button (click)="addRow()">add</button>

<!-- Table-Content -->
              <td mat-cell *matCellDef="let row; let i = index;">
                <div formArrayName="rows" *ngIf="attributesWithFormControls.includes(column.attribute); else otherColumns">
                  <span class="edit-cell" [formGroupName]="i">
                      <mat-form-field>
                        <label>
                          <input matInput type="text" [formControlName]="column.attribute">
                        </label>
                      </mat-form-field>
                  </span>
                </div>
                <ng-template #otherColumns>
                  {{ column.object  !== null ? row[column.object][column.attribute] : row[column.attribute]  }}
                </ng-template>
              </td>

// TS

// For the form
calcBookingsForm: FormGroup;

// Variables for Data Table
  public columns = [];
  public dataSource: MatTableDataSource<MyModel> = null;

public displayedColumns: EditColumns[] = [
{ attribute: 'firstName', name: 'Firstname', object: null },
{ attribute: 'secondName', name: 'Lastname', object: null }
];

// Attributes defined here are displayed as input in the front end
public attributesWithFormControls = ['firstName', 'secondName'];

 ngOnInit(): void {
this.columns = this.displayedColumns.map(c => c.attribute);

 // CalcBookings form
    this.calcBookingsForm = this.formBuilder.group({
      rows: this.formBuilder.array([this.initItemRows()])
    });
}

 get formArr() {
    return this.calcBookingsForm.get('rows') as FormArray;
  }

  initItemRows() {
    return this.formBuilder.group({
      firstName: [''],
      lastName: [''],
    });
  }

// To add new row in the CalculatoryBookings
  addRow() {
    this.formArr.push(this.initItemRows())
  }


Solution

  • Columns represent data types in rows, so adding a row to a table is like adding an empty record. It depends what you want to do with the data in a new row when entering the data you have to add another logic for the behavior of the row when saving the new data, in this example I am teaching how to add a new row. Html file with table and one button to add row:

    <div style="margin-top: 64px; margin-left: 64px;">
        <button (click)="AddRow()" mat-raised-button>Add</button>
    </div>
    
    <div style="margin-top: 64px;"></div>
    <table mat-table [dataSource]="dataSource" multiTemplateDataRows class="mat-elevation-z8">
        <ng-container matColumnDef="{{column}}" *ngFor="let column of columnsToDisplay">
            <th mat-header-cell *matHeaderCellDef> {{column}} </th>
    
            <td mat-cell *matCellDef="let element">
                <mat-form-field>
                    <input matInput [value]="element[column]" />
                </mat-form-field>
            </td>
        </ng-container>
    
        <tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
        <tr mat-row *matRowDef="let element; columns: columnsToDisplay;">
        </tr>
    </table>
    

    Class with data that You want to add:

    export interface ProductElement {
      name: string;
      id: string;
      color: string;
      year: number;
      price: number;
    }
    

    Data to create rows with example data:

    // List of Brands
      brands: string[] = ['Vapid', 'Carson', 'Kitano', 'Dabver', 'Ibex', 'Morello', 'Akira', 'Titan', 'Dover', 'Norma'];
      // List of Colors
      colors: string[] = ['Black', 'White', 'Red', 'Blue', 'Silver', 'Green', 'Yellow'];
      // Column definition
      columnsToDisplay: string[] = ['id', 'name', 'year', 'color', 'price'];
      dataSource: MatTableDataSource<ProductElement>;
    

    Method for create random data:

    generateName = () => {
        return this.brands[Math.floor(Math.random() * Math.floor(10))];
      }
    
      generatePrice = () => {
        return Math.floor(Math.random() * Math.floor(50000) + 1);
      }
    
      generateColor = () => {
        return this.colors[Math.floor(Math.random() * Math.floor(7))];
      }
    
      generateYear = () => {
        return 2000 + Math.floor(Math.random() * Math.floor(19));
      }
    
      createId(): string {
        let id = '';
        const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        for (let i = 0; i < 5; i++) {
          id += chars.charAt(Math.floor(Math.random() * chars.length));
        }
        return id;
      }
    

    method to add new row:

    newRow = () => {
        const data = {
          id: this.createId(),
          name: this.generateName(),
          year: this.generateYear(),
          color: this.generateColor(),
          price: this.generatePrice(),
        };
        return data;
      }
    

    and method to add new row with sample data.

    AddRow(): void {
        ELEMENT_DATA.push(this.newRow());
        this.dataSource = new MatTableDataSource<ProductElement>(ELEMENT_DATA);
      }
    

    Finaly result: Resultado

    Practice with this and you will tell me how it goes. Update: in You example if You change Add method like below its work enter image description here Result:

    enter image description here