Hello. Please tell me why I do not display data in the Material table. I just can not understand. Moreover, there is data, moreover, the number of rows corresponds to the real amount of data, but the table is just empty.

Empty table

device.model.ts

export class DeviceModel { private _id: number; private _device: string; private _status: string; private _lastUpdate: Date; contructor(id?: number, device?: string, status?: string, lastUpdate?: Date) { this._id = id; this._device = device; this._status = status; this._lastUpdate = lastUpdate; } get id(): number { return this._id; } set id(value: number) { this._id = value; } get device(): string { return this._device; } set device(value: string) { this._device = value; } get status(): string { return this._status; } set status(value: string) { this._status = value; } get lastUpdate(): Date { return this._lastUpdate; } set lastUpdate(value: Date) { this._lastUpdate = value; } } 

device.service.ts

 import {Injectable} from '@angular/core'; import {HttpClient} from "@angular/common/http"; import {Observable, of} from "rxjs"; import {AppConfig} from "../../../config/app.config"; import {DeviceModel} from "./device.model"; import {LoggerService} from "../../../core/shared/logger.service"; import {catchError, tap} from "rxjs/operators"; @Injectable({ providedIn: 'root' }) export class DeviceService { private readonly deviceUrl: string; constructor(private httpClient: HttpClient) { this.deviceUrl = AppConfig.endpoints.device; } private static handleError<T>(operation = 'operation', result?: T) { return (error: any): Observable<T> => { // TODO: send the error to remote logging infrastructure console.error(error); // log to console instead // TODO: better job of transforming error for user consumption LoggerService.log(`${operation} failed: ${error.message}`); if (error.status >= 500) { throw error; } return of(result as T); }; } public getAllDevices(): Observable<DeviceModel[]> { return this.httpClient.get<DeviceModel[]>(this.deviceUrl) .pipe( tap(() => LoggerService.log(`fetched devices`)), catchError(DeviceService.handleError('getDevices', [])) ); } } 

device-list.component.ts

 import {Component, OnInit} from '@angular/core'; import {DeviceModel} from "../../shared/device.model"; import {DeviceService} from "../../shared/device.service"; @Component({ selector: 'app-device', templateUrl: './device-list.component.html', styleUrls: ['./device-list.component.scss'] }) export class DeviceListComponent implements OnInit { devices: DeviceModel[]; displayedColumns: ['id', 'device', 'state', 'lastUpdate']; constructor(private deviceService: DeviceService) { } ngOnInit() { this.deviceService.getAllDevices().subscribe((devices: DeviceModel[]) => { this.devices = devices }); console.log(this.devices); } } 

device-list.components.html

  <!-- Table container--> <div class="table-container"> <mat-table #deviceTable [dataSource]="devices"> <!-- Id Column --> <ng-container matColumnDef="id"> <mat-header-cell *matHeaderCellDef> Id </mat-header-cell> <mat-cell *matCellDef="let device">{{device.id}}</mat-cell> </ng-container> <!-- Device Column --> <ng-container matColumnDef="device"> <mat-header-cell *matHeaderCellDef> Device </mat-header-cell> <mat-cell *matCellDef="let device">{{device.device}}</mat-cell> </ng-container> <!-- Status Column --> <ng-container matColumnDef="state"> <mat-header-cell *matHeaderCellDef> State </mat-header-cell> <mat-cell *matCellDef="let device">{{device.state}}</mat-cell> </ng-container> <!-- Lst update Column --> <ng-container matColumnDef="lastUpdate"> <mat-header-cell *matHeaderCellDef> Last update </mat-header-cell> <mat-cell *matCellDef="let device">{{device.lastUpdate}}</mat-cell> </ng-container> <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row> </mat-table> </div> <!-- Table container--> 

And if you display in the usual html-table - there is data.

Html-table

 <div> <table> <thead> <tr> <th>Id</th> <th>DeviceModel</th> <th>State</th> <th>Last update</th> </tr> </thead> <tbody> <tr *ngFor="let device of devices"> <td>{{device.id}}</td> <td>{{device.device}}</td> <td>{{device.status}}</td> <td>{{device.lastUpdate}}</td> </tr> </tbody> </table> </div> 

Of course, I am inclined to think that the data map in DeviceModel in DeviceService is somehow wrong with me. But I just can not understand what is wrong. I also attach the contents of the logs in the console.

Chrome log

In general, there it is already clear that the array of devices is undefined. Can you please tell me how can I fix this situation?

Everyone is very grateful in advance.

PS: I have been doing angular quite recently, just a few days, so I apologize if I can sometimes not notice the obvious things.

I made adjustments according to the answer .

Thanks, it helped, now it works.

    1 answer 1

    First, the data-bound property of the dataSource expects a MatTableDataSource instance, rather than a regular array:

     import { MatTableDataSource } from '@angular/material'; @Component({ ... }) export class DeviceListComponent { public devices = new MatTableDataSource<DeviceModel>([]); constructor(private deviceService: DeviceService) { this.deviceService.getDevices().toPromise().then((devices: DeviceModel[]) => { this.devices.data = devices; }); } } 

    Secondly, there is no need to create such complex models, such as DeviceModel with private properties, getters and setters, in this case, a simple contract is enough:

     export interface Device { id: number; device: string; status: string; lastUpdate: Date; } 

    In addition, you incorrectly initialize displayedColumns ; here you simply declare a type:

     displayedColumns: ['id', 'device', 'state', 'lastUpdate']; 

    And you need to initialize:

     displayedColumns: ReadonlyArray<string> = ['id', 'device', 'state', 'lastUpdate']; 

    Convert the lastUpdate type number to Date :

     import { tap, catchError } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class DeviceService { private readonly deviceUrl: string = AppConfig.endpoints.device; constructor(private http: HttpClient) {} public getAllDevices(): Promise<DeviceModel[]> { return this.http.get<DeviceModel[]>(this.deviceUrl).pipe( tap(() => LoggerService.log(`fetched devices`)) ).toPromise().then((devices: DeviceModel[]) => { devices.forEach((device: DeviceModel) => { device.lastUpdate = new Date(device.lastUpdate); }); return devices; }); } } 

    In the component, we simply call this method in the constructor:

     constructor(private deviceService: DeviceService) { this.deviceService.getAllDevices().then((devices: DeviceModel[]) => { this.dataSource.data = devices; }); } 

    Also in the template you can now use DatePipe

    • I tried to do this, but now comes a new error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays. Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays. The error occurs in the place: <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row> In general, the first cancer sees this solution, I initially tried as shown in the examples material.angular.io , ordinary arrays are used there. - titaniche
    • @titaniche, updated - overthesanity
    • I also wanted to clarify a small question. The service returns the result of the lastUpdate as a number. Is it possible to somehow immediately put it in the Date? In the sense, right in the service when httpClient.get() occurs, in order not to do this separately then inside the component. - titaniche
    • @titaniche, yes, now add - overthesanity
    • Many thanks, I will keep in mind. - titaniche