【问题标题】:Spring + Thymeleaf - how to implement pagination for a listSpring + Thymeleaf - 如何实现列表的分页
【发布时间】:2013-08-31 16:46:30
【问题描述】:

我正在使用 Spring + Thymeleaf 开发一个简单的应用程序。在其中一个页面上,我有一个需要分页的项目列表。

理想情况下,我只想将currPageNo(当前页数)和numOfPages(总页数)变量发送到视图,其余的工作将在那里完成(这个是一个演示问题,与业务逻辑无关)。但是,如果最干净的解决方案需要我首先在控制器中进行一些计算,我会接受它作为一个小问题。

我想获取以下形式的页面列表。

<Prev | 1 | ... | 3 | 4 | 5 | 6 | 7 | ... | 15 | Next>

我只能提供以下解决方案。它有效,但我相信您会同意它非常混乱且非常难以阅读。

此外,除了currPageNonumOfPages,我还必须向视图发送另外两个变量。理想的解决方案不需要我这样做。

firstPageNo = Math.max(2, currPageNo - 2)
lastPageNo = Math.min(numOfPages - 1, currPageNo + 2)

我的代码的当前版本如下。

<ul>
    <li th:if="${currPageNo &gt; 1}">
        <a th:href="@{/items.html(pageNo = ${currPageNo - 1})}" href="">&lt; Prev</a>
    </li>
    <li th:class="${currPageNo == 1} ? 'selected'">
        <a th:href="@{/items.html(pageNo = 1)}" th:if="${currPageNo &gt; 1}" href="">1</a>
        <span th:if="${currPageNo == 1}">1</span>
    </li>
    <li th:if="${currPageNo &gt;= 5}">
        ...
    </li>
    <li th:each="pageNo : ${#numbers.sequence(firstPageNo, lastPageNo)}"  th:class="${currPageNo == pageNo} ? 'selected'">
        <a th:href="@{/items.html(pageNo = ${pageNo})}" th:if="${pageNo != currPageNo}" th:text="${pageNo}" href="">2</a>
        <span th:if="${pageNo == currPageNo}" th:text="${pageNo}">2</span>
    </li>
    <li th:if="${currPageNo &lt;= (numOfPages - 4)}">
        ...
    </li>
    <li th:class="${currPageNo == numOfPages} ? 'selected'">
        <a th:href="@{/items.html(pageNo = ${numOfPages})}" th:if="${currPageNo &lt; numOfPages}" th:text="${numOfPages}" href="">10</a>
        <span th:if="${currPageNo == numOfPages}" th:text="${numOfPages}">1</span>
    </li>
    <li th:if="${currPageNo &lt; numOfPages}">
        <a th:href="@{/items.html(pageNo = ${currPageNo + 1})}" href=""> Next &gt;</a>
    </li>
</ul>

以下列表总结了我最想摆脱的问题。我知道其中一些是平台固有的,但列表似乎很长而且代码很乱。

  • 必须将 firstPageNolastPageNo 的预计算值从控制器发送到视图。
  • 必须在表达式中使用&amp;lt; 而不是&lt;
  • 必须同时使用具有互斥条件的锚点和跨度,以使浏览器不使用当前页面的链接。

我也欢迎任何其他关于如何提高代码质量的建议。


我知道这个问题可能更适合 Code Review 网站,但是,由于 Thymeleaf 似乎是一项用户群很小的技术,我希望在 Stack Overflow 上得到一个合理的答案,它有一个更大的用户群(我相信)。

但是,如果这里真的不欢迎这样的问题,请考虑将其移至正确的站点而不是关闭它,以便我获得所需的建议。

【问题讨论】:

    标签: spring pagination thymeleaf


    【解决方案1】:

    类似于中描述的解决方案 http://www.javacodegeeks.com/2013/03/implement-bootstrap-pagination-with-spring-data-and-thymeleaf.html

    但不使用 Spring Pageable 的包装器

    <div class="table-pagination">
        <ul class="pagination">
            <li th:class="${contactsPage.number eq 0} ? 'disabled' : ''">
                <a th:if="${not contactsPage.firstPage}" th:href="@{${'/contacts'}(page=${contactsPage.number-1},size=${contactsPage.size})}">Previous</a>
                <a th:if="${contactsPage.firstPage}" href="javascript:void(0);">Previous</a>
            </li>
    
            <li th:each="pageNo : ${#numbers.sequence(0, contactsPage.totalPages - 1)}" th:class="${contactsPage.number eq pageNo}? 'active' : ''">
                <a th:if="${contactsPage.number  eq pageNo}" href="javascript:void(0);">
                    <span th:text="${pageNo + 1}"></span>
                </a>
                <a th:if="${not (contactsPage.number  eq pageNo)}" th:href="@{${'/contacts'}(page=${pageNo},size=${contactsPage.size})}">
                    <span th:text="${pageNo + 1}"></span>
                </a>
    
            </li>
            <li th:class="${contactsPage.number + 1 ge contactsPage.totalPages} ? 'disabled' : ''">
                <a th:if="${not contactsPage.lastPage}" th:href="@{${'/contacts'}(page=${contactsPage.number+1},size=${contactsPage.size})}">Next</a>
                <a th:if="${contactsPage.lastPage}" href="javascript:void(0);">Next</a>
            </li>
        </ul>
    </div>
    

    【讨论】:

    • 请附上一些文字,例如带代码的解释。
    • 不要只是转储代码...还要添加一些肉。
    • 请注意,现在您必须将 firstPagelastPage 替换为 firstlast
    • 页面多时如何做到用户友好。 (例如超过 100 个)
    【解决方案2】:

    另一个选择是 Ben Thurley 的解决方案。我们已经实现它并且运行顺利:http://bthurley.wordpress.com/2012/07/18/spring-mvc-with-restful-datatables/

    它缺少几个项目,例如用于搜索的过滤器参数,但您可以通过 PagingCriteria 对象轻松添加,并确保将其添加到 TableParamArgumentResolver。

    public class TableParamArgumentResolver implements WebArgumentResolver {
    
        private static final String S_ECHO           = "sEcho";
        private static final String I_DISPLAY_START  = "iDisplayStart";
        private static final String I_DISPLAY_LENGTH = "iDisplayLength";
        private static final String I_SORTING_COLS   = "iSortingCols";
    
        private static final String I_SORT_COLS      = "iSortCol_";
        private static final String S_SORT_DIR       = "sSortDir_";
        private static final String S_DATA_PROP      = "mDataProp_";
        private static final String I_DATA_SEARCH    = "sSearch";
    
        public Object resolveArgument(MethodParameter param, NativeWebRequest request)
                throws Exception {
            TableParam tableParamAnnotation = param.getParameterAnnotation(TableParam.class);
    
            if (tableParamAnnotation != null) {
                HttpServletRequest httpRequest = (HttpServletRequest) request.getNativeRequest();
    
                String sEcho = httpRequest.getParameter(S_ECHO);
                String sDisplayStart = httpRequest.getParameter(I_DISPLAY_START);
                String sDisplayLength = httpRequest.getParameter(I_DISPLAY_LENGTH);
                String sSortingCols = httpRequest.getParameter(I_SORTING_COLS);
                String sSearch = httpRequest.getParameter(I_DATA_SEARCH);
    
                Integer iEcho = Integer.parseInt(sEcho);
                Integer iDisplayStart = Integer.parseInt(sDisplayStart);
                Integer iDisplayLength = Integer.parseInt(sDisplayLength);
                Integer iSortingCols = Integer.parseInt(sSortingCols);
    
                List<SortField> sortFields = new ArrayList<SortField>();
                for (int colCount = 0; colCount < iSortingCols; colCount++) {
                    String sSortCol = httpRequest.getParameter(I_SORT_COLS + colCount);
                    String sSortDir = httpRequest.getParameter(S_SORT_DIR + colCount);
                    String sColName = httpRequest.getParameter(S_DATA_PROP + sSortCol);
                    sortFields.add(new SortField(sColName, sSortDir));
                }
    
                PagingCriteria pc = new PagingCriteria(iDisplayStart, iDisplayLength, iEcho, sortFields, sSearch);
    
                return pc;
            }
    
            return WebArgumentResolver.UNRESOLVED;
        }
    }
    

    【讨论】:

      【解决方案3】:

      我已经准备好了,希望对您有所帮助....

      <div class="tag-box tag-box-v7 text-justify">
          <div class="text-center">
              <ul class="pagination" th:with="elementsperpage=2, blocksize=10, pages=${page2th.Number}/${elementsperpage}, wholepages=${format.format(pages)},
      whole=(${page2th.Number}/${blocksize})+1, wholex=${format.format(whole)}, startnlockpage=${wholepages}*${blocksize+1}, endblockpage=${wholepages}*${blocksize+1}, 
      startpage=${wholex-1}*${blocksize}, endpage=(${wholex}*${blocksize})+1">
      
                  <li>
                      <a th:if="${startpage gt 0}" th:href="@{${'/viewannouncements?p='}+${startpage}}">&lt;&lt;</a>
                      <a th:if="${startpage eq 0}" href="javascript:void(0);">&lt;&lt;</a>
                  </li>
      
                  <li th:each="pageNo : ${#numbers.sequence(endpage-11, (endpage lt page2th.TotalPages)? endpage-2 : page2th.TotalPages-1)}" 
                  th:class="${page2th.Number eq pageNo}? 'active' : ''">
                          <a th:if="${page2th.Number eq pageNo}" href="javascript:void(0);">
                              <span th:text="${pageNo + 1}"></span>
                          </a>
                          <a th:if="${not (page2th.Number  eq pageNo)}" th:href="@{${'/viewannouncements?p='}+${pageNo+1}}">
                              <span th:text="${pageNo + 1}"></span>
                          </a>
                  </li>
      
                  <li>
                      <a th:if="${(endpage*elementsperpage) le (page2th.TotalElements)}" th:href="@{${'/viewannouncements?p='}+${endpage}}">&gt;&gt;</a>
                      <a th:if="${(endpage*elementsperpage) le (page2th.TotalElements)}" href="javascript:void(0);"></a>
                  </li>
      
      
      
              </ul>
          </div>
      </div>
      

      【讨论】:

        【解决方案4】:

        这是我的解决方案。 结果会变成这样

        控制器:

         @GetMapping("/myPage")
        public String main(
                @PageableDefault(sort = {"id"}, direction = Sort.Direction.ASC, value = 100) Pageable pageable,
                Model model) {
            Page<myData> page;
        
            page = boardgameService.findAll(pageable);
        
            int[] body;
            if (page.getTotalPages() > 7) {
                int totalPages = page.getTotalPages();
                int pageNumber = page.getNumber()+1;
                int[] head = (pageNumber > 4) ? new int[]{1, -1} : new int[]{1,2,3};
                int[] bodyBefore = (pageNumber > 4 && pageNumber < totalPages - 1) ? new int[]{pageNumber-2, pageNumber-1} : new int[]{};
                int[] bodyCenter = (pageNumber > 3 && pageNumber < totalPages - 2) ? new int[]{pageNumber} : new int[]{};
                int[] bodyAfter = (pageNumber > 2 && pageNumber < totalPages - 3) ? new int[]{pageNumber+1, pageNumber+2} : new int[]{};
                int[] tail = (pageNumber < totalPages - 3) ? new int[]{-1, totalPages} : new int[] {totalPages-2, totalPages-1, totalPages};
                body = ControllerUtils.merge(head, bodyBefore, bodyCenter, bodyAfter, tail);
        
            } else {
                body = new int[page.getTotalPages()];
                for (int i = 0; i < page.getTotalPages(); i++) {
                    body[i] = 1+i;
                }
            }
        
            model.addAttribute("body", body);
            model.addAttribute("page", page);
            return "myPage";
        }
        

        ControllerUtils 方法“合并”:

        public static int[] merge(int[]... intarrays) {
            return Arrays.stream(intarrays).flatMapToInt(Arrays::stream)
                    .toArray();
        }
        

        还有我的 pager.html

        <div th:fragment="pager (pagination)">
            <ul class="pagination">
                <th:block th:each="pageNumber : ${body}">
                    <li th:if="${pageNumberStat.first}" class="page-item">
                        <a class="page-link" th:href="${url} + '?page='+ ${page.getNumber()} + '&size=' + ${page.getSize()}" aria-label="Previous">
                            <span aria-hidden="true">&laquo;</span>
                        </a>
                    </li>
                    <li class="page-item active" th:if="${pageNumber} == ${page.getNumber()+1}">
                        <a class="page-link" th:text="${pageNumber}" href="#"></a>
                    </li>
                    <li class="page-item disabled" th:if="${pageNumber} == -1">
                        <a class="page-link" href="#">...</a>
                    </li>
                    <li class="page-item" th:if="${pageNumber} != -1 and ${pageNumber} != ${page.getNumber()+1}">
                        <a class="page-link" th:text="${pageNumber}" th:href="${url} + '?page='+ ${pageNumber} + '&size=' + ${page.getSize()}"></a>
                    </li>
                    <li th:if="${pageNumberStat.last}" class="page-item"  aria-label="Next">
                        <a class="page-link" th:href="${url} + '?page='+ ${page.getNumber() + 2} + '&size=' + ${page.getSize()}">
                            <span aria-hidden="true">&raquo;</span>
                        </a>
                    </li>
                </th:block>
            </ul>
        

        我差点忘了最后一刻。这样索引不是从 0 开始,而是从 1 开始。在 MvcConfig 中:

            @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
            resolver.setOneIndexedParameters(true);
            resolvers.add(resolver);
            WebMvcConfigurer.super.addArgumentResolvers(resolvers);
        }
        

        【讨论】:

          【解决方案5】:

          你可以使用分页界面

          @GetMapping("/show-contacts/{page}") public String showContacts(@PathVariable("page") Integer page, Model model, Principal principal) {

              model.addAttribute("title", "Show Contacts");
              
              //have to send contacts list
              String name = principal.getName();
              User user = this.userRepository.getUserByUserName(name);
                      
              Pageable pageable = PageRequest.of(page, 5);
              
              Page<Contact> contacts = this.contactRepository.findContactsByUser(user.getId(), pageable);
          
              model.addAttribute("contacts", contacts);
              model.addAttribute("currentPage", page);
              model.addAttribute("totalPages", contacts.getTotalPages());
              
              return "user/show_contacts";
          }
          

          百里香

          1

          【讨论】:

            【解决方案6】:

            以下片段可用于项目中的任何模板,因为它使用 ServletUriComponentsBuilder 来计算每个页面的 URL,无需更改控制器。

            您只需要在您的 URL (/?page=X) 中提供页码并注入 Page 对象作为模板变量(这里用作 “分页”)。任何其他查询字符串参数将自动附加到所选页面。 定义为 th:with="max=3"ma​​x 变量提供了在选定页面之前和之后应该显示多少页面的信息。

            示例截图:

            <th:block th:fragment="paginationLinks(pagination)" th:with="urlBuilder=${T(org.springframework.web.servlet.support.ServletUriComponentsBuilder)}">
                <nav aria-label="page navigation" th:if="${pagination.totalPages>1}" th:with="max=3">
                    <ul class="pagination justify-content-center">
                        <li class="page-item" th:if="${pagination.hasPrevious()}">
                            <a class="page-link"
                               th:href="@{${urlBuilder.fromCurrentRequest().replaceQueryParam('page', pagination.number - 1).toUriString()}}"><i class="mdi mdi-arrow-left"></i></a>
                        </li>
                        <li class="page-item" th:each="page: ${#numbers.sequence(0, pagination.totalPages-1)}"
                            th:if="${pagination.number} >= ${page - max} and ${pagination.number} <= ${page + max} or ${page} == ${pagination.totalPages - 1} or ${page} == 0"
                            th:classappend="${page} == ${pagination.number} ? 'active'">
                            <span class="page-link" th:if="${pagination.number} == ${page + max} and ${page != 0}">...</span>
                            <a class="page-link"
                               th:if="${pagination.number} > ${page - max} and ${pagination.number} < ${page + max} or ${page} == ${pagination.totalPages - 1} or ${page} == 0"
                               th:href="@{${urlBuilder.fromCurrentRequest().replaceQueryParam('page', page).toUriString()}}" th:text="${page+1}">1</a>
                            <span class="page-link" th:if="${pagination.number} == ${page - max} and ${page} != ${pagination.totalPages - 1}">...</span>
                        </li>
                        <li class="page-item" th:if="${pagination.hasNext()}">
                            <a class="page-link"
                               th:href="@{${urlBuilder.fromCurrentRequest().replaceQueryParam('page', pagination.number + 1).toUriString()}}"><i class="mdi mdi-arrow-right"></i></a>
                        </li>
                    </ul>
                </nav>
            </th:block>
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-11-01
              • 2017-06-17
              • 2013-12-03
              • 2014-08-10
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多