In this blog, we will discuss how we can create a reusable pagination component using Thymeleaf and Spring MVC/Data.
Goal is to create a pagination UI similar to this:
Controller
@GetMapping("/articles")
public String userHome(Model model, Pageable pageable) {
Page<Article> page= articleService.findAllByStatus(Status.PUBLISHED, pageable);
model.addAttribute("articles", page);
return "home-page";
}
@GetMapping("/users")
public String userHome(Model model, Pageable pageable) {
Page<User> page= userRepo.findAllByStatus(Status.ACTIVE, pageable);
model.addAttribute("users", page);
return "user-list";
}
Pagination at home-page.html
<th:block th:each="article : ${articles}">
<!-- display article in a table row or card/box -->
</th:block>
<!--pager at bottom -->
<th:block th:replace="_fragments/_utils :: pagination(${articles})">
</th:block>
Pagination at user-list.html
<th:block th:each="user : ${users}">
<!-- display user in a table row or card/box -->
</th:block>
<!--pager at bottom -->
<th:block th:replace="_fragments/_utils :: pagination(${users})">
</th:block>
Reusable Fragment Definition at _fragments/_utils.html
Note that the pages are 0 based meaning page=0 equals the first page
of data. You can make it to 1 based using the steps described here:
https://ganeshtiwaridotcomdotnp.blogspot.com/2020/09/spring-pageable-start-index-with-one.html. You might also need to update the indices below to to handle that.
<th:block th:fragment="pagination(pagedObject)">
<!-- works with org.springframework.data.domain.Page<pagedObject>, -->
<div th:if="${pagedObject.getTotalPages() != 1}" class="form-group col-md-11 pagination-centered">
<ul class="pagination">
<!-- page number start with 0, totalPages returns actual number of pages -->
<li th:classappend="${pagedObject.getNumber() == 0} ? disabled" class="page-item">
<a class="page-link" th:href="@{''(size=${pagedObject.getSize()}, page=0)}">«</a>
</li>
<li th:classappend="${pagedObject.getNumber() == 0} ? disabled" class="page-item">
<a class="page-link"
th:href="@{''(size=${pagedObject.getSize()}, page=${pagedObject.getNumber() -1})}">←</a>
</li>
<li th:classappend="${pagedObject.getNumber() == (page )} ? 'active pointer-disabled'"
th:each="page : ${#numbers.sequence(0, pagedObject.getTotalPages() -1)}" class="page-item">
<a class="page-link" th:href="@{''(size=${pagedObject.getSize()}, page=${page})}"
th:text="${page + 1}"></a>
</li>
<li th:classappend="${pagedObject.getNumber() + 1 == pagedObject.getTotalPages()} ? disabled"
class="page-item">
<a class="page-link"
th:href="@{''(size=${pagedObject.getSize()}, page=${pagedObject.getNumber() + 1})}">→</a>
</li>
<li th:classappend="${pagedObject.getNumber() + 1 == pagedObject.getTotalPages()} ? disabled"
class="page-item">
<a class="page-link"
th:href="@{''(size=${pagedObject.getSize()}, page=${pagedObject.getTotalPages()})}">»</a>
</li>
</ul>
<p class="text-muted small">
Showing page <span th:text="${pagedObject.getNumber() +1}"></span> of <span
th:text="${pagedObject.getTotalPages()}"></span>.
Total: <span th:text="${pagedObject.getTotalElements()}"></span>.
</p>
</div>
<div th:if="${pagedObject.getTotalElements() ==0}">
<div class="alert alert-warning" role="alert">
<span>No items available.</span>
</div>
</div>
</th:block>
Reference: https://github.com/gtiwari333/spring-boot-web-application-seed/blob/master/main-app/src/main/resources/templates/_fragments/_utils.html