4

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 ChangeDetectorRef and Running ChangeDetectorRef.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.

2 Answers 2

3

try this

<mat-paginator [pageSizeOptions]="[20, 30, 50]" showFirstLastButtons></mat-paginator>

import below statement in .ts file

import {MatPaginator} from '@angular/material/paginator';

copy below line below to your class constructor

@ViewChild(MatPaginator) paginator: MatPaginator;

copy below line in your function

 this.dataSource.paginator = this.paginator;
Sign up to request clarification or add additional context in comments.

5 Comments

Hey Raj! Apologies, but all 4 lines are already present in the code. Not sure why but the top of my data-table.ts got cut off; editing now.
did you import MaterialModule, MatTreeModule modules in app.module.ts ?
Yeah, right now I'm exporting the entire materials module as a class and importing directly into app.module.ts. Just to add, I have paging and sorting working in other parts of the same project, it just seems to be an issue when Im using it with Observables (RXJS).
Import Matpaginator like this import {MatPaginator} from '@angular/material/paginator'; in the component.ts. I got the same issue.
Oh awesome, that fixes it! I would never have caught that.
2

This was resolved by changing:

import { MatPaginator } from '@angular/material';

to:

import { MatPaginator } from '@angular/material/paginator';

The same applies to Sort i.e.

import { MatSort} from '@angular/material/sort';

Thanks to @Raj for the assist.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.