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.
Final Development Outcome
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.
$ 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.
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 accessing 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 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.
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 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 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.
$ 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
$ 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
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.
$ 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 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.
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.
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.
{
"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
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.
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.
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.
Top comments (0)