Spring + Thymeleaf - 如何实现列表的分页

时间:2013-08-28 14:30:24

标签: spring pagination thymeleaf

我正在使用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的预先计算值。
  • 必须在表达式中使用&lt;而不是<
  • 必须同时使用具有互斥条件的锚点和跨度,以便浏览器不使用当前页面的链接。

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


我理解也许这个问题可能更适合Code Review网站,但是,由于Thymeleaf似乎是一个拥有微小用户群的技术,我希望在Stack Overflow上有一个合理的答案,它有一个更多的用户群(我相信)。

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

5 个答案:

答案 0 :(得分:19)

与中描述的解决方案类似 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>

答案 1 :(得分: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;
    }
}

答案 2 :(得分:0)

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

<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>

答案 3 :(得分:0)

这是我的解决方案。 结果,它看起来像这样enter image description here

控制器: enter image description here

 @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);
}

答案 4 :(得分:0)

你可以使用分页界面

@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