Всем привет. Подскажите пожалуйста, почему у меня не происходит отображение данных в 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-таблице - данные есть.
<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. Но я никак не могу понять, что же не так. Так же прикладываю содержание логов в консоли.
В целом там уже видно, что массив devices у меня undefined. Подскажите пожалуйста, как мне можно исправить эту ситуацию?
Всем очень благодарен заранее.
P.S.: Я занимаюсь angular совсем-совсем недавно, буквально несколько дней, поэтому прошу прощения, если я могу порой не замечать вполне очевидные вещи.
Я внес корректировки согласно ответу.
Спасибо, это помогло, теперь работает.
1 ответ 1
Во-первых 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
-
Попробовал так сделать, но теперь выходит новая ошибка:
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, там используются обычные массивы.titaniche– titaniche2018年09月02日 09:50:46 +00:00Commented 2 сент. 2018 в 9:50 -
@titaniche, обновилuser303477– user3034772018年09月02日 10:24:06 +00:00Commented 2 сент. 2018 в 10:24
-
Еще хотел маленький вопросик уточнить. Сервис у меня возвращает результат lastUpdate как number. Можно-ли как-то сразу его мапить в Date? В смысле, прямо в сервисе, когда происходит
httpClient.get(), чтобы отдельно потом внутри компонента не делать этого.titaniche– titaniche2018年09月02日 11:05:43 +00:00Commented 2 сент. 2018 в 11:05 -
@titaniche, да, сейчас добавлюuser303477– user3034772018年09月02日 11:06:43 +00:00Commented 2 сент. 2018 в 11:06
-
Большое спасибо, буду иметь ввиду.titaniche– titaniche2018年09月02日 11:37:23 +00:00Commented 2 сент. 2018 в 11:37
Начните задавать вопросы и получать на них ответы
Найдите ответ на свой вопрос, задав его.
Задать вопросИзучите связанные вопросы
Посмотрите похожие вопросы с этими метками.