When we have a large dataset and we want to present it to the user in smaller chunks, pagination and sorting is often helpful solution. So in the tutorial, I introduce how to create “SpringBoot Ajax Pagination Example” use JQuery Ajax and Bootstrap to build a table solution for pagination, filtering and sorting the fetching data with SpringBoot RestAPIs examples.
- Overview JQuery Ajax Springboot Table Pagination Filtering Sorting
- SpringBoot RestAPIs Pagination – Backend Development
- JQuery Ajax with Bootstrap Table Pagination Filtering and Sorting – Frontend Development
- Integration Testing – Jquery Ajax Bootstrap Table & SpringBoot RestAPI
- Sourcecode
- Further Reading
Overview Springboot Ajax Pagination Example with Bootstrap Table and JQuery
SpringBoot Architecture Diagram

– We create a “SpringBoot Ajax Pagination Example” project to provide pagination, filtering and sorting data from MySQL/PostgreSQL via RestAPIs with the helpful from PagingAndSorting
Repository of Spring Data JPA.
– We implement a Bootstrap Html view with Ajax scripts to fetch data from the RestAPIs with pagination, filtering and sorting functions.
Goal
– Make a request at API: /api/customers/custom/pageable
with pagination, filtering and sorting params as below:
page
: 0 – first pagesize
: 5 – size of a pagesalary
: 4000 – filtering by salary fieldagesorting
: true – sorting by agedesc
: true – descending or ascending sorting
– Result:

– JQuery Ajax Bootstrap Frontend Pagination with Filtering and Sorting table:
For doing the pagination, we use PagingAndSortingRepository
class that is an extension of CrudRepository
to provide additional methods to retrieve entities using the pagination and sorting abstraction. Here is the sourcecode of it:
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
findAll(Sort sort)
returns all entities sorted by the given options.findAll(Pageable pageable)
Returns a page of entities meeting the paging restriction provided in thePageable
object.
Here is the hierarchy of PagingAndSortingRepository
:

So JpaRepository
of Spring JPA is an alternative solution for PagingAndSortingRepository
:
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
Spring JPA Pagination
For pagination with Spring JPA, we use the methods:
Page<T> findAll(Pageable pageable);
It returns a Page
of entities meeting the paging restriction provided in the Pageable
object.
– Here is how the Page
interface is defined:

– Here is how to Pageable
interface is defined:

Examples coding:
Pageable requestedPage = PageRequest.of(0, 5);
Page<Customer> customers = customerRepository.findAll(requestedPage);
We use the PageRequest
to construct a Pageable
object then pass it to the findAll()
method of PagingAndSortingRepository
.
How to Pagining and Filtering?
For filtering data with pagination, Spring JPA provides many useful Query Creation from method names:
Slice findAllBySalary (double salary, Pageable pageable);
Page findAllByAgeGreaterThan(int age, Pageable pageable);
GreaterThan
examplesfindAllByAgeGreaterThan(int age, Pageable pageable);
means… where x.age> ?1
Is, Equals
examplesfindAllBySalary (double salary, Pageable pageable)
means… where x.salary = ?1
Paging and Sorting
For sorting with Spring data JPA, we use the function: Iterable<T> findAll(Sort sort);
.
We can use public static methods by()
to build the Sort objects:
public static Sort by(String... properties)
creates a newSort
for the given properties.public static Sort by(List≶Order> orders)
creates a newSort
for the given list Orderspublic static Sort by(Order... orders)
create a newSort
for the givenOrder
spublic static Sort by(Direction direction, String... properties)
If we want to both sort and page our data, We can do that by passing the sorting details into our PageRequest
object itself:
Pageable sortedBySalary =
PageRequest.of(0, 3, Sort.by("salary"));
Pageable sortedBySalaryDesc =
PageRequest.of(0, 3, Sort.by("salary").descending());
Pageable sortedBySalaryDescAgeAndFirstnameAsc =
PageRequest.of(0, 5, Sort.by("salary").descending().and(Sort.by("age")).and(Sort.by("firstname")));
For more details about how to builld a SpringBoot RestAPIs for pagination, filtering and sorting, please follow the link at here.
How to build Spring Boot Pagination Project
Create SpringBoot project
We create a SpringBoot project with 3 dependencies:
- Spring Web is used to handle RestAPIs
- Spring JPA is used to do pagination, filtering and sorting with database’s data
- MySQL/PostgreSQL driver is used to work with dabase
Check the pom.xml
file:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
Configure Database
Open the application.properties
to configure the working database environment:
spring.datasource.url=jdbc:mysql://localhost:3306/loizenjavadb spring.datasource.username=root spring.datasource.password=12345 spring.jpa.generate-ddl=true #drop & create table again, good for testing, comment this in production spring.jpa.hibernate.ddl-auto=create
Create JPA Data Model
We create a Customer
model class with 7 attributes: id
, firstname
, lastname
, address
, age
, salary
, copyrightBy
.

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column
private String firstname;
@Column
private String lastname;
@Column
private String address;
@Column
private int age;
@Column
private double salary;
@Column(columnDefinition = "varchar(255) default '@ https://loizenjava.com'")
private String copyrightBy;
– @Column
specifies the mapped column for a persistent property or field. If no Column annotation is specified, the default values apply.
– javax.persistence.Id
specifies the primary key of an entity.
– javax.persistence.Entity
specifies that the class is an entity. This annotation is applied to the entity class.
Define JPA Paging and Sorting Repository
Create interface CustomerRepository
extends PagingAndSortingRepository
:
package com.loizenjava.springboot.pagingansorting.repository;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import com.loizenjava.springboot.pagingansorting.model.Customer;
@Repository
public interface CustomerRepository extends PagingAndSortingRepository<Customer, Long>{
Page<Customer> findAllBySalary (double salary, Pageable pageable);
@Query("SELECT DISTINCT c.salary FROM Customer c")
List<Double> findDistinctSalary();
}
Implement SpringBoot Pagination Filtering and Sorting RestAPI
We implement a SpringBoot RestAPI at URL /api/customers/custom/pageable
to do pagination, filtering and sorting Customer with 5 params:
page
is used to get the right pagesize
is a size of a pagesalary
is used to filteringagesorting
is used to sort withage
column or notdesc
is used to define the sorting direction – descending or default with ascending sorting
Coding:
package com.loizenjava.springboot.pagingansorting.restapis;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.Param;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.loizenjava.springboot.pagingansorting.model.Customer;
import com.loizenjava.springboot.pagingansorting.model.Response;
import com.loizenjava.springboot.pagingansorting.repository.CustomerRepository;
@org.springframework.web.bind.annotation.RestController
@RequestMapping("/api/customers")
@CrossOrigin(origins = "http://localhost:4200")
public class RestController {
@Autowired
CustomerRepository customerRepository;
@GetMapping("/custom/pageable")
public Response retrieveCustomer(@Param(value = "salary") int salary,
@Param(value = "page") int page,
@Param(value = "size") int size,
@Param(value = "agesorting") boolean agesorting,
@Param(value = "desc") boolean desc){
Page<Customer> customers = null;
// not filtering with salary
if(salary < 0) {
// not sorting with age
if(agesorting == false) {
Pageable requestedPage = PageRequest.of(page, size);
customers = customerRepository.findAll(requestedPage);
}else {
// sorting with age and ascending
if(false == desc) {
Pageable requestedPage = PageRequest.of(page, size, Sort.by("age"));
customers = customerRepository.findAll(requestedPage);
}
// sorting with age and descending
else {
Pageable requestedPage = PageRequest.of(page, size,
Sort.by("age").descending());
customers = customerRepository.findAll(requestedPage);
}
}
// Filtering with salary
} else {
// not sorting with age
if(agesorting == false) {
Pageable requestedPage = PageRequest.of(page, size);
// fitering request
customers = customerRepository.findAllBySalary(salary, requestedPage);
}else {
// sorting with age and ascending
if(false == desc) {
Pageable requestedPage = PageRequest.of(page, size, Sort.by("age"));
// filtering request
customers = customerRepository.findAllBySalary(salary, requestedPage);
}
// sorting with age and descending
else {
Pageable requestedPage = PageRequest.of(page, size,
Sort.by("age").descending());
// filtering request
customers = customerRepository.findAllBySalary(salary, requestedPage);
}
}
}
Response res = new Response(customers.getContent(), customers.getTotalPages(),
customers.getNumber(), customers.getSize());
return res;
}
@GetMapping("/salaries")
public List<Double> getListSalaries() {
try {
return customerRepository.findDistinctSalary();
}catch(Exception e) {
// Log errors to user monitoring
System.out.println(e);
return Arrays.asList();
}
}
}
Implement SpringBoot Main Class
In the main class of SpringBoot project, We implement the CommandLineRunner
interface and inject the CustomerRepository
repository to save a list Customer
to database MySQL/PostgreSQL.
Coding:
package com.loizenjava.springboot.pagingansorting;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.loizenjava.springboot.pagingansorting.model.Customer;
import com.loizenjava.springboot.pagingansorting.repository.CustomerRepository;
@SpringBootApplication
public class SpringBootPagingAndSortingApplication implements CommandLineRunner{
@Autowired
CustomerRepository customerRepository;
public static void main(String[] args) {
SpringApplication.run(SpringBootPagingAndSortingApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
List<Customer> customers = Arrays.asList( new Customer("Jack", "Smith", "Massachusetts", 23, 4000)
, new Customer("Adam", "Johnson", "New York", 27, 3000)
, new Customer("Katherin", "Carter", "Washington DC", 26, 4000)
, new Customer("Jack", "London", "Nevada", 33, 4000)
, new Customer("Jason", "Bourne", "California", 36, 3000)
, new Customer("Blade", "Johnson", "Ohio", 18, 3000)
, new Customer("Carol", "Carter", "Florida", 23, 4000)
, new Customer("Avery", "Alvin", "Washington DC", 41, 3000)
, new Customer("Dana", "Bay", "Texas", 32, 4000)
, new Customer("Tom", "Bourne", "Colorado", 28, 3500)
, new Customer("Ardys", "Bean", "Alaska", 23, 4000)
, new Customer("Carol", "Carter", "Arizona", 26, 3500)
, new Customer("Avery", "Zane", "Virginia", 31, 4000)
, new Customer("Barric", "Weldin", "Oregon", 52, 3000)
, new Customer("Galen", "Wayt", "Missouri", 48, 4000)
, new Customer("Mayer", "Acomb", "Wisconsin", 32, 4000)
, new Customer("Sadie", "Albright", "Tennessee", 23, 3500)
, new Customer("Norvell", "Baily", "Oregon", 41, 4000)
, new Customer("Tanzi", "Baney", "North Dakota", 26, 4000)
, new Customer("Osric", "Callender", "New Mexico", 29, 3000)
, new Customer("Trudy", "Zane", "Vermont", 31, 3500)
, new Customer("Reynold", "Zone", "Wyoming", 43, 3000)
, new Customer("Udele", "Wheetley", "Michigan", 37, 3500)
, new Customer("Blackburn", "Atkinson", "Illinois", 19, 4000)
, new Customer("Cotovatre", "Bailey", "Delaware", 26, 4000));
customerRepository.saveAll(customers);
}
}
Testing SpringBoot Pagination RestAPI
RestAPI Testcase 1 – Get All Distinct Salaries
– Make a request to ULR: /api/customers/salaries

RestAPI Testcase 2 – Pagination Filtering and Sorting RestAPI
– Make a request at API: /api/customers/custom/pageable
with pagination, filtering and sorting params as below:
page
: 0 – first pagesize
: 5 – size of a pagesalary
: 4000 – filtering bysalary
fieldagesorting
: true – sorting by agedesc
: true – descending or ascending sorting
– Result:

SpringBoot Ajax Pagination Example Frontend Development
Review Pagination View Page

We define the view page with 4 parts:
- Bootstrap Table is used to display Customer list with table format
- Pagination Bar is used to list all data pages
- Filtering Selection is used to select a specific salary to do the pagination with filtering
- Sorting Checkboxes is used to determine whether the sorting with
age
field or Not and sorting withdesc
ending or ascending(default value) direction
Html Bootstrap Pagination View Implementation
We use Bootstrap framework to style our pagination view, so we need to add the below bootstrap links to head
tag in index.html
file:
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
Pagination Table
We define a Bootstrap table with 7 columns to show all the properties of a Customer object:
- Id
- Firstname
- Lastname
- Age
- Salary
- Address
- Copyright
<table id="customerTable" class="table table-hover table-sm">
<thead class="thead-dark">
<tr>
<th>Id</th>
<th>Firstname</th>
<th>Lastname</th>
<th>Age</th>
<th>Salary</th>
<th>Address</th>
<th>Copyright By</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
Filtering Select
We use the html select form to create a salary filtering list as below code:
<div class="form-group">
<label for="slected_form"><h5>Filtering with Salary:</h5></label>
<select class="form-control" id="selected_form">
</select>
</div>
Sorting Checkboxes
We use html checkboxes to define 2 fields to determine for sorting or not:
agesorting
= true (means be checked) -> do the sorting byage
fielddesc
= true (mean be checked) -> do sorting with descending direction, (default is ascending direction)
Here is the html coding:
<form>
<div class="custom-control custom-checkbox mb-3">
<input type="checkbox" class="custom-control-input" id="age_sorting" name="age_sorting">
<label class="custom-control-label" for="age_sorting">Age</label>
</div>
<input type="checkbox" id="desc_sorting" name="desc_sorting" disabled>
<label for="defaultCheck">Desc</label>
<br>
</form>
<button id="sortingbtn" class="btn btn-primary" disabled>Sort Now</button>
Full HTML view page
We create a index.html
file in src/main/resources
as below code:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Ajax Paging Table</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/js/table.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-sm-7"
style="background-color: #e6f9ff; margin: 10px; padding: 10px; border-radius: 5px">
<div class="alert alert-danger">
<h3>Bootstrap + Ajax + SpringBoot Pagination</h3>
<hr>
<p>
<strong>@Copyright</strong> by <span style="color: blue">
<a href="https://loizenjava.com">https://loizenjava.com
</a></span>
<br>
<strong>youtube</strong>: <span style="color: crimson">
<a href="https://www.youtube.com/channel/UChkCKglndLes1hkKBDmwPWA">loizenjava
</a></span>
</p>
</div>
<h5>Do you want sorting by salary?</h5>
<form>
<div class="custom-control custom-checkbox mb-3">
<input type="checkbox" class="custom-control-input" id="age_sorting" name="age_sorting">
<label class="custom-control-label" for="age_sorting">Age</label>
</div>
<input type="checkbox" id="desc_sorting" name="desc_sorting" disabled>
<label for="defaultCheck">Desc</label>
<br>
</form>
<button id="sortingbtn" class="btn btn-primary" disabled>Sort Now</button>
<hr>
<div class="form-group">
<label for="slected_form"><h5>Filtering with Salary:</h5></label>
<select class="form-control" id="selected_form">
</select>
</div>
<table id="customerTable" class="table table-hover table-sm">
<thead class="thead-dark">
<tr>
<th>Id</th>
<th>Firstname</th>
<th>Lastname</th>
<th>Age</th>
<th>Salary</th>
<th>Address</th>
<th>Copyright By</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<ul class="pagination justify-content-center" style="margin:20px 0">
</ul>
</div>
</div>
</div>
</body>
</html>
JQuery Ajax Implementation
We use Ajax of JQuery to fetch data from SpringBoot restAPIs with pagination, filtering and sorting.
And using JQuery Javascript to control the data in html Bootstrap view.
Ajax Fetching data from remote RestAPI
For getting data with pagination, filtering and sorting from SpringBoot restAPIs, we implement a JQuery Fuction with Ajax code that includes a list of 5 needed parameters:
– Signature of the function: function fetchCustomers(page, size, salary, agesorting, desc)
page
: number of page for requestingsize
: a size of a data page for requestingsalary
: a selected salary for filteringagesorting
: a flag to determine to do the sorting byage
field or notdesc
: a flag to determine the direction for sorting,desc
= true for descending sorting direction anddesc
= false for ascending sorting direction (default value)
function fetchCustomers(page, size, salary, agesorting, desc){
let pageNumber = (typeof page !== 'undefined') ? page : 0;
let sizeNumber = (typeof size !== 'undefined') ? size : 5;
let selectedsalary = (typeof salary !== 'undefined') ? salary : -1;
let ageSorted = (typeof agesorting !== 'undefined') ? agesorting: false;
let descDirection = (typeof desc !== 'undefined') ? desc: false;
/**
* Do a fetching to get data from Backend's RESTAPI
*/
$.ajax({
type : "GET",
url : "/api/customers/custom/pageable",
data: {
page: pageNumber,
size: sizeNumber,
salary: selectedsalary,
agesorting: ageSorted,
desc: descDirection
},
success: function(response){
$('#customerTable tbody').empty();
// add table rows
$.each(response.customers, (i, customer) => {
let tr_id = 'tr_' + customer.id;
let customerRow = '<tr>' +
'<td>' + customer.id + '</td>' +
'<td>' + customer.firstname + '</td>' +
'<td>' + customer.lastname + '</td>' +
'<td>' + customer.age + '</td>' +
'<td>' + '$' + customer.salary + '</td>' +
'<td>' + customer.address + '</td>' +
'<td>' + '<a href="https://loizenjava.com">' + customer.copyrightBy + '</a>' + '</td>' +
'</tr>';
$('#customerTable tbody').append(customerRow);
});
if ($('ul.pagination li').length - 2 != response.totalPages){
// build pagination list at the first time loading
$('ul.pagination').empty();
buildPagination(response.totalPages);
}
},
error : function(e) {
alert("ERROR: ", e);
console.log("ERROR: ", e);
}
});
}
The fetchCustomers
function calls an Ajax request to the URL api/customers/custom/pageable
:
$.ajax({
type : "GET",
url : "/api/customers/custom/pageable",
data: {
page: pageNumber,
size: sizeNumber,
salary: selectedsalary,
agesorting: ageSorted,
desc: descDirection
},
If successfully, it will render a list of response’s Customers on defined-table by code:
success: function(response){
$('#customerTable tbody').empty();
// add table rows
$.each(response.customers, (i, customer) => {
let tr_id = 'tr_' + customer.id;
let customerRow = '<tr>' +
'<td>' + customer.id + '</td>' +
'<td>' + customer.firstname + '</td>' +
'<td>' + customer.lastname + '</td>' +
'<td>' + customer.age + '</td>' +
'<td>' + '$' + customer.salary + '</td>' +
'<td>' + customer.address + '</td>' +
'<td>' + '<a href="https://loizenjava.com">' + customer.copyrightBy + '</a>' + '</td>' +
'</tr>';
$('#customerTable tbody').append(customerRow);
});
And building the pagination list via the response variable’s property totalPages
as below code:
if ($('ul.pagination li').length - 2 != response.totalPages){
// build pagination list at the first time loading
$('ul.pagination').empty();
buildPagination(response.totalPages);
}
– buildPagination(totalPages)
function:
/**
*
* Build the pagination Bar from totalPages
*/
function buildPagination(totalPages){
// Build paging navigation
let pageIndex = '<li class="page-item"><a class="page-link">Previous</a></li>';
$("ul.pagination").append(pageIndex);
// create pagination
for(let i=1; i <= totalPages; i++){
// adding .active class on the first pageIndex for the loading time
if(i==1){
pageIndex = "<li class='page-item active'><a class='page-link'>"
+ i + "</a></li>"
} else {
pageIndex = "<li class='page-item'><a class='page-link'>"
+ i + "</a></li>"
}
$("ul.pagination").append(pageIndex);
}
pageIndex = '<li class="page-item"><a class="page-link">Next</a></li>';
$("ul.pagination").append(pageIndex);
}
Click function with Pagination Bar
When do a click on the pagination bar, we have 3 cases to determine:
- Next
- Previous
- a Specific Index Page
And we need to re-fecth the right pagination data from the remote RestAPI to build fill again the right data in html view page:
$(document).on("click", "ul.pagination li a", function() {
let agesorting = false;
let desc = false;
let selectedSalary = getSeletedSalary();
if($("#age_sorting"). prop("checked") == true){
agesorting = true;
}
if($("#desc_sorting"). prop("checked") == true){
desc = true;
}
let val = $(this).text();
// click on the NEXT tag
if(val.toUpperCase()==="NEXT"){
let activeValue = parseInt($("ul.pagination li.active").text());
let totalPages = $("ul.pagination li").length - 2; // -2 beacause 1 for Previous and 1 for Next
if(activeValue < totalPages){
let currentActive = $("li.active");
fetchCustomers(activeValue, 5, selectedSalary, agesorting, desc); // get next page value
// remove .active class for the old li tag
$("li.active").removeClass("active");
// add .active to next-pagination li
currentActive.next().addClass("active");
}
} else if(val.toUpperCase()==="PREVIOUS"){
let activeValue = parseInt($("ul.pagination li.active").text());
if(activeValue > 1){
// get the previous page
fetchCustomers(activeValue-2, 5, selectedSalary, agesorting, desc);
let currentActive = $("li.active");
currentActive.removeClass("active");
// add .active to previous-pagination li
currentActive.prev().addClass("active");
}
} else {
fetchCustomers(parseInt(val) - 1, 5, selectedSalary, agesorting, desc);
// add focus to the li tag
$("li.active").removeClass("active");
$(this).parent().addClass("active");
}
});
Do the Filtering with Selected Salary
We need to build the list salaries with distinct values, so we need to call an Ajax function getListSalaries()
at the initial time:
/**
* Get a list of distinct salaries
*/
function getListSalaries(){
$.ajax({
type : "GET",
url : "/api/customers/salaries",
success: function(response){
$("#selected_form").empty();
$('#selected_form').append("<option>All</option>");
$.each(response.sort().reverse(), (i, salary) => {
// <option>All</option>
let optionElement = "<option>" + salary + "</option>";
$('#selected_form').append(optionElement);
});
},
error : function(e) {
alert("ERROR: ", e);
console.log("ERROR: ", e);
}
});
}
If having any change from the salay filtering select-form, the fetchCustomers()
function will be invokes again with the selected salary for filtering the right data to paging and sorting. Here is the detail function:
/**
* Select a salary for pagination & filtering
*/
$("select").change(function() {
let salary = -1;
if(isNumeric(this.value)){
salary = this.value;
}
let agesorting = false;
let desc = false;
if($("#age_sorting"). prop("checked") == true){
agesorting = true;
}
if($("#desc_sorting"). prop("checked") == true){
desc = true;
}
// re-fetch customer list again
fetchCustomers(0, 5, salary, agesorting, desc);
});
JQuery process the sorting
With the sorting checkboxes, we implement 2 functions for handling the pagination and sorting:
$('#age_sorting').on('change', function(){...})
$(document).on("click", "#sortingbtn", function() {...});
– $('#age_sorting').on('change', function(){...})
is used to control the disable
attribute and the property checkbox checked
of desc
and sortingbtn
button
/**
* age_sorting checkbox is changed
*/
$('#age_sorting').on('change', function() {
if(this.checked){
$("#desc_sorting").removeAttr("disabled");
$("#sortingbtn").removeAttr("disabled");
}else {
$("#desc_sorting").attr("disabled", true);
$("#desc_sorting").prop("checked", false);
$("#sortingbtn").attr("disabled", true);
}
});
– $(document).on("click", "#sortingbtn", function() {...});
is used to fetch againt the pagination function with sorting parameters agesorting
and desc
: fetchCustomers(selectedPageIndex, 5, selectedSalary, agesorting, desc);
– Coding:
$(document).on("click", "#sortingbtn", function() {
let agesorting = false;
let desc = false;
let selectedSalary = getSeletedSalary();
//get value of check boxes
/* agesorting checkbox */
if($("#age_sorting"). prop("checked") == true){
agesorting = true;
}
/* desc checkbox */
if($("#desc_sorting"). prop("checked") == true){
desc = true;
}
// get the active index of pagination bar
let selectedPageIndex = parseInt($("ul.pagination li.active").text()) - 1;
// just fetch again customers from SpringBoot RestAPIs when agesorting checkbox is checked
if(agesorting){
fetchCustomers(selectedPageIndex, 5, selectedSalary, agesorting, desc); // get next page value
}
});
Full Ajax script
We implement all JQuery Ajax code in a file table.js
which are placed at /static/js/
folder. Here is the full sourcecode:
$(document).ready(function(){
function fetchCustomers(page, size, salary, agesorting, desc){
let pageNumber = (typeof page !== 'undefined') ? page : 0;
let sizeNumber = (typeof size !== 'undefined') ? size : 5;
let selectedsalary = (typeof salary !== 'undefined') ? salary : -1;
let ageSorted = (typeof agesorting !== 'undefined') ? agesorting: false;
let descDirection = (typeof desc !== 'undefined') ? desc: false;
/**
* Do a fetching to get data from Backend's RESTAPI
*/
$.ajax({
type : "GET",
url : "/api/customers/custom/pageable",
data: {
page: pageNumber,
size: sizeNumber,
salary: selectedsalary,
agesorting: ageSorted,
desc: descDirection
},
success: function(response){
$('#customerTable tbody').empty();
// add table rows
$.each(response.customers, (i, customer) => {
let tr_id = 'tr_' + customer.id;
let customerRow = '<tr>' +
'<td>' + customer.id + '</td>' +
'<td>' + customer.firstname + '</td>' +
'<td>' + customer.lastname + '</td>' +
'<td>' + customer.age + '</td>' +
'<td>' + '$' + customer.salary + '</td>' +
'<td>' + customer.address + '</td>' +
'<td>' + '<a href="https://loizenjava.com">' + customer.copyrightBy + '</a>' + '</td>' +
'</tr>';
$('#customerTable tbody').append(customerRow);
});
if ($('ul.pagination li').length - 2 != response.totalPages){
// build pagination list at the first time loading
$('ul.pagination').empty();
buildPagination(response.totalPages);
}
},
error : function(e) {
alert("ERROR: ", e);
console.log("ERROR: ", e);
}
});
}
/**
* Check a string value is a number or NOT
*/
function isNumeric(value) {
return /^-{0,1}\d+$/.test(value);
}
/**
* Select a salary for pagination & filtering
*/
$("select").change(function() {
let salary = -1;
if(isNumeric(this.value)){
salary = this.value;
}
let agesorting = false;
let desc = false;
if($("#age_sorting"). prop("checked") == true){
agesorting = true;
}
if($("#desc_sorting"). prop("checked") == true){
desc = true;
}
// re-fetch customer list again
fetchCustomers(0, 5, salary, agesorting, desc);
});
/**
* Get a list of distinct salaries
*/
function getListSalaries(){
$.ajax({
type : "GET",
url : "/api/customers/salaries",
success: function(response){
$("#selected_form").empty();
$('#selected_form').append("<option>All</option>");
$.each(response.sort().reverse(), (i, salary) => {
// <option>All</option>
let optionElement = "<option>" + salary + "</option>";
$('#selected_form').append(optionElement);
});
},
error : function(e) {
alert("ERROR: ", e);
console.log("ERROR: ", e);
}
});
}
/**
* age_sorting checkbox is changed
*/
$('#age_sorting').on('change', function() {
if(this.checked){
$("#desc_sorting").removeAttr("disabled");
$("#sortingbtn").removeAttr("disabled");
}else {
$("#desc_sorting").attr("disabled", true);
$("#desc_sorting").prop("checked", false);
$("#sortingbtn").attr("disabled", true);
}
});
/**
* Click on sorting Button
*/
$(document).on("click", "#sortingbtn", function() {
let agesorting = false;
let desc = false;
let selectedSalary = getSeletedSalary();
//get value of check boxes
/* agesorting checkbox */
if($("#age_sorting"). prop("checked") == true){
agesorting = true;
}
/* desc checkbox */
if($("#desc_sorting"). prop("checked") == true){
desc = true;
}
// get the active index of pagination bar
let selectedPageIndex = parseInt($("ul.pagination li.active").text()) - 1;
// just fetch again customers from SpringBoot RestAPIs when agesorting checkbox is checked
if(agesorting){
fetchCustomers(selectedPageIndex, 5, selectedSalary, agesorting, desc); // get next page value
}
});
/**
*
* Build the pagination Bar from totalPages
*/
function buildPagination(totalPages){
// Build paging navigation
let pageIndex = '<li class="page-item"><a class="page-link">Previous</a></li>';
$("ul.pagination").append(pageIndex);
// create pagination
for(let i=1; i <= totalPages; i++){
// adding .active class on the first pageIndex for the loading time
if(i==1){
pageIndex = "<li class='page-item active'><a class='page-link'>"
+ i + "</a></li>"
} else {
pageIndex = "<li class='page-item'><a class='page-link'>"
+ i + "</a></li>"
}
$("ul.pagination").append(pageIndex);
}
pageIndex = '<li class="page-item"><a class="page-link">Next</a></li>';
$("ul.pagination").append(pageIndex);
}
/**
* Get the selectedSalary for filtering
*/
function getSeletedSalary(){
if(!isNumeric($("select").val())){
return -1;
}else return parseInt($("select").val());
}
/**
*
* Fetching the Customers from SpringBoot RestAPI at the initial time
*/
(function(){
// get first-page at initial time
fetchCustomers(0);
// get the distinct values of customer's salaries
getListSalaries();
})();
/**
* Fetch again the customer's data from RestAPI when
* having any click on pagination bar for pagination filtering and sorting
*/
$(document).on("click", "ul.pagination li a", function() {
let agesorting = false;
let desc = false;
let selectedSalary = getSeletedSalary();
if($("#age_sorting"). prop("checked") == true){
agesorting = true;
}
if($("#desc_sorting"). prop("checked") == true){
desc = true;
}
let val = $(this).text();
// click on the NEXT tag
if(val.toUpperCase()==="NEXT"){
let activeValue = parseInt($("ul.pagination li.active").text());
let totalPages = $("ul.pagination li").length - 2; // -2 beacause 1 for Previous and 1 for Next
if(activeValue < totalPages){
let currentActive = $("li.active");
fetchCustomers(activeValue, 5, selectedSalary, agesorting, desc); // get next page value
// remove .active class for the old li tag
$("li.active").removeClass("active");
// add .active to next-pagination li
currentActive.next().addClass("active");
}
} else if(val.toUpperCase()==="PREVIOUS"){
let activeValue = parseInt($("ul.pagination li.active").text());
if(activeValue > 1){
// get the previous page
fetchCustomers(activeValue-2, 5, selectedSalary, agesorting, desc);
let currentActive = $("li.active");
currentActive.removeClass("active");
// add .active to previous-pagination li
currentActive.prev().addClass("active");
}
} else {
fetchCustomers(parseInt(val) - 1, 5, selectedSalary, agesorting, desc);
// add focus to the li tag
$("li.active").removeClass("active");
$(this).parent().addClass("active");
}
});
});
Integration Testing – JQuery Ajax Bootstrap Table & SpringBoot RestAPI
Now it’s time for integrative testing for the tutorial: “SpringBoot Ajax Pagination Example”.
Testcase 1 – Pagination View
– Start Loading time:

– Select page 3:

Testcase 2 – Pagination and Filtering View
– Pagination and Filtering with salary is $4000
:

– Pagination and Filtering with Salary = $3500
:

Testcase 3 – Pagination Filtering and Sorting View
– Pagination and Filtering with salary is $3000
and Sorting by Age
:

– Pagination and Filtering with salary = $3500
and sorting by Age
with Desc
direction:

Sourcecode
I attach the running and clearly sourcecode for the tutorial: “SpringBoot Ajax Pagination Example” on the site and in Github.
– Here is the full sourcecode with below implemented features:
- SpringBoot RestAPIs Pagination – Backend Development
- JQuery Ajax with Bootstrap Table Pagination Filtering and Sorting – Frontend Development
– Github Sourcecode:
SpringBoot + Ajax + Bootstrap Table – Paging Filtering and Sorting
great post!