1. Overview
In this quick tutorial, we’ll build a simple application to display a list of items with pagination using Spring and Thymeleaf.
For an introduction on how to integrate Thymeleaf with Spring, please take a look at our article here.
2. Maven Dependencies
Besides the usual Spring dependencies, we’ll add the dependencies for Thymeleaf and Spring Data Commons:
<dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring4</artifactId> <version>3.0.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-commons</artifactId> <version>2.0.7.RELEASE</version> </dependency>
We can find the latest thymeleaf-spring4 and spring-data-commons dependencies in the Maven Central repository.
3. Models
Our sample application will demonstrate pagination for a list of books.
First, let’s define a Book class with two fields and an all-arguments constructor:
public class Book { private int id; private String name; // standard constructor, setters and getters }
4. Service
Then we’ll create a service to generate the paginated book list for the requested page using the Spring Data Commons library:
@Service public class BookService { final private List<Book> books = BookUtils.buildBooks(); public Page<Book> findPaginated(Pageable pageable) { int pageSize = pageable.getPageSize(); int currentPage = pageable.getPageNumber(); int startItem = currentPage * pageSize; List<Book> list; if (books.size() < startItem) { list = Collections.emptyList(); } else { int toIndex = Math.min(startItem + pageSize, books.size()); list = books.subList(startItem, toIndex); } Page<Book> bookPage = new PageImpl<Book>(list, PageRequest.of(currentPage, pageSize), books.size()); return bookPage; } }
In the above service, we created a method to return the selected Page based on requested page, which is represented by the Pageable interface. The PageImpl class helps to filter out the paginated list of books.
5. Spring Controller
We’ll need a Spring controller to retrieve the book list of a selected page when given the page size and current page number.
To use the default values for selected page and page size, we can simply access the resource at /listBooks, without any parameters.
If any page size or specific page is required, we can add parameters page and size.
For example, /listBooks?page=2&size=6 will retrieve page two with six items per page:
@Controller public class BookController { private static int currentPage = 1; private static int pageSize = 5; @Autowired private BookService bookService; @RequestMapping(value = "/listBooks", method = RequestMethod.GET) public String listBooks( Model model, @RequestParam("page") Optional<Integer> page, @RequestParam("size") Optional<Integer> size) { page.ifPresent(p -> currentPage = p); size.ifPresent(s -> pageSize = s); Page<Book> bookPage = bookService.findPaginated(PageRequest.of(currentPage - 1, pageSize)); model.addAttribute("bookPage", bookPage); int totalPages = bookPage.getTotalPages(); if (totalPages > 0) { List<Integer> pageNumbers = IntStream.rangeClosed(1, totalPages) .boxed() .collect(Collectors.toList()); model.addAttribute("pageNumbers", pageNumbers); } return "listBooks.html"; } }
To prepare our pagination for the view, we’ve added model attributes in the Spring controller, including the selected Page and a list of page numbers.
6. Thymeleaf Template
Now it’s time to create a Thymeleaf template “listBooks.html”, which displays the list of books with pagination based on model attributes from our Spring controller.
First, we iterate the list of books and display them in a table. Then we show the pagination when the total number of pages is greater than zero.
Every time we click and select a page, the corresponding list of books will be displayed with the current page link highlighted:
<table border="1"> <thead> <tr> <th th:text="#{msg.id}" /> <th th:text="#{msg.name}" /> </tr> </thead> <tbody> <tr th:each="book, iStat : ${bookPage.content}" th:style="${iStat.odd}? 'font-weight: bold;'" th:alt-title="${iStat.even}? 'even' : 'odd'"> <td th:text="${book.id}" /> <td th:text="${book.name}" /> </tr> </tbody> </table> <div th:if="${bookPage.totalPages > 0}" class="pagination" th:each="pageNumber : ${pageNumbers}"> <a th:href="@{/listBooks(size=${bookPage.size}, page=${pageNumber})}" th:text=${pageNumber} th:class="${pageNumber==bookPage.number + 1} ? active"></a> </div>
7. Conclusion
In this article, we demonstrated how we can paginate a list using Thymeleaf with the Spring framework.
As usual, all the code samples used in the article are available over on GitHub.