重新加载分页垫-table 无法显示 Angular 中的行
Reloading a paginated mat-table fails to display rows in Angular
我有一个 mat-table 从 firebase 加载数据。
全部-matches.component.html
...
<mat-table #table [dataSource]="dataSource" class="mat-elevation-z8">
...
<ng-container matColumnDef="rank">
<mat-header-cell *matHeaderCellDef> Rank </mat-header-cell>
<mat-cell *matCellDef="let entry"> {{entry.rank}} </mat-cell>
</ng-container>
<ng-container matColumnDef="weightClass">
<mat-header-cell *matHeaderCellDef> Weight Class </mat-header-cell>
<mat-cell *matCellDef="let entry"> {{entry.weightClass}} </mat-cell>
</ng-container>
...
<mat-header-row *matHeaderRowDef="columnsToDisplay"></mat-header-row>
<mat-row *matRowDef="let row; columns: columnsToDisplay;"></mat-row>
</mat-table>
...
根据在线建议(我还没有完全理解),我选择使用数据源对象来填充我的 table。 dataSource 在 all-matches.component.ts:
中被实例化
全部-matches.component.ts
...
@Component({
selector: 'app-all-matches',
templateUrl: './all-matches.component.html',
styleUrls: ['./all-matches.component.scss']
})
export class AllMatchesComponent implements OnInit, OnDestroy, AfterViewInit {
private columnsToDisplay = ['rank','weightClass', 'ageClass','athlete1Name', 'athlete2Name', 'gender','tournamentName','location', 'date', 'matchRating', 'videoUrl']; //TODO make this dynamic somehow
private loading = true;
...
@ViewChild(MatPaginator) paginator: MatPaginator;
constructor(private authService: AuthorizationService, private d3Service: D3Service, private dbService: DatabaseService, private textTransformationService: TextTransformationService, private dataSource: MatchDataSource) { }
ngOnInit() {
...
this.pageSize = 2; //TODO increase me to something reasonable
this.dataSource = new MatchDataSource(this.dbService);
this.dataSource.loadMatches('test', '', '', 0, this.pageSize);
this.dbService.getMatchCount().subscribe(results=>{
this.matchCount = results;
});
...
}
MatchDataSource.model.ts
import {CollectionViewer, DataSource} from "@angular/cdk/collections";
import { BehaviorSubject , Observable , of } from 'rxjs';
import { catchError, finalize } from 'rxjs/operators';
import { Match } from './match.model';
import { DatabaseService } from './database.service';
import { Injectable } from '@angular/core';
@Injectable()
export class MatchDataSource implements DataSource<Match> {
private matchesSubject = new BehaviorSubject<Match[]>([]);
private loadingMatches = new BehaviorSubject<boolean>(false);
public loading$ = this.loadingMatches.asObservable();
constructor(private dbService: DatabaseService) {}
connect(collectionViewer: CollectionViewer): Observable<Match[]> {
return this.matchesSubject.asObservable();
}
disconnect(collectionViewer: CollectionViewer): void {
this.matchesSubject.complete();
this.loadingMatches.complete();
}
loadMatches(matchId: string, filter = '',
sortDirection='asc', pageIndex: number, pageSize: number) {
this.loadingMatches.next(true);
this.dbService.getKeyOfMatchToStartWith(pageIndex, pageSize).subscribe(keyIndex=>{
this.dbService.getMatchesFilteredPaginator(keyIndex, pageSize).pipe(
catchError(()=> of([])),
finalize(()=>{
//TODO the tutorial here https://blog.angular-university.io/angular-material-data-table/ toggled the loading spinner off here, but it seemed to work better below for me?
})
)
.subscribe(matches => {
let results = this.makeIntoArray(matches);
this.matchesSubject.next(results);
// console.log("loading done");
this.loadingMatches.next(false);
});
});
}
makeIntoArray(matches: any){
let results = []; //TODO there should be a way to tighten the below up
for(var i in matches){
let obj1 = {id:matches[i].id};
if(matches[i].matchDeets){
let obj2 = matches[i].matchDeets;
obj1 = Object.assign({}, obj1, obj2);
}
results.push(obj1);
}
// console.log(results);
return results;
}
}
行加载得很好(尽管我对缩放有些担心,因为我正在计算可观察到的总行数(为什么没有直接的方法来计算 firebase 节点中的条目??))。
但是,当我重新加载页面时,微调器永远不会消失,并且行也永远不会填充。我欢迎任何建议!
重现我的问题:
git clone https://github.com/Atticus29/dataJitsu.git
cd dataJitsu
git checkout matTableSO
在 /src/app 中创建一个 api-keys.ts 文件,并用后面的文本填充它
api-keys.ts
export var masterFirebaseConfig = {
apiKey: "AIzaSyCaYbzcG2lcWg9InMZdb10pL_3d1LBqE1A",
authDomain: "dataJitsu.firebaseapp.com",
databaseURL: "https://datajitsu.firebaseio.com",
storageBucket: "",
messagingSenderId: "495992924984"
};
export var masterStripeConfig = {
publicApiTestKey: "pk_test_NKyjLSwnMosdX0mIgQaRRHbS",
secretApiTestKey: "sk_test_6YWZDNhzfMq3UWZwdvcaOwSa",
publicApiKey: "",
secretApiKey: ""
};
然后,返回您的终端会话,键入:
npm install
ng serve
创建 BehaviorSubject 对象然后将其转换为 Observable 是一种好方法,但更改发生在与 AllMatchesComponent 不同的上下文中。因此,您不仅要订阅组件 class 中 $loading 的变化,还要更新模型值(变化检测)
您可以通过以下方式做到这一点:
1。使用 NgZone.run()
:NgZone
是一种可注入服务,用于在 Angular 区域内部或外部执行工作。通过 运行 的 运行 功能将允许您从在上下文之外执行的任务重新进入 Angular 区域。
因此,在组件和 MatchDataSource 中注入 NgZone:
import { NgZone } from '@angular/core';
constructor(..., private nz: NgZone) { }
然后,在 AllMatchesComponent 中更新数据源对象创建:
this.dataSource = new MatchDataSource(this.dbService, this.nz);
此更新服务代码为:
loadMatches(matchId: string, filter = '',
sortDirection='asc', pageIndex: number, pageSize: number) {
this.loadingMatches.next(true);
this.dbService.getKeyOfMatchToStartWith(pageIndex, pageSize).subscribe(keyIndex=>{
this.dbService.getMatchesFilteredPaginator(keyIndex, pageSize).pipe(
catchError(()=> of([])),
finalize(()=>{
//TODO
})
)
.subscribe(matches => {
let results = this.makeIntoArray(matches);
this.nz.run(() => {
this.matchesSubject.next(results);
this.loadingMatches.next(false);
});
});
});
}
我在您的代码中唯一更新的是,在订阅中我用 NgZone.run() 包含了 .next() 调用。无需其他更改,异步管道应按预期工作
可以参考Github Repo example。检查 AllMatchesComponent
& MatchDataSource
2。在另一种方法中,您可以跳过 async
管道的使用 & 只需订阅 datasource.$loading
,然后使用 ChangeDetectorRef
.
更新模型 var 中的更改
import { ChangeDetectorRef } from '@angular/core';
constructor(..., private cdr: ChangeDetectorRef ) { }
ngOnInit() {
//Keep other code as it is
// Uncomment loading$ subscribe & update it as below
this.dataSource.loading$.subscribe(result =>{
this.showLoader = result;
this.cdr.detectChanges();
});
}
不更改 MatchDataSource 服务。将加载程序模板代码更新为:
<div class="spinner-container" *ngIf="showLoader">
<mat-spinner id="spinner"></mat-spinner>
</div>
然后这将按预期工作并更新更改。
我有一个 mat-table 从 firebase 加载数据。
全部-matches.component.html
...
<mat-table #table [dataSource]="dataSource" class="mat-elevation-z8">
...
<ng-container matColumnDef="rank">
<mat-header-cell *matHeaderCellDef> Rank </mat-header-cell>
<mat-cell *matCellDef="let entry"> {{entry.rank}} </mat-cell>
</ng-container>
<ng-container matColumnDef="weightClass">
<mat-header-cell *matHeaderCellDef> Weight Class </mat-header-cell>
<mat-cell *matCellDef="let entry"> {{entry.weightClass}} </mat-cell>
</ng-container>
...
<mat-header-row *matHeaderRowDef="columnsToDisplay"></mat-header-row>
<mat-row *matRowDef="let row; columns: columnsToDisplay;"></mat-row>
</mat-table>
...
根据在线建议(我还没有完全理解),我选择使用数据源对象来填充我的 table。 dataSource 在 all-matches.component.ts:
中被实例化全部-matches.component.ts
...
@Component({
selector: 'app-all-matches',
templateUrl: './all-matches.component.html',
styleUrls: ['./all-matches.component.scss']
})
export class AllMatchesComponent implements OnInit, OnDestroy, AfterViewInit {
private columnsToDisplay = ['rank','weightClass', 'ageClass','athlete1Name', 'athlete2Name', 'gender','tournamentName','location', 'date', 'matchRating', 'videoUrl']; //TODO make this dynamic somehow
private loading = true;
...
@ViewChild(MatPaginator) paginator: MatPaginator;
constructor(private authService: AuthorizationService, private d3Service: D3Service, private dbService: DatabaseService, private textTransformationService: TextTransformationService, private dataSource: MatchDataSource) { }
ngOnInit() {
...
this.pageSize = 2; //TODO increase me to something reasonable
this.dataSource = new MatchDataSource(this.dbService);
this.dataSource.loadMatches('test', '', '', 0, this.pageSize);
this.dbService.getMatchCount().subscribe(results=>{
this.matchCount = results;
});
...
}
MatchDataSource.model.ts
import {CollectionViewer, DataSource} from "@angular/cdk/collections";
import { BehaviorSubject , Observable , of } from 'rxjs';
import { catchError, finalize } from 'rxjs/operators';
import { Match } from './match.model';
import { DatabaseService } from './database.service';
import { Injectable } from '@angular/core';
@Injectable()
export class MatchDataSource implements DataSource<Match> {
private matchesSubject = new BehaviorSubject<Match[]>([]);
private loadingMatches = new BehaviorSubject<boolean>(false);
public loading$ = this.loadingMatches.asObservable();
constructor(private dbService: DatabaseService) {}
connect(collectionViewer: CollectionViewer): Observable<Match[]> {
return this.matchesSubject.asObservable();
}
disconnect(collectionViewer: CollectionViewer): void {
this.matchesSubject.complete();
this.loadingMatches.complete();
}
loadMatches(matchId: string, filter = '',
sortDirection='asc', pageIndex: number, pageSize: number) {
this.loadingMatches.next(true);
this.dbService.getKeyOfMatchToStartWith(pageIndex, pageSize).subscribe(keyIndex=>{
this.dbService.getMatchesFilteredPaginator(keyIndex, pageSize).pipe(
catchError(()=> of([])),
finalize(()=>{
//TODO the tutorial here https://blog.angular-university.io/angular-material-data-table/ toggled the loading spinner off here, but it seemed to work better below for me?
})
)
.subscribe(matches => {
let results = this.makeIntoArray(matches);
this.matchesSubject.next(results);
// console.log("loading done");
this.loadingMatches.next(false);
});
});
}
makeIntoArray(matches: any){
let results = []; //TODO there should be a way to tighten the below up
for(var i in matches){
let obj1 = {id:matches[i].id};
if(matches[i].matchDeets){
let obj2 = matches[i].matchDeets;
obj1 = Object.assign({}, obj1, obj2);
}
results.push(obj1);
}
// console.log(results);
return results;
}
}
行加载得很好(尽管我对缩放有些担心,因为我正在计算可观察到的总行数(为什么没有直接的方法来计算 firebase 节点中的条目??))。
但是,当我重新加载页面时,微调器永远不会消失,并且行也永远不会填充。我欢迎任何建议!
重现我的问题:
git clone https://github.com/Atticus29/dataJitsu.git
cd dataJitsu
git checkout matTableSO
在 /src/app 中创建一个 api-keys.ts 文件,并用后面的文本填充它
api-keys.ts
export var masterFirebaseConfig = {
apiKey: "AIzaSyCaYbzcG2lcWg9InMZdb10pL_3d1LBqE1A",
authDomain: "dataJitsu.firebaseapp.com",
databaseURL: "https://datajitsu.firebaseio.com",
storageBucket: "",
messagingSenderId: "495992924984"
};
export var masterStripeConfig = {
publicApiTestKey: "pk_test_NKyjLSwnMosdX0mIgQaRRHbS",
secretApiTestKey: "sk_test_6YWZDNhzfMq3UWZwdvcaOwSa",
publicApiKey: "",
secretApiKey: ""
};
然后,返回您的终端会话,键入:
npm install
ng serve
创建 BehaviorSubject 对象然后将其转换为 Observable 是一种好方法,但更改发生在与 AllMatchesComponent 不同的上下文中。因此,您不仅要订阅组件 class 中 $loading 的变化,还要更新模型值(变化检测) 您可以通过以下方式做到这一点:
1。使用 NgZone.run()
:NgZone
是一种可注入服务,用于在 Angular 区域内部或外部执行工作。通过 运行 的 运行 功能将允许您从在上下文之外执行的任务重新进入 Angular 区域。
因此,在组件和 MatchDataSource 中注入 NgZone:
import { NgZone } from '@angular/core';
constructor(..., private nz: NgZone) { }
然后,在 AllMatchesComponent 中更新数据源对象创建:
this.dataSource = new MatchDataSource(this.dbService, this.nz);
此更新服务代码为:
loadMatches(matchId: string, filter = '',
sortDirection='asc', pageIndex: number, pageSize: number) {
this.loadingMatches.next(true);
this.dbService.getKeyOfMatchToStartWith(pageIndex, pageSize).subscribe(keyIndex=>{
this.dbService.getMatchesFilteredPaginator(keyIndex, pageSize).pipe(
catchError(()=> of([])),
finalize(()=>{
//TODO
})
)
.subscribe(matches => {
let results = this.makeIntoArray(matches);
this.nz.run(() => {
this.matchesSubject.next(results);
this.loadingMatches.next(false);
});
});
});
}
我在您的代码中唯一更新的是,在订阅中我用 NgZone.run() 包含了 .next() 调用。无需其他更改,异步管道应按预期工作
可以参考Github Repo example。检查 AllMatchesComponent
& MatchDataSource
2。在另一种方法中,您可以跳过 async
管道的使用 & 只需订阅 datasource.$loading
,然后使用 ChangeDetectorRef
.
import { ChangeDetectorRef } from '@angular/core';
constructor(..., private cdr: ChangeDetectorRef ) { }
ngOnInit() {
//Keep other code as it is
// Uncomment loading$ subscribe & update it as below
this.dataSource.loading$.subscribe(result =>{
this.showLoader = result;
this.cdr.detectChanges();
});
}
不更改 MatchDataSource 服务。将加载程序模板代码更新为:
<div class="spinner-container" *ngIf="showLoader">
<mat-spinner id="spinner"></mat-spinner>
</div>
然后这将按预期工作并更新更改。