Twitter Bootstrap имеет очень приятный пользовательский интерфейс для разбивки на страницы , и здесь я покажу вам, как реализовать его с помощью функции нумерации веб-страниц Spring Data и функций условной оценки Thymeleaf .
Стандартная нумерация страниц в Bootstrap
Простая нумерация страниц, вдохновленная Rdio, отлично подходит для приложений и результатов поиска. Большой блок трудно пропустить, он легко масштабируется и обеспечивает большие области щелчков.
Исходный исходный код для отображения нумерации страниц из документа Bootstrap очень прост:
|
01
02
03
04
05
06
07
08
09
10
11
|
<div class='pagination'> <ul> <li><a href='#'>Prev</a></li> <li><a href='#'>1</a></li> <li><a href='#'>2</a></li> <li><a href='#'>3</a></li> <li><a href='#'>4</a></li> <li><a href='#'>5</a></li> <li><a href='#'>Next</a></li> </ul></div> |
Вы можете видеть, что это всего лишь макет кода, и чтобы он динамически отображал номер страницы с правильным URL-адресом гиперссылки, мне нужно сделать много изменений в моем существующем коде. Итак, давайте начнем снизу вверх, сначала изменим уровень домена, затем уровень обслуживания приложений, уровень представления. Наконец, конфигурация, чтобы склеить их.
Изменения уровня домена
Единственное изменение в доменном слое — в BlogPostRepository . Прежде, чем у него есть метод для получения списка опубликованных BlogPost отсортированных по publishedTime :
|
1
2
3
4
5
|
public interface BlogPostRepository extends MongoRepository<BlogPost, String>{... List<BlogPost> findByPublishedIsTrueOrderByPublishedTimeDesc();...} |
Теперь нам нужно получить список результатов, разбитых на страницы. На странице данных Spring мы вернем Page<BlogPost> вместо List<BlogPost> и Pageable параметр Pageable :
|
1
2
3
4
5
|
public interface BlogPostRepository extends MongoRepository<BlogPost, String>{... Page<BlogPost> findByPublishedIsTrueOrderByPublishedTimeDesc(Pageable pageable);...} |
Изменения уровня обслуживания приложений:
Смена уровня приложения также очень проста, просто используя новую функцию из BlogPostRepository :
Интерфейс BlogService
|
1
2
3
4
5
|
public interface BlogService {... Page<BlogPost> getAllPublishedPosts(Pageable pageable);...} |
Класс BlogServiceImpl
|
01
02
03
04
05
06
07
08
09
10
11
|
public class BlogServiceImpl implements BlogService {... private final BlogPostRepository blogPostRepository;... @Override public Page<BlogPost> getAllPublishedPosts(Pageable pageable) { Page<BlogPost> blogList = blogPostRepository.findByPublishedIsTrueOrderByPublishedTimeDesc(pageable); return blogList; }...} |
Изменения в уровне презентации:
Интерфейс Spring Data Page имеет много приятных функций для получения номера текущей страницы, получения общего количества страниц и т. Д. Но по-прежнему не хватает способов, позволяющих мне отображать только частичный диапазон страниц с общей нумерацией страниц. Поэтому я создал класс адаптера, чтобы обернуть интерфейс страницы данных Sprng дополнительными функциями.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
public class PageWrapper<T> { public static final int MAX_PAGE_ITEM_DISPLAY = 5; private Page<T> page; private List<PageItem> items; private int currentNumber; private String url; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public PageWrapper(Page<T> page, String url){ this.page = page; this.url = url; items = new ArrayList<PageItem>(); currentNumber = page.getNumber() + 1; //start from 1 to match page.page int start, size; if (page.getTotalPages() <= MAX_PAGE_ITEM_DISPLAY){ start = 1; size = page.getTotalPages(); } else { if (currentNumber <= MAX_PAGE_ITEM_DISPLAY - MAX_PAGE_ITEM_DISPLAY/2){ start = 1; size = MAX_PAGE_ITEM_DISPLAY; } else if (currentNumber >= page.getTotalPages() - MAX_PAGE_ITEM_DISPLAY/2){ start = page.getTotalPages() - MAX_PAGE_ITEM_DISPLAY + 1; size = MAX_PAGE_ITEM_DISPLAY; } else { start = currentNumber - MAX_PAGE_ITEM_DISPLAY/2; size = MAX_PAGE_ITEM_DISPLAY; } } for (int i = 0; i<size; i++){ items.add(new PageItem(start+i, (start+i)==currentNumber)); } } public List<PageItem> getItems(){ return items; } public int getNumber(){ return currentNumber; } public List<T> getContent(){ return page.getContent(); } public int getSize(){ return page.getSize(); } public int getTotalPages(){ return page.getTotalPages(); } public boolean isFirstPage(){ return page.isFirstPage(); } public boolean isLastPage(){ return page.isLastPage(); } public boolean isHasPreviousPage(){ return page.hasPreviousPage(); } public boolean isHasNextPage(){ return page.hasNextPage(); } public class PageItem { private int number; private boolean current; public PageItem(int number, boolean current){ this.number = number; this.current = current; } public int getNumber(){ return this.number; } public boolean isCurrent(){ return this.current; } }} |
С помощью этого PageWrapper мы можем обернуть Page<BlogPost> возвращенную из BlogService и поместить ее в модель пользовательского интерфейса SpringMVC. Смотрите код контроллера для страницы блога:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
@Controllerpublic class BlogController... @RequestMapping(value = '/blog', method = RequestMethod.GET) public String blog(Model uiModel, Pageable pageable) { PageWrapper<BlogPost> page = new PageWrapper<BlogPost> (blogService.getAllPublishedPosts(pageable), '/blog'); uiModel.addAttribute('page', page); return 'blog'; }...} |
Pageable передается из PageableArgumentResolver , что я объясню позже. Еще одна хитрость заключается в том, что я также PageWrapper URL представления в PageWrapper , и его можно использовать для создания гиперссылок Thymeleaf на панели разбивки на страницы.
Так как мой PageWrapper очень универсален, я создал HTML-фрагмент для панели разбивки на страницы, так что я могу использовать его в любом месте на страницах моего приложения, когда потребуется разбивка на страницы. Этот фрагмент HTML использует Thymeleaf- th:if динамически переключаться между статическим текстом или гиперссылкой в зависимости от того, отключена ссылка или нет. И он использует th:href для создания URL с правильным номером страницы и размером страницы.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<!-- Pagination Bar --><div th:fragment='paginationbar'> <div class='pagination pagination-centered'> <ul> <li th:class='${page.firstPage}? 'disabled' : '''> <span th:if='${page.firstPage}'>← First</span> <a th:if='${not page.firstPage}' th:href='@{${page.url}(page.page=1,page.size=${page.size})}'>← First</a> </li> <li th:class='${page.hasPreviousPage}? '' : 'disabled''> <span th:if='${not page.hasPreviousPage}'>«</span> <a th:if='${page.hasPreviousPage}' th:href='@{${page.url}(page.page=${page.number-1},page.size=${page.size})}' title='Go to previous page'>«</a> </li> <li th:each='item : ${page.items}' th:class='${item.current}? 'active' : '''> <span th:if='${item.current}' th:text='${item.number}'>1</span> <a th:if='${not item.current}' th:href='@{${page.url}(page.page=${item.number},page.size=${page.size})}'><span th:text='${item.number}'>1</span></a> </li> <li th:class='${page.hasNextPage}? '' : 'disabled''> <span th:if='${not page.hasNextPage}'>»</span> <a th:if='${page.hasNextPage}' th:href='@{${page.url}(page.page=${page.number+1},page.size=${page.size})}' title='Go to next page'>»</a> </li> <li th:class='${page.lastPage}? 'disabled' : '''> <span th:if='${page.lastPage}'>Last →</span> <a th:if='${not page.lastPage}' th:href='@{${page.url}(page.page=${page.totalPages},page.size=${page.size})}'>Last →</a> </li> </ul> </div></div> |
Изменение конфигурации Spring
Последний шаг — собрать их всех вместе. К счастью, я провел некоторое исследование, прежде чем обновил свой код. Даг Хабер написал очень хороший пост Pagination с Spring MVC, Spring Data и Java Config . В своем блоге Дуг упомянул несколько ошибок, особенно параметр Pageable нуждается в некоторой магии конфигурации:
Чтобы Spring знал, как преобразовать параметр в объект Pageable, вам необходимо настроить HandlerMethodArgumentResolver. Spring Data предоставляет PageableArgumentResolver, но использует старый интерфейс ArgumentResolver вместо нового (Spring 3.1) интерфейса HandlerMethodArgumentResolver. Конфигурация XML может справиться с этим расхождением для нас, но, поскольку мы используем Java Config, нам нужно сделать это немного больше вручную. К счастью, это может быть легко решено, если вы знаете правильное магическое заклинание …
Даг Хабер
С помощью Дуга я добавил этот преобразователь аргументов в свой класс WebConfig :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@Configuration@EnableWebMvc@ComponentScan(basePackages = 'com.jiwhiz.blog.web')public class WebConfig extends WebMvcConfigurerAdapter {... @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { PageableArgumentResolver resolver = new PageableArgumentResolver(); resolver.setFallbackPagable(new PageRequest(1, 5)); argumentResolvers.add(new ServletWebArgumentResolverAdapter(resolver)); }...} |
После всех этих изменений мой список блогов будет иметь панель нумерации страниц вверху и внизу страницы и всегда будет иметь не более 5 номеров страниц, с текущим номером посередине и отключенным. На панели разбиения на страницы также есть первая и предыдущая ссылки в начале, следующая и последняя ссылки в конце. Я также использовал его на своих страницах администратора, для списка пользователей и списка комментариев, и это работает очень хорошо.
Ссылка: Реализация нумерации страниц с помощью Spring Data и Thymeleaf от нашего партнера JCG Юаня Цзи в блоге Jiwhiz .