Spring + Thymeleaf - как реализовать разбиение на страницы для списка
Я разрабатываю простое приложение с помощью 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, но, поскольку Тимелеаф, похоже, является технологией с крошечной базой пользователей, я ожидаю разумного ответа, скорее, на Stack Overflow, который имеет гораздо больше пользовательской базы (я считаю).
Если, однако, такой вопрос действительно не приветствуется здесь, подумайте о том, чтобы переместить его на нужный сайт, а не закрывать его, чтобы я получил совет, который мне нужен.
Ответы
Ответ 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>
Ответ 2
Другим вариантом будет решение Бен Тёрли. Мы внедрили его, и он работает плавно: 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}}"><<</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>