16

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

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

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

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

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

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

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 上得到一个合理的答案,它有更多的用户基地(我相信)。

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

4

6 回答 6

21

类似于 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>
于 2014-04-30T16:26:06.793 回答
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;
    }
}
于 2013-09-20T10:55:52.367 回答
0

这是我的解决方案。结果,它看起来像这样在此处输入图像描述

控制器: 在此处输入图像描述

 @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);
}
于 2021-01-28T10:50:08.237 回答
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
于 2021-07-13T17:29:28.253 回答
0

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

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

示例截图:

<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>
于 2021-09-17T13:40:27.903 回答
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>
于 2017-02-28T09:18:57.710 回答