2

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

Anna Melashkina
  • 422
  • 2
  • 13
  • https://stackoverflow.com/questions/49621281/azure-table-storage-pagination-for-request-10-items-each-time is it helpful for you? – Developer Jan 19 '21 at 11:13

2 Answers2

2

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.

rickvdbosch
  • 14,105
  • 2
  • 40
  • 53
2

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();
  }
}
Anna Melashkina
  • 422
  • 2
  • 13