Разбивка на страницы - сложная тема, если вы начнете думать и обсуждать ее. Темы, о которых вы можете не думать: что, если пользователь изменит URL-адрес и перейдет на страницу меньше 1 или пользователь войдет на страницу больше, чем максимальная страница, что, если есть большое количество страниц, должен ли я показывать каждый номер страницы , если я вернусь с другой страницы, вы не хотите перезагружать данные, пользователь должен увидеть то, что он видел ранее,…

Задав себе все эти вопросы, мы пришли к выводу, что использование управления состоянием может помочь нам в решении этих проблем. Решение в angular - использовать @ ngrx / store. Звучит неплохо, но как мы это реализуем?

Начните с основ

Создайте маршруты для пагинации

Наш путь по умолчанию перенаправлен на первую страницу.

Создайте компонент нумерации страниц

Одна из первых вещей, которые нам нужны для разбивки на страницы, - это сам компонент страницы. Он будет иметь входной объект Page, он будет содержать параметр текущей страницы и общий размер страниц. Для этого мы всегда будем начинать с логики, согласно которой 1 - это первая страница. Также предоставляется цель, потому что наша маршрутизация основана на URL-адресе.

Это логика для ограничения размера разбивки на страницы. Максимальное количество отображаемых страниц - 7. Всегда будут отображаться первый и последний номер. Если активна первая или последняя страница, будут показаны 2 цифры до или после.
Если выбрана другая страница, будут показаны 2 окружающих цифры.

Полный компонент разбивки на страницы:

Мы можем провести простой тест с нашим компонентом разбивки на страницы.

Что в итоге получится:

Создайте управление состоянием для разбивки на страницы

Состояния доступны только для чтения. Это один из основных принципов использования ngrx / store. Таким образом, у нас всегда есть одно или несколько действий для состояния и редуктор, который переводит действия в новое состояние. В этой статье я не буду рассказывать, как мы используем управление состоянием, для этого требуются некоторые базовые знания.

Для нашей логики разбивки на страницы каждый контейнер имеет собственное состояние страницы и состояние данных. Это означает, что эту логику необходимо реализовать для каждого контейнера.

Мы начнем с нашего поискового контейнера.

ApplicationState - это глобальное состояние нашего приложения. Мы могли бы добавить сюда также dataState, AuthenticationState,…. В зависимости от потребностей нашего приложения.

ContainerState - это набор состояний контейнера. Каждый контейнер, которому требуется состояние, будет иметь отдельное состояние контейнера.

SearchState - это особое состояние для searchContainer. На данный момент у нас есть только страница и фильмы, которые соответствуют правильной странице здесь. Но мы могли бы добавить намного больше, например searchTerm, loading,…

Как упоминалось ранее, мы заявляем, что доступны только для чтения. Поэтому нам нужны некоторые действия для их обновления. Мы определяем действие SetSearch, которое будет обновлять фильмы и страницу в состоянии. Это обновление будет выполнено с помощью search-reducer.

Редуктор поиска будет использоваться для обновления состояния. Фактически это создаст новый экземпляр состояния. Потому что состояние неизменяемо или доступно только для чтения.

Не забудьте зарегистрировать редуктор!

Внедрите службу поиска

Для этого приложения мы используем themoviedb как сервис отдыха. Мы хотим искать фильмы. Наш api-ключ определен в файле окружения.

Это не служба ракетостроения, это просто http-вызов.

Но особенность этого вызова заключается в том, что мы трансформируем наш запрос так, чтобы он соответствовал нашему предопределенному объекту страницы. Это ответственность службы. Все, что не соответствует объектам нашего приложения, мы преобразуем в сервисе. Это также может быть, например, даты, которые мы возвращаем в виде строк и хотим сопоставить. Объекты, которые отображаются в виде плоских строк,…

Создайте песочницу для разбивки на страницы

Я предпочитаю работать с песочницей. Это архитектурное решение, позволяющее убедиться, что ваш компонент не передается оттуда, откуда поступает информация. Просто скажите, дайте мне список фильмов и на какой это странице. Если он поступает из магазина - отлично, если он поступает напрямую от внутреннего вызова - тоже отлично. Компоненту действительно все равно! Этот метод также основан на разделении проблем.

Здесь будет много логики.

Будем слушать на pageNumber $. Событие, пришедшее извне магазина, будет запущено для нашего pageNumber $ subject. Он проверит текущую страницу в магазине, если страница отличается. Если страница новая, данные должны быть загружены. Также предоставляется значение по умолчанию: 1. ОтдельныйUntilChanged необходим для захвата большого количества пожаров для объекта pageNumber $ с тем же номером. Мы только хотим уведомить слушателей, если число действительно изменило значение.

Далее идет фильм $ наблюдаемый. Это будет прослушивать pageNumberChanged $, если указан новый номер. Он выполнит внутренний вызов (с фиксированным поисковым запросом). Обратите внимание на switchMap, это дает возможность отменить запрос, если запущен новый. Если в магазине было доступное значение, оно будет объединено в этот вызов. Слияние будет выполнено до того, как будет запущено новое событие.

Используйте searchSandbox в компоненте контейнера

Глупые и умные компоненты, вам обязательно стоит углубиться в эти концепции. В приложении, которое я создаю или помогаю в разработке, я хочу провести различие между ними, чтобы сразу увидеть, глупое оно или умное. Мои интеллектуальные компоненты всегда являются компонентами контейнера. Мои глупые компоненты всегда являются просто компонентом.
Таким образом, поисковый компонент - это мой умный компонент, а компонент поиска и пагинации - оба немые компоненты.

Контейнер поиска отвечает за смену страниц. В этом случае был сделан выбор добавить страницу в маршрутизатор, если мы обновим страницу, чем вернемся на ту же страницу. При этом следует учитывать, что нам нужно прослушивать параметры params. Каждый раз, когда параметр страницы изменяется, мы проверяем, правильный ли он параметр. Если он пуст или меньше 1, мы перенаправляем на первую страницу. Если запрашиваемый pageNumber больше totalPages, он будет перенаправлен на последнюю страницу.

Повторно используйте эту логику во всем приложении, где вы используете разбиение на страницы

Действия, которые необходимо предпринять для повторного использования этой логики в вашем приложении

  1. Создать новое состояние контейнера
  2. Создать новое действие
  3. Создать новый редуктор
  4. Добавить редуктор
  5. Создать сервис
  6. Создайте песочницу
  7. Создайте контейнер

Внимание

использовать один объект-контейнер для каждого контейнера

Используемые технологии

  • угловатый
  • @ ngrx / магазин
  • rxjs

Рабочий пример