We will add some more advanced behaviours

Code on github https://github.com/kendarorg/Angular101

The Material Data Source

Let's start adding a new method to the service to handle the DataSource. The data source is nice because it handles all the pagination, ordering, filtering and sorting. For our example we will handle only the pagination, that was implemented via http headers on the server.

First we need to import globally the paginator on app.module.ts

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

And load it in the imports

  imports: [
    ...
    MatPaginatorModule,
  ],

Modify the service

We need to import the HttpHeaders and rxjs. The latter is needed to map the result from the request to a consistent object

import { HttpClient, HttpHeaders } from '@angular/common/http';
import { map } from 'rxjs/operators'; 

We add a new type to return, containing the data for the pagination (this could be placed in a common part since its "type agnostic"

export interface AddressResult {
    body: Object;
    pageCount: number;
    totalCount: number;
    pageIndex:number;
}

We will not use sorting and filter. Just the pagination. We are reading the data from the API that tells us the specification of the data, like the total count and the page index returned

public findAddresses(
    addressId:number = -1, filter = '', sortOrder = 'asc',
    pageNumber = 0, pageSize = 3):Observable<AddressResult> {

    var headers = new HttpHeaders();

    var address = this.baseUrl;
    if(addressId>=0){
        address+="/"+addressId;
    }else{
        headers = headers.set('X-Page',pageNumber.toString());
        headers = headers.set('X-PageSize',pageSize.toString());
    }

    return this.http.get(address, {headers:headers, observe: "response"}).pipe(
        map(res =>  {
            var pageCount =parseInt(res.headers.get('X-PageCount'));
            var pageIndex =parseInt(res.headers.get('X-PageIndex'));
            var totalCount =parseInt(res.headers.get('X-Count'));
            return {
                body:res.body as AddressElement[],
                pageCount:pageCount,
                totalCount:totalCount,
                pageIndex:pageIndex} as AddressResult;
        })
    );
}

Changes in the controller

We need to import

We setup a new variable for the paginator. The annotation tells angular to initialize the item only when everything is ready

@ViewChild(MatPaginator, { static: false }) 
paginator: MatPaginator;

Then setup the data source and all the paginator variables

dataSource = new MatTableDataSource();
length: number;
pageIndex: number =0;
pageSize: number=3;
pageSizeOptions = [1, 5, 10, 50];

And an event. That will be monitored and will update the paginator

pageEvent: PageEvent;

Let's rewrite the getAddresses. It receives an event from paginator. If nothing is passed an empty object casted as an event is created filling the blanks with the default values.

Then a subscription to findAddresses is created that updates the values shown.

Finally the event is returned. Prettu straightforward for now. Notice the cast to Array of AddressElement of the AddressResult

getAddresses(event?:PageEvent){
    if(event==null){
        event ={pageIndex:0,pageSize:this.pageSize} as PageEvent;
    }

    this.dataService.findAddresses(-1,'','asc',event.pageIndex,event.pageSize).subscribe(
            addresses =>{

                var a = addresses as AddressResult;
                this.addresses=a.body as Array<AddressElement>;
                this.dataSource.data = this.addresses;
                this.length=a.totalCount;
                this.pageIndex = a.pageIndex;
            },
            error => {
                console.log("error retrieving addresses");
            }
        );
    return event;
}

The onInit will be greatly simplified. Just call getAddresses

ngOnInit(): void {
    this.getAddresses(null);
}


dataSource: AddressesDs;
constructor(public dataService: AddressesDataService) {}

ngOnInit(): void {
    this.dataSource = new AddressesDs(this.dataService);
    this.dataSource.loadAddresses(-1);
}

The html presentation

The dataSource will be changed to dataSource

<mat-table [dataSource]="dataSource" class="mat-elevation-z1" >

We will add after the mat-table the following. #paginator tells Angular that a variable exists inside the controller with that value. The most interesting thing is the (page). This means that when the page is changed on the paginator an event is intercepted and sent to the controller!

<mat-paginator  #paginator
                [length]="length"
                [pageSize]="pageSize"
                [pageIndex]="pageIndex"
                [pageSizeOptions]="pageSizeOptions"
                (page)="pageEvent = getAddresses($event)">
</mat-paginator>

The controller will then call the service to get the data and it will be shown :D


Last modified on: February 17, 2020