I am having some issues with a data table - the background is that I need to make multiple simultaneous API calls through an observable subscription which are then combined into a single data structure. I need to pass this data through into the Data Table which I am successful in doing so however, once the data is loaded, my ViewChild for MatPaginator and MatSort are not detecting a change so the values of my paginator and sort function as if no data exists i.e. 0 values in page.
Is there something I need to do in outside of the norm here since my data load is still executing after ngAfterViewInit has completed.
Some things I have tried:
- Adding a
ChangeDetectorRefand RunningChangeDetectorRef.DetectChanges()to no avail - Handling the data so that a function runs post completion of the API Calls
- Using an NgZone
Hopefully this will make more sense with my code (I've tried to keep it short by removing functions, but multiple functions are writing values to the dataSource array (this.dataHashArray)
data-table.html
<div class="global-container-padding">
<mat-form-field>
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Search Shares">
</mat-form-field>
<div class="data-container">
<table mat-table [dataSource]="dataSource" matSort class="share-table mat-elevation-z8">
<!-- Checkbox Column -->
<ng-container matColumnDef="select">
<th mat-header-cell *matHeaderCellDef>
<mat-checkbox (change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()">
</mat-checkbox>
</th>
<td mat-cell *matCellDef="let row">
<mat-checkbox (click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row) : null"
[checked]="selection.isSelected(row)">
</mat-checkbox>
</td>
</ng-container>
<!-- SVMName Column -->
<ng-container matColumnDef="Name">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Name </th>
<td mat-cell *matCellDef="let row"> {{row.Name}} </td>
</ng-container>
<!-- shareName Column -->
<ng-container matColumnDef="shareName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Share Name </th>
<td mat-cell *matCellDef="let row"> {{row.shareName}} </td>
</ng-container>
<!-- storagePath Column -->
<ng-container matColumnDef="storagePath">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Storage Path </th>
<td mat-cell *matCellDef="let row"> {{row.storagePath}} </td>
</ng-container>
<!-- Protocol Column -->
<ng-container matColumnDef="Protocol">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Protocol </th>
<td mat-cell *matCellDef="let row"> {{row.Protocol}} </td>
</ng-container>
<!-- exists Column -->
<ng-container matColumnDef="exists">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Exists </th>
<td mat-cell *matCellDef="let row"> {{row.exists}} </td>
</ng-container>
<ng-container matColumnDef="HostId">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Host ID </th>
<td mat-cell *matCellDef="let row"> {{row.HostId}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="selection.toggle(row)"></tr>
</table>
<mat-paginator [length]="length"
[pageSize]="pageSize"
[pageSizeOptions]="pageSizeOptions">
</mat-paginator>
</div>
</div>
data-table.ts
import {AfterViewInit, Component, Injectable, NgZone, OnInit, ViewChild} from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';
import { MatPaginator, MatSort } from '@angular/material';
@Injectable()
export class DataComponent implements OnInit, AfterViewInit {
@ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;
@ViewChild(MatSort, {static: true}) sort: MatSort;
private length = 0;
private pageSize = 25;
private pageSizeOptions: number[] = [5, 10, 25, 100];
constructor(
private ShareService: ShareService,
private _snackBar: MatSnackBar,
private ngZone: NgZone
) {
this.dataHashArray = [];
}
public ngOnInit(): void {
this.displayedColumns = ['select', 'Name', 'shareName', 'storagePath', 'Protocol', 'exists', 'HostId'];
if(this.dataHashArray == null) {
this.dataSource = new MatTableDataSource<DataHash>([]);
} else {
this.dataSource = new MatTableDataSource<DataHash>(this.dataHashArray);
}
for(let nx = 0; nx < this.store.length; nx++) {
if(this.store[nx]['type'] == 'nas') {
this.loadApp(this.store[nx]);
}
}
}
public ngAfterViewInit(): void {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
this.ngZone.run(() => {
this.ShareService.getShares(this.Token, '').subscribe( shares => {
this.DataHashBuild(shares, this.Token, HostID);
});
});
public DataHashBuild(Shares: string, Token: string | null, HostId: string | null) {
this.ShareService.getVserverList().subscribe(vServer => {
for (let x = 0; x < vServer.length; x++) {
this.ShareService.getShares(vServer[x]).subscribe(nShares => {
for (let i = 0; i < nShares.length; i++) {
let exists: boolean = false;
for (let j = 0; j < Shares.length; j++) {
if (Shares[j]['hostname'] == `${vServer[x]}_cifs` &&
Shares[j]['exportPoint'] == nShares[i]['share-name']) {
exists = true;
let paramStr = `?host_id=${Shares[j]['hostId']}&share_id=${Shares[j]['id']}`;
this.ShareService.getShare(Token, paramStr).subscribe(getShare => {
console.log(getShare);
});
}
}
this.dataHashArray.push({
SVMName: vServer[x],
shareName: `\\\\${nShares[i]['cifs-server']}\\${nShares[i]['share-name']}`,
storagePath: `/${nShares[i]['volume']}`,
Protocol: 'SMB',
exists: exists,
HostId: HostId
});
this.dataSource = new MatTableDataSource<DataHash>(this.dataHashArray);
this.renewDataSource();
}
});
}
});
}
public renewDataSource() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
ShareService.ts
public getVserverList(): Observable<vServerInterface>{
return this.http.post(url, data, {headers: headers, responseType: 'text'})
.pipe(map(message => {
xmlObj = this.parseXml(message);
for (let i = 0; i < xmlObj.getElementsByTagName('vserver-name').length; i++) {
outputArray.push(xmlObj.getElementsByTagName('vserver-name')[i].textContent);
}
return outputArray;
}));
}
The API Calls made via subscriptions are simply HTTPClient GET requests which are Observable and return the http call.