Menu

Using OData paginated payload with ngx-bootstrap Pagination component

I have recently started playing around with OData, and it really easy to get things up and running with an existing database. All my frontend code is in Angular and I use ngx-bootstrap for all bootstrap components.

All the OData endpoints have stranded responses, I don’t want to change it. Now I need to do my Angular implementation to support OData stranded.

export interface IODataResponse<T> {
    '@odata.context': string;
    '@odata.nextLink': string;
    '@odata.count': number;
    value: T;
}

my service class method looks like this

export class BaseService {
  constructor(private http: HttpClient) {}

  getPaged<T>(path: string, page: number, itemsPerPage: number, columns:string[], expand?:string[], filterParams?: HttpParams): Observable<T> {    
    let params = new HttpParams()    
        .set('$select', columns.toString())  
        .set('$count', true)
        .set('$top', itemsPerPage)
        .set('$skip', page > 1 ? (page -1) * itemsPerPage : 0);

        if(expand != null)
          params = params.append('$expand', expand.toString());

        if(filterParams != null)
          params = this.mergeParams(params, filterParams);    

    return this.http
              .get<T>(`${environment.api_url}${path}`, { params })
              .pipe(catchError(this.formatErrors));
  }

  private mergeParams(params: HttpParams, _params: HttpParams): HttpParams{    
     const items: any = _params;
     Object.keys(items.updates).forEach(function (key) 
     {
        params = params.append(items.updates[key].param, items.updates[key].value);
     });

     return params;
  }
}

I can use the service class method to get data like below

  getPaged<T>(page: number, itemsPerPage: number, columns:string[], searchTerm?: string): Observable<IODataResponse<T[]>> { 
    const filterParams = new HttpParams()
                      .set('$filter', `contains(salesOrderNumber,'${searchTerm}')`);                     

    const expand = ['customer($select=companyName,firstName,lastName)' + 
                    ',billToAddress($select=addressLine1,addressLine2,city,stateProvince,countryRegion,postalCode)' +
                    ',shipToAddress($select=addressLine1,addressLine2,city,stateProvince,countryRegion,postalCode)'];

    return this.service.getPaged<IODataResponse<T[]>>(this.resource, page, itemsPerPage, columns, expand, searchTerm ? filterParams : undefined)
         .pipe(first())
         .subscribe(
        (response: IODataResponse<ISalesOrder[]>) => {
          this.orders = response.value;
          this.totalItems = ODataHelper.getTotalItems(response, this.itemsPerPage);
          this.busy = false;
        },
        (error: any) => {
          this.busy = false;
        }
      );
  }

in order to extract the count from JSON payload, had to write a helper method

export abstract class ODataHelper { 
    public static getTotalItems(response: any, itemsPerPage: number): number{
        return (Math.trunc(response['@odata.count'] / itemsPerPage) * itemsPerPage)
    }
}

Looks easy? let me know if there is a better way to do it.

Leave a comment