Angular Datatable with Pagination Using ag-Grid and REST API
Tags: angular, java, javascript
Published: 2020-12-29 13:25:54

In this article, I’ll explain how we can build a data table with angular using [ag-Grid](https://www.ag-grid.com/). 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](https://javatodev.com/spring-boot-pagination-sorting-and-filtering/).

Final Development Outcome

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/zoqe2d1uwun8szpsqt9m.gif)

Technologies I’m going to use in Frontend,

* Angular 10.1.5
* ag-Grid
* Angular HttpClient

Main topics inside the article,

* Up and Running Backend REST API
* Developing Angular JS Project
 * Adding ag-Grid into the Initiated Project
 * API Service to Consume REST API
 * Component To Show ag-Grid Implementation
  * Setting Author Name With Custom String Parser
  * Showing Image On ag-Grid
* Conclusion

## 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.

```shell
$ git clone https://github.com/javatodev/spring-boot-mysql-pagination-filtering-sorting.git
```
After downloading the project, change the **src/main/resources/application.properties** to connect with the preferred MySQL instance on your side.

```shell
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,

```shell
$ ./gradlew bootRun
```

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

```curl
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 database.

```json
{
  "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.

```curl
curl -X 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 the following [Fake REST API](https://instantwebtools.net/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 which returns the 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 JS Project

If you don’t have the basic setup to develop angular JS project, Just follow this [documentation](https://cli.angular.io/) 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 which necessary to have in routing enabled angular application.

```shell
$ 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 project with routing module integrations.

### Adding ag-Grid into the Initiated Project

```shell
$ 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)

```javascript
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,

```css
@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.

```shell
$ 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.

```javascript
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

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

```shell
$ ng g c Dashboard
```

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

```javascript
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,

```html
<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 into 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 need to set columnDefs with correct field name totally with whatever the data is coming from our API.

Then all the data coming from API will be bound 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 retrieval data will be set to the successCallback.

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

```javascript
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.

```html
<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,

```shell
$ ng serve --open
```

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

![Datatable with Angular Ag-grid and Spring Boot REST API](https://dev-to-uploads.s3.amazonaws.com/i/7v0f6gtyj1o93qqnwiwf.png)

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

#### Setting Author Name With Custom String Parser

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

```JSON
{
      "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 following method to src/app/dashboard/dashboard.component.ts

```javascript
nameParser(params:any) {
    if (params.data != null) {
      return params.data.author.firstName+" "+params.data.author.lastName;
    }
    return "";
  }
```
Then change following column definition on columnDefs,

```javascript
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](https://dev-to-uploads.s3.amazonaws.com/i/94rvbp5l0wm7feiy5xmd.png)

#### 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 <img> 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.

```javascript
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 <img> with the value we set from the API.

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

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

Then call the custom component using cellRendererFramework as below,

```javascript
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](https://dev-to-uploads.s3.amazonaws.com/i/giub39fn0daucddl82dh.png)

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 js 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](https://github.com/javatodev/angular-datatable-with-spring-boot-api).