[英]I want my component not to render if data is not received from api in angular 2
[英]How should I render a Angular MatTable with pagination only after data is received from a REST API?
我正在尝试创建一个表组件,用户可以在其中添加要加载到数据中的REST端点。 数据应该是一个任意对象的数组,因此这里不一定有可能没有严格的模式。
问题是我无法获取表,并且在我在REST端点上发出请求之前等待呈现的是paginator。 我有一个版本可以使分页工作,但是在加载数据之前,paginator(和空表)仍然出现。
我尝试过使用AfterViewChecked,以及初始化paginator和table的各种不同的地方。
Stackblitz样本在这里。 请注意,您不必在模态中输入有效的端点,因为它是从虚拟端点获取的。 我将模态尽可能接近我的默认状态。
component.ts文件:
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatPaginator, MatTable, MatTableDataSource } from '@angular/material';
import { DomSanitizer } from '@angular/platform-browser';
import { RestService } from '../rest.service';
@Component({
providers: [RestService],
selector: 'table-widget',
styleUrls: ['./table-widget.component.scss'],
templateUrl: './table-widget.component.html',
})
export class TableWidgetComponent implements OnInit {
public dataSource: MatTableDataSource<any> = new MatTableDataSource();
// map of header names (which are keys in series) to whether they should be visible
public headers: Map<string, boolean>;
public headerKeys: string[];
@ViewChild(MatTable) private table: MatTable<any>;
@ViewChild(MatPaginator) private paginator: MatPaginator;
// array of header titles, to get arround ExpressionChangedAfterItHasBeenCheckedError
private restEndpoint: string;
private editModalVisibility: boolean;
constructor(private restService: RestService, private sanitizer: DomSanitizer) { }
// called each time an AppComponent is initialized
public ngOnInit(): void {
this.headers = new Map<string, boolean>();
this.editModalVisibility = false;
this.dataSource.paginator = this.paginator;
}
public eventFromChild(data: string): void {
this.restEndpoint = data;
// return value of rest call
this.restService.getFromEndpoint(this.restEndpoint)
.subscribe((series) => {
this.dataSource.data = series;
this.table.renderRows();
// set keys, unless series is empty
if (series.length > 0) {
Object.keys(this.dataSource.data[0]).forEach((value) => {
this.headers.set(value, true);
});
}
this.headerKeys = Array.from(this.headers.keys());
});
}
.
.
.
}
和HTML文件:
<div [ngClass]="['table-widget-container', 'basic-container']">
<div [ngClass]="['table-container']">
<mat-table [ngClass]="['mat-elevation-z8']" [dataSource]="dataSource">
<ng-container *ngFor="let header of getVisibleHeaders()" matColumnDef={{header}}>
<mat-header-cell *matHeaderCellDef> {{header | titlecase}} </mat-header-cell>
<mat-cell *matCellDef="let entry"> {{entry[header]}} </mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="getVisibleHeaders()"></mat-header-row>
<mat-row *matRowDef="let row; columns: getVisibleHeaders();"></mat-row>
</mat-table>
<mat-paginator #paginator [pageSize]="5" [pageSizeOptions]="[5, 11, 20]"></mat-paginator>
</div>
<button *ngIf="dataSource.data.length" (click)="openModal()" class="add-rest-button">
Edit Table
</button>
<add-rest-button (sendDataToParent)="eventFromChild($event)"></add-rest-button>
</div>
当我尝试将*ngIf="dataSource.data.length"
到mat-table
和mat-paginator
,显示的是paginator但是没有“链接”到表,或者表中没有数据,并且我得到以下错误:
TypeError: Cannot read property 'renderRows' of undefined
我知道发生此错误是因为由于ngIf指令而未定义表,但我不确定如何有效地隐藏表。
将属性添加到组件isInitialized
将您的方法更新为:
public eventFromChild(data: string): void {
console.log(this.dataSource.data.length);
this.restEndpoint = data;
// TODO: handle errors of restService
// return value of rest call
this.restService.getFromEndpoint(this.restEndpoint)
.subscribe((series) => {
this.isInitialized = true;
...
}
和你的HTML:
<div *ngIf="isInitialized" [ngClass]="['table-widget-container', 'basic-container']">
.
.
.
</div>
我找到了解决这个问题的方法,但我不会说它解决了根本问题; 在数据准备好之前,只需隐藏表和分页器的容器。
首先,我将HTML中的第2行更改为:
<div [ngClass]="['table-container']" [style.display]="getTableVisibleStyle()">
然后我将以下函数添加到TS文件中:
public getTableVisibleStyle() {
let style: string = 'none';
if (this.tableVisibility) {
style = 'block';
}
// have to do this to get past overzealous security
return this.sanitizer.bypassSecurityTrustStyle(style);
}
其中tableVisibility
是在tableVisibility
中设置为false
的布尔值,然后在更新数据时更改为true
。
它确实工作得很好,虽然看起来很hacky而且不是特别健壮。 如果我发现一种更好的方法,我可能会在以后重新审视这个问题。
您可以执行以下操作来完成此操作。
不要初始化dataSource
变量。
public dataSource: MatTableDataSource<any>;
在table-container
你可以*ngIf="dataSource"
<div [ngClass]="['table-container']" *ngIf="dataSource">
然后在收到数据时初始化变量。
this.dataSource = new MatTableDataSource();
this.dataSource.data = series;
this.table.renderRows();
要再次隐藏它,请将变量null
this.dataSource = null;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.