Spring with Thymeleaf Pagination for a List

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-spring5</artifactId>
    <version>3.0.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-commons</artifactId>
    <version>2.0.9.RELEASE</version>
</dependency>

We can find the latest thymeleaf-spring5 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 {

    @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) {
        int currentPage = page.orElse(1);
        int pageSize = size.orElse(5);

        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.

Leave a Reply

Your email address will not be published.