Пагинация с весенним REST и таблицей AngularJS
1. Обзор
В этой статье мы сосредоточимся в основном на реализации pagination стороны сервера в Весенний REST API и простой передний конец AngularJS.
Мы также исследуем широко используемую сетку таблицы в Angular под названием UI Grid .
2. Зависимости
Здесь мы подробно различные зависимости, которые необходимы для этой статьи.
2.1. JavaScript
Для того, чтобы Angular UI Grid работал, нам понадобится ниже скрипты, импортированные в нашем HTML.
2.2. Мавен
Для нашего бэкэнда мы будем использовать Весенняя загрузка , так что нам понадобится ниже зависимостей:
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-tomcat provided
Примечание: Другие зависимости не были указаны здесь, для полного списка, проверьте полный пом.xml в GitHub проект.
3. О заявке
Приложение является простым студенческим каталогом приложение, которое позволяет пользователям видеть детали студента в paginated таблице сетки.
Приложение использует Весенняя загрузка и работает на встроенном сервере Tomcat со встроенной базой данных.
Наконец, на стороне API вещей, Есть несколько способов сделать pagination, описанные в REST Pagination весной статьи здесь – что настоятельно рекомендуется чтение в сочетании с этой статьей.
Наше решение здесь простое – иметь информацию о paging в запросе URI следующим образом: /студент/получить?page-размер 2 .
4. Клиентская сторона
Во-первых, нам нужно создать логику на стороне клиента.
4.1. UI-Grid
Наша индекс.html будет иметь импорт нам нужно и простое осуществление таблицы сетки:
Давайте более подробно рассмотрим код:
- ng-app – это угловая директива, которая загружает модуль приложение . Все элементы под ним будут частью приложение модуль
- ng-контроллер – это угловая директива, которая загружает контроллер СтуденческаяКтрл с псевдонимом vm. Все элементы под ним будут частью СтуденческаяКтрл контроллер
- ui-сетка – это угловая директива, которая принадлежит Angular ui-сетка и использует gridOptions в качестве настроек по умолчанию, gridOptions объявляется в соответствии с $scope в приложение.js
4.2. Модуль AngularJS
Давайте сначала определим модуль в приложение.js :
var app = angular.module('app', ['ui.grid','ui.grid.pagination']);
Мы объявили приложение модуль, и мы ввели ui.grid для включения функциональности UI-Grid; мы также вводили ui.grid.pagination для поддержки pagination.
Далее мы определим контроллер:
app.controller('StudentCtrl', ['$scope','StudentService', function ($scope, StudentService) { var paginationOptions = { pageNumber: 1, pageSize: 5, sort: null }; StudentService.getStudents( paginationOptions.pageNumber, paginationOptions.pageSize).success(function(data){ $scope.gridOptions.data = data.content; $scope.gridOptions.totalItems = data.totalElements; }); $scope.gridOptions = { paginationPageSizes: [5, 10, 20], paginationPageSize: paginationOptions.pageSize, enableColumnMenus:false, useExternalPagination: true, columnDefs: [ { name: 'id' }, { name: 'name' }, { name: 'gender' }, { name: 'age' } ], onRegisterApi: function(gridApi) { $scope.gridApi = gridApi; gridApi.pagination.on.paginationChanged( $scope, function (newPage, pageSize) { paginationOptions.pageNumber = newPage; paginationOptions.pageSize = pageSize; StudentService.getStudents(newPage,pageSize) .success(function(data){ $scope.gridOptions.data = data.content; $scope.gridOptions.totalItems = data.totalElements; }); }); } }; }]);
Давайте теперь посмотрим на пользовательские настройки pagination в $scope.gridOptions :
- paginationPageSizes – определяет доступные параметры размера страницы
- paginationPageSize – определяет размер страницы по умолчанию
- включитьКолумнМенус – используется для включения/отключения меню в столбцах
- использованиеИстерналПагинация – требуется, если вы paginating на стороне сервера
- колонкаДефы — имена столбца, которые будут автоматически отображены на объект JSON, возвращенный с сервера. Имена поля в объекте JSON, возвращенные с сервера, и определенное имя столбца должны соответствовать.
- onRegisterApi – возможность регистрации публичных методов событий внутри сетки. Здесь мы зарегистрировали gridApi.pagination.on.paginationИзмеленные чтобы сообщить UI-Grid, чтобы вызвать эту функцию всякий раз, когда страница была изменена.
И отправить запрос в API:
app.service('StudentService',['$http', function ($http) { function getStudents(pageNumber,size) { pageNumber = pageNumber > 0?pageNumber - 1:0; return $http({ method: 'GET', url: 'student/get?page='+pageNumber+'&size='+size }); } return { getStudents: getStudents }; }]);
5. Бэкэнд и API
5.1. Служба RESTful
Вот простая реализация API RESTful с поддержкой pagination:
@RestController public class StudentDirectoryRestController { @Autowired private StudentService service; @RequestMapping( value = "/student/get", params = { "page", "size" }, method = RequestMethod.GET ) public PagefindPaginated( @RequestParam("page") int page, @RequestParam("size") int size) { Page resultPage = service.findPaginated(page, size); if (page > resultPage.getTotalPages()) { throw new MyResourceNotFoundException(); } return resultPage; } }
@RestController была введена весной 4.0 как удобная аннотация, которая неявно @Controller и @ResponseBody.
Для нашего API, мы объявили его принять два параметра, которые страница и размер, который также будет определять количество записей, чтобы вернуться к клиенту.
Мы также добавили простую проверку, которая бросит MyResourceNotFoundException если число страниц превышает общее количество страниц.
Наконец, мы вернемся Страница как ответ – это супер полезный компонент S pring Данные который провел pagination данных.
5.2. Внедрение службы
Наш сервис просто вернет записи на основе страницы и размера, предоставленных контроллером:
@Service public class StudentServiceImpl implements StudentService { @Autowired private StudentRepository dao; @Override public PagefindPaginated(int page, int size) { return dao.findAll(new PageRequest(page, size)); } }
5.3. Реализация репозитория
Для нашего уровня настойчивости мы используем встроенную базу данных и Spring Data JPA.
Во-первых, мы должны настроить нашу настойчивость config:
@EnableJpaRepositories("com.baeldung.web.dao") @ComponentScan(basePackages = { "com.baeldung.web" }) @EntityScan("com.baeldung.web.entity") @Configuration public class PersistenceConfig { @Bean public JdbcTemplate getJdbcTemplate() { return new JdbcTemplate(dataSource()); } @Bean public DataSource dataSource() { EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); EmbeddedDatabase db = builder .setType(EmbeddedDatabaseType.HSQL) .addScript("db/sql/data.sql") .build(); return db; } }
Настойчивость config проста – у нас есть @EnableJpaRepositories сканировать указанный пакет и найти наши интерфейсы репозитория Spring Data JPA.
У нас есть @ComponentScan здесь, чтобы автоматически сканировать для всех бобов, и мы есть @EntityScan (от Spring Boot) для сканирования классов сущностей.
Мы также объявили наш простой источник данных – с помощью встроенной базы данных, которая будет работать скрипт S’L, предусмотренный при запуске.
Теперь пришло время создать наш репозиторий данных:
public interface StudentRepository extends JpaRepository{}
Это в основном все, что нам нужно сделать здесь; если вы хотите пойти глубже в том, как настроить и использовать очень мощный Весенние данные JPA, безусловно, читать руководство к нему здесь .
6. Запрос и ответ на вопросы о палации
При вызове API – , JSON ответ будет выглядеть что-то вроде этого:
{ "content":[ {"studentId":"1","name":"Bryan","gender":"Male","age":20}, {"studentId":"2","name":"Ben","gender":"Male","age":22}, {"studentId":"3","name":"Lisa","gender":"Female","age":24}, {"studentId":"4","name":"Sarah","gender":"Female","age":26}, {"studentId":"5","name":"Jay","gender":"Male","age":20} ], "last":false, "totalElements":20, "totalPages":4, "size":5, "number":0, "sort":null, "first":true, "numberOfElements":5 }
Одна вещь, чтобы заметить здесь, что сервер возвращает org.springframework.data.domain.Page DTO, упаковка наших Студенческие ресурсы.
Страница объект будет иметь следующие поля:
- последний – установлен на истинное если это последняя страница в противном случае ложные
- первый – установлен на истинное если это первая страница в противном случае ложные
- totalElements – общее количество строк/записей. В нашем примере мы передали это ui-сетка варианты $scope.gridOptions.totalItems определить, сколько страниц будет доступно
- totalPages – общее количество страниц, которые были получены из ( totalElements/размер )
- размер – количество записей на страницу, это было передано от клиента через param размер
- номер – номер страницы, отправленный клиентом, в нашем ответе номер 0, потому что в нашем бэкэнде мы используем массив Студенческие s, который является нулевым индексом, так что в нашем бэкэнде, мы decrement номер страницы на 1
- сортировать – параметр сортировки страницы
- numberOfElements – количество строк/записей для страницы
7. Тестирование pagination
Теперь давайте навеем тест на нашу логику pagination, используя RestAssured ; чтобы узнать больше о RestAssured Вы можете взглянуть на эту учебный .
7.1. Подготовка теста
Для удобства развития нашего тестового класса мы добавим статический импорт:
io.restassured.RestAssured.* io.restassured.matcher.RestAssuredMatchers.* org.hamcrest.Matchers.*
Далее мы наберем тест с поддержкой Spring:
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = Application.class) @WebAppConfiguration @IntegrationTest("server.port:8888")
@SpringApplicationConfiguration помогает Весна знать, как загрузить ПриложениеКонтекст, в этом случае мы использовали Применение.java настроить наши ПриложениеКонтекст.
@WebAppConfiguration был определен, чтобы сказать Весна, что ПриложениеКонтекст загрузка должна быть WebApplicationКонтекст.
И @IntegrationTest был определен, чтобы вызвать запуск приложения при запуске теста, это делает наши услуги REST доступными для тестирования.
7.2. Тесты
Вот наш первый тестовый случай:
@Test public void givenRequestForStudents_whenPageIsOne_expectContainsNames() { given().params("page", "0", "size", "2").get(ENDPOINT) .then() .assertThat().body("content.name", hasItems("Bryan", "Ben")); }
Этот тестовый случай выше, чтобы проверить, что при странице 1 и размер 2 передается в службу REST JSON содержание возвращается с сервера должны иметь имена Брайан и Бен.
Давайте вскроем тестовый случай:
- учитывая – часть RestAssured и используется для начала построения запроса, вы также можете использовать с ()
- получить – часть RestAssured и если используется триггеры получить запрос, используйте пост () для запроса на почту
- имеетItems – часть hamcrest, который проверяет, если значения имеют какой-либо матч
Добавим еще несколько тестовых случаев:
@Test public void givenRequestForStudents_whenResourcesAreRetrievedPaged_thenExpect200() { given().params("page", "0", "size", "2").get(ENDPOINT) .then() .statusCode(200); }
Этот тест утверждает, что, когда точка на самом деле называется OK ответ получен:
@Test public void givenRequestForStudents_whenSizeIsTwo_expectNumberOfElementsTwo() { given().params("page", "0", "size", "2").get(ENDPOINT) .then() .assertThat().body("numberOfElements", equalTo(2)); }
Этот тест утверждает, что при запрашиваемом размере страницы в два страницы размер возвращен на самом деле составляет два:
@Test public void givenResourcesExist_whenFirstPageIsRetrieved_thenPageContainsResources() { given().params("page", "0", "size", "2").get(ENDPOINT) .then() .assertThat().body("first", equalTo(true)); }
Этот тест утверждает, что при первом использовании ресурсов значение имени первой страницы верно.
Есть еще много тестов в репозитории, так что определенно посмотрите на GitHub проект.
8. Заключение
Эта статья проиллюстрировала, как реализовать сетку таблиц данных с помощью UI-Grid в AngularJS и как реализовать требуемую pagination стороны сервера.
Реализация этих примеров и тестов можно найти в Проект GitHub . Это проект Maven, поэтому он должен быть легким для импорта и запуска, как она есть.
Чтобы запустить проект загрузки Spring, вы можете просто сделать mvn весна-загрузка:бегите и получить доступ к нему локально http://localhost:8080/.