Angular MatPaginator and Azure Table Storage

Viewed 201

I'm trying to implement an Angular material table with pagination, that is connected to the backend, that retrieves data from Azure Table Storage.

I know, that Table Storage supports ExecuteQuerySegmentedAsync, which returns TableContinuationToken. It looks fine. So on the frontend, I get something like this:

interface IPagedResult<T> {
    items: T[];
    isFinalPage: boolean;
    continuationToken: string;
}

interface ILog {
    enqueuedDate: string;
    ...
}

Somewhere in component.ts:


private logsTableSource: MatTableDataSource<ILog>;
@ViewChild(MatPaginator)paginator: MatPaginator;

ngAfterViewInit() {
   myService.GetRecords(this.paginator.pageSize)
            .subscribe(
               (res: IPagedResult<ILog>) => {
                    this.logsTableSource = new MatTableDataSource<ILog>(res.items);
               });
}

Now I'm wondering, how to get the number of pages? And let the server know what specific page I want?

continuationToken looks like this: enter image description here

In fact, what I can do with this continuationToken?

For better understanding this is how the table looks like: enter image description here

2 Answers

The TableContinuationToken documentation you linked to also states:

A method that may return a partial set of results via a TableResultSegment object also returns a continuation token, which can be used in a subsequent call to return the next set of available results.

This means the token can be used to get the next set of available results, you cannot use them as a pagination index. There's no way of crafting a TableContinuationToken for page 7 of the results.

As @rickvdbosch said, TableContinuationToken expects only moving forward. After some changes in the paginator I can move only forward and backward. Looks quite good and works for me: enter image description here

If someone is interested. Here are the changes:

  1. Implement your own MatPaginatorIntl to remove pages label. My looks like this:
@Injectable()
export class LogsPaginator extends MatPaginatorIntl {
    public getRangeLabel = function (page: number, pageSize: number, length: number) {
        return '';
    };
}
  1. Cache items, that you have previously loaded since we can move ONLY forward with TableContinuationToken. Your component.ts should look something like this:
export class LogsComponent {
  // As table storage does not support paging per index, we should cache already loaded logs and use continuation token if needed.

  private cachedLogs: ILog[] = [];
  private cachedIndexes: number[] = [];
  private continuationToken = '';

  ngOnInit() {
    this.paginator.page.subscribe(this.pageChanged.bind(this));
  }

  async ngAfterViewInit() {
    await this.loadLogs();
  }

  private async pageChanged(event: PageEvent) {
    if (event.previousPageIndex < event.pageIndex && this.cachedIndexes.indexOf(event.pageIndex) === -1) {
      await this.loadLogs();
    } else {
      this.redrawTable();
    }
  }

  private redrawTable() {
    const start = this.paginator.pageIndex * this.paginator.pageSize;
    const end = start + this.paginator.pageSize;
    this.logsTableSource.data = this.cachedLogs.slice(start, end);
  }


  private async loadLogs() {
      const res = await this.myService.GetLogs(this.paginator.pageSize, this.continuationToken).toPromise();
      this.cachedIndexes.push(this.paginator.pageIndex);
      this.cachedLogs.push(...res.items);
      this.paginator.length = res.isFinalPage ? this.cachedLogs.length : this.cachedLogs.length + 1;
      this.continuationToken = res.continuationToken;

      this.redrawTable();
  }
}
Related