Angular Datatable with Pagination Using ag-Grid and REST API

Angular Datatable with Pagination Using ag-Grid and REST API

In this article, I’ll explain how we can build a data table with angular using ag-Grid. Additionally, the application will consume third-party paginated REST API and load the data to the table.

Here I’m using the API which we developed for our article on, Spring Boot Pagination, Sorting and Filtering.

Angular Datatable with Pagination Using ag-Grid

Angular Datatable with Pagination Using ag-Grid

Technologies I’m going to use in Frontend,

  • Angular 10.1.5
  • ag-Grid
  • Angular HttpClient

topics that we are going to cover,

Up and Running Backend REST API

Here as I mentioned above, I’ll use the API we have developed in our previous tutorial, First, download the source codes for that Spring boot REST API from here.

https://github.com/javatodev/spring-boot-mysql-pagination-filtering-sorting.git

If you are really new to Spring Boot, You can get an idea from our article on How to Create a Spring Boot Project.

After downloading the project, change the src/main/resources/application.properties to connect with the preferred MySQL instance on your side.

server.port=8081
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/java_to_dev_api_mysql
spring.datasource.username=root
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

Change these properties accordingly to access the mysql instance on your machine.

Then start the application using the following command,

$ ./gradlew bootRun

Then you can validate the API status just by accessing the following URL from your browser or using CURL in the command line,

http://localhost:8081/api/library/book/search?page=0&size=2

It should return an empty list or list of books along with a number of items and number of pages if data is available on your database, after correctly up and running.

I’ve added simple API endpoint to write dummy data set on the database

{
  "bookList": [
    {
      "id": 2,
      "name": "Unlocking Android",
      "isbn": "1933988673",
      "imageUrl": "https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ.book-thumb-images/ableson.jpg",
      "author": {
        "id": 3,
        "firstName": "W. Frank",
        "lastName": "Ableson"
      }
    },
    {
      "id": 3,
      "name": "Android in Action, Second Edition",
      "isbn": "1935182722",
      "imageUrl": "https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ.book-thumb-images/ableson2.jpg",
      "author": {
        "id": 3,
        "firstName": "W. Frank",
        "lastName": "Ableson"
      }
    }
  ],
  "numberOfItems": 400,
  "numberOfPages": 200
}

If you need more data to test this API, Just trigger following API, then it will create some sample data set in the database. Add an author to the DB before running this API.

POST http://localhost:8081/api/library/create

Now we have a running API which works correctly.

If you are not familiar with Spring Boot based application then you can use following Fake REST API from instantwebtools.net which is online and free to use with this tutorial. One thing will be changed since that API returns different data structure but you can set up that with this application as well.

They have this API endpoint that returns a paginated response from their API and it has all the parameters which we need to have in this tutorial.

https://api.instantwebtools.net/v1/passenger?page=0&size=10

Developing Angular Project

If you don’t have the basic setup to develop angular project, Just follow this documentation to install and configure Angular CLI before starting this tutorial.

Here I’m using angular CLI (10.1.5) to generate our base project for this tutorial.

First, generate your project using the following command and add –routing=true to the same command, then it will generate our base application with all the components necessary to have in routing enabled angular application.

$ ng new angular-js-datatable-with-spring-boot-api --routing=true

Then select the option you like in the next stages after the above command, For my sources, I’m using the following for the options, But you are free to use any option you would like.

  • Which style sheet format would you like to use? – CSS

Ok, Now we have our fresh angular 10 projects with routing module integrations.

Adding ag-Grid into the Initiated Project

$ npm install --save ag-grid-community ag-grid-angular

now all the modules related to ag-grid should be added into the project. Then let’s add the ag-Grid Angular module to our app module (src/app/app.module.ts)

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { AgGridModule } from 'ag-grid-angular';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent,  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    AgGridModule.withComponents([]),
    NgbModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Additionally, I’m configuring HttpClientModule with this application since It will be used to communicate with the REST API.

After that, add the following CSS imports to src/styles.css or styles.scss get ag-Grid themes,

@import "../node_modules/ag-grid-community/dist/styles/ag-grid.css";
@import "../node_modules/ag-grid-community/dist/styles/ag-theme-alpine.css";

Now we are ready to go with ag-grid implementation.

API Service to Consume REST API

Here we are using a separate service class to communicate with REST API. create a new service using following command.

$ ng g s api

Then add following content into the src/app/api.service.ts Here I’m developing a service method to accept pagesize and pagenumber then retrieve paginated API response from the API using those parameters.

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ApiService {

  constructor(private client: HttpClient) {}

  getAllBooks(pageSize: Number, pageNumber: Number): Observable<any> {
    const url = "http://localhost:8081/api/library/book/search?size="+pageSize+"&page="+pageNumber;
    return this.client.get(url);
  }
  
}

Component To Show ag-Grid Implementation

Here we are using a separate component to build ag-Grid view. So first create a new component and add router param to show it with the root URL.

$ ng g c Dashboard

Then add following into the src/app/app.routing.module.ts in order to setup routes.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { DashboardComponent } from './dashboard/dashboard.component';

const routes: Routes = [
 {
   path: "",
   component: DashboardComponent
 }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Then remove all the content from src/app/app.component.html, and add following,

<router-outlet></router-outlet>

Done now we have implemented a new component with routing, Then we should focus on adding data table component.

First add following in to the src/app/dashboard/dashboard.component.ts,

for the moment I’m only going to set two columns in the datatable with name and isbn from the book API.

so basically you needs to set columnDefs with correct field name to tally with whatever the data is coming from our API.

Then all the data coming from API will be binded to rowData array and setting rowModelType to ‘infinite’ and default page size will be 10.

onGridReady method will be called when the grid is loaded and it will set the data source with API service and the params will be captured with gridApi.paginationGetPageSize() and gridApi.paginationGetCurrentPage() and its available around whole application for the current session.

After successful retireval data will be set to the successCallback.

Additionally, onPageSizeChanged will be used to set changes on page size.

import { Component, OnInit } from '@angular/core';
import { IDatasource, IGetRowsParams } from 'ag-grid-community';
import { ApiService } from '../api.service';

@Component({
  selector: 'app-new-dashboard',
  templateUrl: './new-dashboard.component.html',
  styleUrls: ['./new-dashboard.component.css']
})
export class NewDashboardComponent implements OnInit {

  private gridApi: any;
  private gridColumnApi: any;  

  constructor(private api: ApiService) { }

  columnDefs = [
    { field: 'name', sortable: true, filter: true , flex: 1, minWidth: 100},
    { field: 'isbn', sortable: true, filter: true , flex: 1, minWidth: 100}
  ];

  rowData = [];
  rowModelType = 'infinite';
  defaultPageSize = 10;

  ngOnInit(): void {
  }

  onGridReady(params: any) {
    this.gridApi = params.api;
    this.gridColumnApi = params.columnApi;
    this.gridApi.setDatasource(this.dataSource);
  }

  dataSource: IDatasource = {
    getRows: (params: IGetRowsParams) => {      
      this.api.getAllBooks(this.gridApi.paginationGetPageSize(), this.gridApi.paginationGetCurrentPage()).subscribe(response => {
        params.successCallback(
          response.bookList, response.numberOfItems
        );
      })
    }
  }

  onPageSizeChanged(event: any) {
    this.gridApi.paginationSetPageSize(Number(event.target.value));
  }

}

Now add following into the src/app/dashboard/dashboard.component.html, this is the UI part for our datatable and there are pending changes that we need to do in the typescript side.

<div style="padding-right:100px;padding-left:100px; padding-top:20px">
    <h1 style="font-weight: bold;">Datatable with Ag-Grid + Angular With Spring Boot REST API</h1>
<div>
    Page Size:
    <select (change)="onPageSizeChanged($event)">
        <option value="10">10</option>
        <option value="100">100</option>
        <option value="500">500</option>
        <option value="1000">1000</option>
    </select>
</div>
<ag-grid-angular 
    #agGrid style="width: 100%; height: 550px;" 
    class="ag-theme-alpine" 
    [rowData]="rowData" 
    id="myGrid"
    [columnDefs]="columnDefs" 
    [pagination]="true" 
    (gridReady)="onGridReady($event)" 
    [rowModelType]="rowModelType"
    [paginationPageSize]="defaultPageSize" 
    [cacheBlockSize]="defaultPageSize" 
    [enableRangeSelection]="true"
>
</ag-grid-angular>
</div>

with ag-grid we need to custom develop page size selection component. That’s why I’ve developed select with onPageSizeChanged method to set selected page size by the user.

Then start the project with following command,

$ ng serve --open

Then following UI should be present when accessing http://localhost:4200 on your browser.

Datatable with Angular Ag-grid and Spring Boot REST API

Datatable with Angular Ag-grid and Spring Boot REST API

All done with the basic implementation. let’s add a few more changes to show images and custom string columns.

Setting Author Name With Custom String Parser

Here API is sending author’s first name and last name in two parameters.

{
      "id": 2,
      "name": "Unlocking Android",
      "isbn": "1933988673",
      "imageUrl": "https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ.book-thumb-images/ableson.jpg",
      "author": {
        "id": 3,
        "firstName": "W. Frank",
        "lastName": "Ableson"
      }
    }

So If we need to show both params in a single column in ag-grid, We can use valueGetter and set our custom parser to the getter. Then it will set parsed author name on that column.

Add the following method to src/app/dashboard/dashboard.component.ts

nameParser(params:any) {
    if (params.data != null) {
      return params.data.author.firstName+" "+params.data.author.lastName;
    }
    return "";
  }

Then change following column definition on columnDefs,

columnDefs = [
    { field: 'name', sortable: true, filter: true , flex: 1, minWidth: 100},
    { field: 'isbn', sortable: true, filter: true , flex: 1, minWidth: 100},
    { valueGetter: this.nameParser , flex: 1, minWidth: 100, headerName: 'Author'}
  ];

All done, now our datatable could show author name with concatenating first name and last name.

Angular ag-grid with valueGetter to custom colomn implementation

Angular ag-grid with valueGetter to custom colomn implementation

Showing Image On ag-Grid

Now our last column, Image for book. Our API sends the direct URL to the image. So we just needs to set tag with the URL coming from our API.

So to do that we should use custom component and load it with the datatable.

Let’s create another component (src/app/ImageFormatterComponent.ts) with adding the following content.

import { Component } from "@angular/core";

@Component({
  selector: 'app-image-formatter-cell',
  template: `<img border="0" width="50" height="50" src=\"{{ params.value }}\">` })

export class ImageFormatterComponent {
  params: any;
  agInit(params: any){
    this.params = params; 
  } 
}

Here it’s creating a with the value we set from the API.

Then add this same component to the AgGridModule implementation on src/app/app.module.ts

imports: [
    BrowserModule,
    AppRoutingModule,
    AgGridModule.withComponents([ImageFormatterComponent]),
    NgbModule,
    HttpClientModule
  ]

Then call the custom component using cellRendererFramework as below,

columnDefs = [
    { field: 'name', sortable: true, filter: true , flex: 1, minWidth: 100},
    { field: 'isbn', sortable: true, filter: true , flex: 1, minWidth: 100},
    { valueGetter: this.nameParser , flex: 1, minWidth: 100, headerName: 'Author'},
    { field: 'imageUrl' , autoHeight: true, flex: 1, minWidth: 100, headerName: 'Image', cellRendererFramework: ImageFormatterComponent}
  ];

Now our application is almost complete with all the necessary column definitions.

showing images on angular ag grid datatable

showing images on angular ag grid datatable

All done, Now we have completed the whole implementation with ag-grid on an angular project using REST API.

Conclusion

All done, Now I hope you have a good understanding of how to develop an angular frontend with datatable using ag-grid and how to configure it to consume paginated REST API developed using Spring Boot. Comment on your ideas or issues you are facing while your development. I’m eagerly waiting to answer those.

You can find source codes for this tutorial from our Github.