我正在使用Spring + Thymeleaf开发一个简单的应用程序。在其中一个页面上,我有一个需要分页的项目列表。
理想情况下,我只想将currPageNo
(当前页面的编号)和numOfPages
(总页数)变量发送到视图,其余的工作将完成在那里(这是一个演示问题,与业务逻辑无关)。但是,如果最干净的解决方案要求我先在控制器中进行一些计算,我会接受它作为一个小恶魔。
我想以下列形式获取页面列表。
<Prev | 1 | ... | 3 | 4 | 5 | 6 | 7 | ... | 15 | Next>
我只能使用以下解决方案。它有效,但我相信你会同意它非常混乱并且很难阅读。
此外,除了currPageNo
和numOfPages
之外,我还必须向视图发送另外两个变量。理想的解决方案不需要我这样做。
firstPageNo = Math.max(2, currPageNo - 2)
lastPageNo = Math.min(numOfPages - 1, currPageNo + 2)
我的代码的当前版本如下。
<ul>
<li th:if="${currPageNo > 1}">
<a th:href="@{/items.html(pageNo = ${currPageNo - 1})}" href="">< Prev</a>
</li>
<li th:class="${currPageNo == 1} ? 'selected'">
<a th:href="@{/items.html(pageNo = 1)}" th:if="${currPageNo > 1}" href="">1</a>
<span th:if="${currPageNo == 1}">1</span>
</li>
<li th:if="${currPageNo >= 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 <= (numOfPages - 4)}">
...
</li>
<li th:class="${currPageNo == numOfPages} ? 'selected'">
<a th:href="@{/items.html(pageNo = ${numOfPages})}" th:if="${currPageNo < numOfPages}" th:text="${numOfPages}" href="">10</a>
<span th:if="${currPageNo == numOfPages}" th:text="${numOfPages}">1</span>
</li>
<li th:if="${currPageNo < numOfPages}">
<a th:href="@{/items.html(pageNo = ${currPageNo + 1})}" href=""> Next ></a>
</li>
</ul>
以下列表总结了我想要摆脱的问题。据我所知,其中一些是平台所固有的,但是,这个列表似乎很长,而且代码很乱。
firstPageNo
和lastPageNo
的预先计算值。<
而不是<
。我也欢迎任何其他有关如何提高代码质量的建议。
我理解也许这个问题可能更适合Code Review网站,但是,由于Thymeleaf似乎是一个拥有微小用户群的技术,我希望在Stack Overflow上有一个合理的答案,它有一个更多的用户群(我相信)。
但是,如果在这里真的不欢迎这样的问题,请考虑将其移至正确的网站而不是关闭它,以便我得到我需要的建议。
答案 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}}"><<</a>
<a th:if="${startpage eq 0}" href="javascript:void(0);"><<</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}}">>></a>
<a th:if="${(endpage*elementsperpage) le (page2th.TotalElements)}" href="javascript:void(0);"></a>
</li>
</ul>
</div>
</div>
答案 3 :(得分: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">«</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">»</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