We would not complete this series on How to Implement Pagination in Spring Boot. You can find Part 1 here.
In this part we would add the Page Number links to the table footer. We would also add the links for the First, Previous, Next and Last pages.
1. The Pagination Layout
We would use the pagination layout available in Bootstrap. This is shown below:
<nav aria-label="Page navigation example"> Total Items [[${totalItems}]] : Page [[${currentPage}]] of [[${totalPages}]] <ul class="pagination"> <li class="page-item"> First </li> <li class="page-item"> Previous </li> <li class="page-item"> 1 2 3 4 ... </li> <li class="page-item"> Next </li> <li class="page-item"> Last </li> </ul> </nav>
2. First Page
This would be a link if the current page is greater than 1. In other words, it would be a plain text unless the current page is greater than 1. It makes a request for page number of 1. The code is given below:
<a class="page-link" th:if="${currentPage > 1}" th:href="@{/parameters/countries/page/1}">First</a> <span th:unless="${currentPage > 1}">First</span>
3. Previous Page
Just like the first page, the text would be a link if the current page is greater than 1. However, it makes a request for page number of currentPage – 1. See the code below:
<a th:if="${currentPage > 1}" th:href="@{'/parameters/countries/page/' + ${currentPage - 1}}">Previous</a> <span th:unless="${currentPage > 1}">Previous</span>
4. Page Number Links
The page number links would exactly as we have it before but now inside the <li></li> tags.
<span th:each="i: ${#numbers.sequence(1, totalPages)}"> <a th:if="${i != currentPage}" th:href="@{'/parameters/countries/page/' + ${i}}">[[${i}]]</a> <span th:unless="${i != currentPage}">[[${i}]]</span> </span>
5. Next Page
In case of the next page, the Next displays a link if the current page is less than the total pages. Otherwise, it displays a plain text. It request for currentPage + 1
<a th:if="${currentPage < totalPages}" th:href="@{'/parameters/countries/page/' + ${currentPage + 1}}">Next</a> <span th:unless="${currentPage < totalPages}">Next</span>
6. Last Page
In this case, a link would be displayed if the current page is less than the total pages. Otherwise, it displays a plain text. It simply request for the page number of totalPages.
<a th:if="${currentPage < totalPages}" th:href="@{'/parameters/countries/page/' + ${totalPages}}">Last</a> <span th:unless="${currentPage < totalPages}">Last</span>
7. Combining Paging and Sorting
Now, if you we have the pagination working as expected. But if you try to do sorting by clicking on the column headers, it doesn’t work. We’ll fix this by combining pagination and sorting.
First, note the following points:
- pagination takes precedence over sorting. So we’ll focus on making sorting work
- to sort, we’ll need to provide the page number to sort. This can be done via path variable
- paging to another page could undo existing sort status
You have to make the following changes:
Change 1 – In the html template, adjust the head links to includes the page to sort. In this case, it would be the current page like so:
<a th:href="@{'/parameters/countries/page/' + ${currentPage} + '/description?sortDir=' + ${reverseSortDir}}">
Change 2 – In the controller, create a new method getPageWithSort() that derives from getAllWithSort()
Change 3 – The GetMapping for this method would be:
@GetMapping("/parameters/page/{pageNumber}/countries/{field}")
Change 4 – Add the pageNumber as parameter to the method. Name it currentPage
Change 5 – Add the currentPage as third argument to the findCountryWithSorting() function call. This means the findAll() method would then take pageable as argument instead of sort. The modified findAllWithSort() is given below:
public Page<Country> findCountryWithSorting2(String field, String direction, int pageNumber){ Sort sort = direction.equalsIgnoreCase(Sort.Direction.ASC.name())? Sort.by(field).ascending(): Sort.by(field).descending(); Pageable pageable = PageRequest.of(pageNumber - 1,5, sort); return countryRepository.findAll(pageable); }
8. Modify the Controller Method
Currently, in the controller, we have the getAllWithSort() function that takes two parameters: sortField and sortDirection. Now we would do three things:
- change the name of the function from getAllWithSort() to getPageWithSort()
- make the function accept one more parameter, that is the page number to get
- pass the page number across as well to the findAllWithSort() which is in the service.
The new getPageWithSort() is given below:
@GetMapping("/parameters/countries/page/{pageNumber}/{field}") public String getPageWithSort(Model model, @PathVariable("pageNumber") int currentPage, @PathVariable String field, @PathParam("sortDir") String sortDir){ Page<Country> page = countryService.findCountryWithSorting2(field, sortDir, currentPage); List<Country> countries = page.getContent(); int totalPages = page.getTotalPages(); long totalItems = page.getTotalElements(); model.addAttribute("currentPage", currentPage); model.addAttribute("totalPages", totalPages); model.addAttribute("totalItems", totalItems); model.addAttribute("sortDir", sortDir); model.addAttribute("reverseSortDir", sortDir.equals("asc")?"desc":"asc"); model.addAttribute("countries", countries); return "/parameters/countries"; }
I do recommend that you watch the video tutorial here for clarification.