0

Всем привет. Подскажите пожалуйста, почему у меня не происходит отображение данных в Material 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-->

Причем если вывести в обычной html-таблице - данные есть.

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>

Я конечно склоняюсь к тому, что у меня как-то неправильно происходит map данных в DeviceModel в DeviceService. Но я никак не могу понять, что же не так. Так же прикладываю содержание логов в консоли.

Chrome log

В целом там уже видно, что массив devices у меня undefined. Подскажите пожалуйста, как мне можно исправить эту ситуацию?

Всем очень благодарен заранее.

P.S.: Я занимаюсь angular совсем-совсем недавно, буквально несколько дней, поэтому прошу прощения, если я могу порой не замечать вполне очевидные вещи.

Я внес корректировки согласно ответу.

Спасибо, это помогло, теперь работает.

задан 1 сент. 2018 в 12:38

1 ответ 1

0

Во-первых data-bound свойство dataSource ожидает инстанс MatTableDataSource, а не обычный массив:

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;
 });
 }
}

Во-вторых не нужно создавать такие комплексные модели, типа DeviceModel с приватными свойствами, геттерами и сеттерами, в данном случае простого контракта достаточно:

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

К тому же вы неправильно инициализируете displayedColumns, здесь вы объявляете просто тип:

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

А нужно провести инициализацию:

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

Преобразовываем lastUpdate типа number в 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;
 });
 }
}

В компоненте просто вызываем данный метод в конструкторе:

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

Также в шаблоне вы теперь можете использовать DatePipe

ответ дан 1 сент. 2018 в 15:21
5
  • Попробовал так сделать, но теперь выходит новая ошибка: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays. Ошибка происходит в месте: <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row> Вообще, первый рак вижу такой вариант решения, я изначально пробовал как показано в примерах material.angular.io, там используются обычные массивы. Commented 2 сент. 2018 в 9:50
  • @titaniche, обновил Commented 2 сент. 2018 в 10:24
  • Еще хотел маленький вопросик уточнить. Сервис у меня возвращает результат lastUpdate как number. Можно-ли как-то сразу его мапить в Date? В смысле, прямо в сервисе, когда происходит httpClient.get(), чтобы отдельно потом внутри компонента не делать этого. Commented 2 сент. 2018 в 11:05
  • @titaniche, да, сейчас добавлю Commented 2 сент. 2018 в 11:06
  • Большое спасибо, буду иметь ввиду. Commented 2 сент. 2018 в 11:37

Ваш ответ

Черновик сохранён
Черновик удалён

Зарегистрируйтесь или войдите

Регистрация через Google
Регистрация через почту

Отправить без регистрации

Необходима, но никому не показывается

Отправить без регистрации

Необходима, но никому не показывается

Нажимая «Отправить ответ», вы соглашаетесь с условиями пользования и подтверждаете, что прочитали политику конфиденциальности.

Начните задавать вопросы и получать на них ответы

Найдите ответ на свой вопрос, задав его.

Задать вопрос

Изучите связанные вопросы

Посмотрите похожие вопросы с этими метками.