Home > Back-end >  Angular MatTable does not update with data that comes from a Form
Angular MatTable does not update with data that comes from a Form

Time:10-20

I'm just learning Angular (actually started to learn web development a few months ago). I want to display data from a Form into a table. I have the following model:

export class Content {

    id: number = 0;
    title?: string;
    subtitle?: string;
    content?: string;
    date?: string;
    media?: string;

}

Then, I have a form with some methods as onSubmit() that pushes data into an array.

onSubmit() {
    if (this.selectedContent.id === 0) {
      this.selectedContent.id = this.contentsList.length   1;
      this.contentsList.push(this.selectedContent);
    }

    this.selectedContent = new Content();
    this.notificationService.success('El postulante se creó correctamente');
    console.log(this.contentsList);
}

When I console.log contentsList it shows with the new data from the form, but it does not show in the table:

dataSource = this.contentsList;
columnsToDisplay = ['title', 'subtitle', 'body', 'date'];
expandedElement: Content | null | undefined;

THIS IS MY TABLE:


<table mat-table [dataSource]="dataSource" multiTemplateDataRows >
    <ng-container matColumnDef="{{column}}" *ngFor="let column of columnsToDisplay">
        <th mat-header-cell *matHeaderCellDef> {{column}} </th>
        <td mat-cell *matCellDef="let content"> {{content[column]}} </td>
    </ng-container>
    <!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->
    <ng-container matColumnDef="expandedDetail">
        <td mat-cell *matCellDef="let element[attr.colspan]="columnsToDisplay.length">
            <div  [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'">
                <div >
                    <div > {{content.title}} </div>
                    <div > {{content.subtitle}} </div>
                    <div > {{content.date}} </div>
                    <div > {{content.body}} </div>
                </div>
                <div >
                  {{content.body}}
                  <span > </span>
                </div>
            </div>
        </td>
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
    <tr mat-row *matRowDef="let content; columns: columnsToDisplay;"
       [class.example-expanded-row]="expandedElement === content" (click)="expandedElement = expandedElement === content ? null : content">
    </tr>
    <tr mat-row *matRowDef="let row; columns: ['expandedDetail']" ></tr>
</table>

I'm very sorry for my English, I'm from Argentina.

Thanks in advance.

CodePudding user response:

It might be you have used changeDetection method in your component. Please look at your ts file if you have ChangeDetectionStrategy This is the example code.

@Component({
    selector: 'test',
    changeDetection: ChangeDetectionStrategy.OnPush,
    templateUrl: './test.component.html',
    styleUrls: ['test.component.scss'],
})

If you have change detection then there are two ways to fix this issue.

  1. Simply remove the below line from your component ts file.

    changeDetection: ChangeDetectionStrategy.OnPush

  2. Please add the below code to your ts component file.

Import ChangeDetectorRef at the top of the ts file.

import { ChangeDetectorRef } from '@angular/core';

Add a reference to the constructor.

constructor(public changeDetectorRef: ChangeDetectorRef) { }

Add below the line at the end of onSubmit method.

this.changeDetectorRef.detectChanges();

CodePudding user response:

Has no about changeDetectorRef. the problem with a MatTable is that if we add/remove/change an element of the datasource we need say that redraw the table again See the docs, in the API about DataSource

If a data array is provided, the table must be notified when the array's objects are added, removed, or moved. This can be done by calling the renderRows() function which will render the diff since the last table render. If the data array reference is changed, the table will automatically trigger an update to the rows

So, if you only has one table in your component you can use ViewChild to get the table

@ViewChild(MatTable) table:MatTable<any>

So, when you add the element you use this.table.renderRows().

if (this.selectedContent.id === 0) {
    ...
    this.table.renderRows()
}

But you need take account another thing in your code. You use

//the code is WRONG
if (this.selectedContent.id === 0) {
  this.contentsList.push(this.selectedContent);
}
this.selectedContent = new Content();

Is wrong because you are adding the same "object", you need make a "copy" of the object. If your object is a simple object, this copy can be make it using the spreadOperator, so the function becomes finally like

onSubmit() {
    if (this.selectedContent.id === 0) {
      this.selectedContent.id = this.contentsList.length   1;
      //push a "copy of the object
      this.contentsList.push({...this.selectedContent});
      this.table.renderRows()
    }
    this.selectedContent = new Content();
}
  • Related