Spring Data + Thymeleaf 3 + Bootstrap 4 Implementing Paginator

Keywords: Javascript Spring Thymeleaf less

Synchronize the original text to< https://waylau.com/spring-data-thymeleaf-bootstrap-paginator>;

In fact, paginators or paging components are widely used in reality, so it is not necessary for Lao Wei to write this article alone. In fact, I recently wrote an application course on Spring Data, Thymeleaf 3, Bootstrap 4, so I used Bootstrap 4 style. As a result, many plug-ins such as Bootstrap 3 tables, paginators and so on were not well compatible. Baidu Google failed, and Bootstrap 4 has not yet been released a stable version. There are few examples on the official website. Finally, I decided to write my own paginator, using Spring Data, Thymeleaf 3, Bootstrap 4.

What are the requirements for paginators

Chinese-style reports are always the most complex, and the paging requirements derived at will are also intricate. In order to tell you the principle of paginator, this example abstracts the Paginator component into the following general contents:

  • Display a list of page numbers;
  • The first item in the list is "the previous page" and the last item is "the next page".
  • The currently selected page number should be highlighted.
  • When there is no optional page number on the previous page of the current page, the "previous page" is set as a non-clickable state.
  • When the next page of the current page has no optional page number, the "next page" is set as a non-clickable state.

We can easily find a design prototype of Bootstrap paginator, as follows:

You can refer to the introduction of Bootstrap.com< Http://getbootstrap.com/components/#pagination>, but it is recommended that you do not use the above style directly, because this style is Bootstrap version 3. Finally, I found the style in Bootstrap 4, but it wasn't on the official website.< Http://www.quackit.com/bootstrap/bootstrap_4/tutorial/bootstrap_pagination.cfm>. Thank you. books-collection The project brings programmers open source, free book collection!

What Spring Data Can Do

org.springframework.data.domain.Page Spring Data provides a Paginator interface that provides common methods, such as:

  • List < T > getContent (); // Returns a list of paginated data
  • Int get Total Pages (); // Total Pages
  • Long get Total Elements (); // Total Data Volume
  • boolean isFirst(); // Is it the first data??
  • boolean isLast(); // is it the last data;?
  • int getNumber(); // Current page index

To construct a Page, you usually need to pass in a Page org.springframework.data.domain.PageRequest.PageRequest Object, the required parameter is (int page, int size), where page is the index of the requested page, and size is the size of the page (how many data are there in a page at most).

Spring Data can be said to provide all the elements we need for our front-end paginator.

A Trial of Thymeleaf Cattle Knife

As a template engine, Thymeleaf has the advantage of binding data sources and rendering pages according to data sources. The best way to do this is to traverse the bound data list to generate page elements, such as:

&lt;ul class="pagination" &gt;
    &lt;!-- Previous page --&gt;
    &lt;li class="page-item" data-th-classappend="*{first} ? 'disabled' : ''"&gt;
        &lt;a href="javascript:void(0);" class="page-link" data-th-attr="pageIndex=${page.number} - 1" aria-label="Previous"&gt;
            &lt;span aria-hidden="true"&gt;«&lt;/span&gt;
        &lt;/a&gt;
    &lt;/li&gt;

    &lt;!-- Iterative generation of page numbers --&gt;
    &lt;li class="page-item" data-th-each="i : ${#numbers.sequence(1, page.totalPages)}" 
        data-th-classappend="${(page.number + 1) eq i} ? 'active' : ''" &gt;
        &lt;a class="page-link" data-th-attr="pageIndex=${i} - 1" href="javascript:void(0);"&gt;
             &lt;span data-th-text="${i}"&gt;&lt;/span&gt;
        &lt;/a&gt;
    &lt;/li&gt;

    &lt;!-- next page --&gt;
    &lt;li class="page-item" data-th-classappend="*{last} ? 'disabled' : ''"&gt;
        &lt;a href="javascript:void(0);" class="page-link" data-th-attr="pageIndex=${page.number} + 1" aria-label="Next"&gt;
            &lt;span aria-hidden="true"&gt;»&lt;/span&gt;
        &lt;/a&gt;
    &lt;/li&gt;
&lt;/ul&gt;

This is a simple version of the paginator, you can see that our Paginator "last page" and "next page" is fixed, in the middle according to the total Pages (total page) to dynamically generate pages. At the same time, we set whether the style is active based on whether it is the current page (number + 1). "The previous page" and "the next page" need to be judged, if the current page is the first page, then "the previous page" can not be disabled; if the current page is the last page, then "the next page" can not be disabled.

Think a little more about it.

In fact, the above version can handle most of the application scenarios. But it may be a little imperfect. For example, what if I have a lot of pages? Then our paging list may be stretched too long, and the leaders may not be satisfied! It is absolutely necessary to strangle this dissatisfaction in the cradle.

As you can see, if you want to be more perfect, you need to consider that when there are too many pages, you should use ellipses. This involves three situations:

  • When the current page number is close to the home page, the ellipsis number appears at the back.
  • When the current page number is connected to the last page, the ellipsis number appears at the front.
  • The most annoying thing is that when the current page is in the middle, both the front and the back need ellipsis marks.

Paginator with Ellipsis

Smart engineers should act immediately and sketch the algorithm roughly:

For simplicity, we preset that the list of page numbers should be at most 7 pages (you can also change it as needed). That is to say, when total Pages exceed 7, we need to consider ellipsis.

  • The algorithms of "the previous page" and "the next page" are similar to those of our simple version above, so we won't dwell on them here.
  • When the current page number is less than or equal to 4, the ellipsis number appears in the penultimate second at the back of the list.
  • When the difference between the last page and the current page is less than or equal to 3, the ellipsis sign appears at the second position in the front of the list.
  • Otherwise, the current page is in the middle, and the ellipsis appears in the second and penultimate position of the list.

The implementation is as follows:

    &lt;!-- Handle cases where the number of pages is greater than 7 --&gt;  
    &lt;ul class="pagination" data-th-if="${page.totalPages gt 7}" &gt;
        &lt;!-- Previous page --&gt;
        &lt;li class="page-item" data-th-classappend="*{first} ? 'disabled' : ''"&gt;
            &lt;a href="javascript:void(0);" class="page-link" data-th-attr="pageIndex=${page.number} - 1" aria-label="Previous"&gt;
                &lt;span aria-hidden="true"&gt;«&lt;/span&gt;
            &lt;/a&gt;
        &lt;/li&gt;

        &lt;!-- home page --&gt;
        &lt;li class="page-item" data-th-classappend="${(page.number + 1) eq 1} ? 'active' : ''" &gt;
            &lt;a href="javascript:void(0);" class="page-link" data-th-attr="pageIndex=0"&gt;1&lt;/a&gt;
        &lt;/li&gt;

        &lt;!-- The current page is less than or equal to 4 --&gt;
        &lt;li class="page-item" data-th-if="${(page.number + 1) le 4}" data-th-each="i : ${#numbers.sequence(2,5)}" 
            data-th-classappend="${(page.number + 1) eq i} ? 'active' : ''" &gt;
            &lt;a class="page-link" href="javascript:void(0);" data-th-attr="pageIndex=${i} - 1"&gt;
                &lt;span data-th-text="${i}"&gt;&lt;/span&gt;
            &lt;/a&gt;
        &lt;/li&gt;

        &lt;li class="page-item disabled" data-th-if="${(page.number + 1) le 4}"&gt;
            &lt;a href="javascript:void(0);" class="page-link"&gt;
                &lt;span aria-hidden="true"&gt;...&lt;/span&gt;
            &lt;/a&gt;
        &lt;/li&gt;

        &lt;!-- The difference between the last page and the current page is less than or equal to 3 --&gt;
        &lt;li class="page-item disabled" data-th-if="${(page.totalPages-(page.number + 1)) le 3}"&gt;
            &lt;a href="javascript:void(0);" class="page-link"&gt;
                &lt;span aria-hidden="true"&gt;...&lt;/span&gt;
            &lt;/a&gt;
        &lt;/li&gt;  
        &lt;li class="page-item" data-th-if="${(page.totalPages-(page.number + 1)) le 3}" data-th-each="i : ${#numbers.sequence(page.totalPages-4, page.totalPages-1)}" 
            data-th-classappend="${(page.number + 1) eq i} ? 'active' : ''" &gt;
            &lt;a class="page-link" href="javascript:void(0);" data-th-attr="pageIndex=${i} - 1"&gt;
                &lt;span data-th-text="${i}"&gt;&lt;/span&gt;
           &lt;/a&gt;
        &lt;/li&gt;

         &lt;!-- The difference between the last page and the current page is greater than 3, and the current page is greater than 4.--&gt;

        &lt;li class="page-item disabled" data-th-if="${((page.number + 1) gt 4) &amp;&amp; ((page.totalPages-(page.number + 1)) gt 3 )}"&gt;
            &lt;a href="javascript:void(0);" class="page-link"&gt;
                &lt;span aria-hidden="true"&gt;...&lt;/span&gt;
            &lt;/a&gt;
        &lt;/li&gt; 
        &lt;li class="page-item" data-th-if="${((page.number + 1) gt 4) &amp;&amp; ((page.totalPages-(page.number + 1)) gt 3 )}" &gt;
            &lt;a href="javascript:void(0);" class="page-link" data-th-attr="pageIndex=${page.number}"&gt;[[${page.number}]]&lt;/a&gt;
        &lt;/li&gt;
        &lt;li class="page-item active" data-th-if="${((page.number + 1) gt 4) &amp;&amp; ((page.totalPages-(page.number + 1)) gt 3 )}"&gt;
            &lt;a href="javascript:void(0);" class="page-link" data-th-attr="pageIndex=${page.number} + 1"&gt;[[${page.number + 1}]]&lt;/a&gt;
        &lt;/li&gt;
        &lt;li class="page-item" data-th-if="${((page.number + 1) gt 4) &amp;&amp; ((page.totalPages-(page.number + 1)) gt 3 )}"&gt;
            &lt;a href="javascript:void(0);" class="page-link" data-th-attr="pageIndex=${page.number} + 2"&gt;[[${page.number + 2}]]&lt;/a&gt;
        &lt;/li&gt;

        &lt;li class="page-item disabled"  data-th-if="${((page.number + 1) gt 4) &amp;&amp; ((page.totalPages-(page.number + 1)) gt 3 )}"&gt;
            &lt;a href="javascript:void(0);" class="page-link"&gt;
                &lt;span aria-hidden="true"&gt;...&lt;/span&gt;
            &lt;/a&gt;
        &lt;/li&gt;

        &lt;!-- last page --&gt;
        &lt;li class="page-item" data-th-classappend="${(page.number + 1) eq page.totalPages} ? 'active' : ''" &gt;
            &lt;a href="javascript:void(0);" class="page-link" data-th-attr="pageIndex=${page.totalPages} - 1"&gt;[[${page.totalPages}]]&lt;/a&gt;
        &lt;/li&gt;

        &lt;!-- next page --&gt;
        &lt;li class="page-item" data-th-classappend="*{last} ? 'disabled' : ''"&gt;
            &lt;a href="javascript:void(0);" class="page-link" data-th-attr="pageIndex=${page.number} + 1" aria-label="Next"&gt;
                &lt;span aria-hidden="true"&gt;»&lt;/span&gt;
            &lt;/a&gt;
        &lt;/li&gt;
    &lt;/ul&gt;

More to think about?

Of course, as we said at the beginning, the demand for Chinese-style reports is very strange. This article just starts from most of the general needs and gives a train of thought, which may not meet the needs of all people. If possible, consider more, such as:

  • Can you choose the largest page of the page?
  • Can you choose the index of any page?
  • ...

Wait a minute. Nima looks like it's almost 1 a.m. It's not going well. I'm going to sleep. Readers and friends can continue to improve~

Reference

Posted by micmania1 on Wed, 17 Apr 2019 02:21:33 -0700