Виктор Савкин - соучредитель nrwl.io, предоставляющий Angular консалтинг корпоративным командам. Ранее он входил в основную команду Angular в Google и создавал модули внедрения зависимостей, обнаружения изменений, форм и маршрутизатора.

Приложение Angular - это дерево компонентов. Некоторые из этих компонентов являются повторно используемыми компонентами пользовательского интерфейса (например, список, таблица), а некоторые - компонентами приложения, которые представляют экраны или некоторые логические части приложения. Маршрутизатор заботится о компонентах приложения или, точнее, об их расположении. Назовем такие компоновки компонентов состояниями маршрутизатора. Другими словами, состояние маршрутизатора - это набор компонентов приложения, который определяет, что отображается на экране.

В этой статье мы подробно рассмотрим RouterState.

Эта статья основана на книге Angular Router, которую вы можете найти здесь https://leanpub.com/router. Книга выходит за рамки руководства по началу работы и подробно рассказывает о маршрутизаторе. Мысленная модель, ограничения дизайна и тонкости API - все покрыто. Если вам понравилась статья, загляните в книгу!

Во время навигации после применения перенаправления маршрутизатор создает RouterStateSnapshot. Что такое RouterStateSnapshot и чем он отличается от RouterState?

RouteStateSnapshot - это неизменяемая структура данных, представляющая состояние маршрутизатора в определенный момент времени. Каждый раз, когда добавляется или удаляется компонент или обновляется параметр, создается новый снимок.

RouterState похож на RouteStateSnapshot, за исключением того, что он представляет состояние маршрутизатора, меняющееся с течением времени.

Как видите, RouterStateSnapshot - это дерево активированных снимков маршрута. Каждый узел в этом дереве знает о «потребленных» сегментах URL, извлеченных параметрах и разрешенных данных. Чтобы было понятнее, давайте посмотрим на этот пример:

Когда мы переходим к «/ inbox / 33 / messages / 44», маршрутизатор просматривает URL-адрес и создает следующий RouterStateSnapshot:

После этого маршрутизатор создаст экземпляр ConversationCmp с MessageCmp в нем.

Теперь представьте, что мы переходим к другому URL-адресу: «/ inbox / 33 / messages / 45», что приведет к следующему снимку:

Чтобы избежать ненужных модификаций DOM, маршрутизатор будет повторно использовать компоненты при изменении параметров соответствующих маршрутов. В этом примере параметр id компонента сообщения изменился с 44 на 45. Это означает, что мы не может просто внедрить ActivatedRouteSnapshot в MessageCmp, потому что моментальный снимок всегда будет иметь параметр id, равный 44, т. е. он станет устаревшим.

Снимок состояния маршрутизатора представляет состояние приложения в определенный момент времени, отсюда и название «снимок». Но компоненты могут оставаться активными в течение нескольких часов, и данные, которые они показывают, могут меняться. Таким образом, наличие только снимков не поможет - нам нужна структура данных, которая позволит нам иметь дело с изменениями.

RouterState и ActivatedRoute аналогичны своим аналогам из моментальных снимков, за исключением того, что они предоставляют все значения как наблюдаемые, что отлично подходит для работы со значениями, изменяющимися с течением времени.

Любой компонент, созданный маршрутизатором, может внедрить свой ActivatedRoute.

Если мы перейдем от «/ inbox / 33 / messages / 44» к «/ inbox / 33 / messages / 45», наблюдаемый объект данных выдаст новый набор данных с новым объектом сообщения, и компонент отобразит сообщение 45.

Маршрутизатор предоставляет параметры и данные как наблюдаемые, что удобно в большинстве случаев, но не всегда. Иногда нам нужен снимок состояния, который мы можем сразу изучить.

ActivatedRoute обеспечивает доступ к url, params, data, queryParams и наблюдаемым фрагментам. Мы подробно рассмотрим каждый из них, но сначала давайте рассмотрим отношения между ними.

Изменения URL-адресов являются источником любых изменений в маршруте. И это должно быть так, поскольку пользователь имеет возможность напрямую изменять местоположение.

Каждый раз при изменении URL-адреса маршрутизатор извлекает из него новый набор параметров: маршрутизатор принимает позиционные параметры (например, ': id') совпадающих сегментов URL-адреса и матричные параметры последних сопоставлен сегмент URL и объединяет их. Это чистая операция: URL-адрес должен измениться, чтобы параметры изменились. Или, другими словами, один и тот же URL всегда будет приводить к одному и тому же набору параметров.

Затем маршрутизатор вызывает преобразователи данных маршрута и объединяет результат с предоставленными статическими данными. Поскольку преобразователи данных являются произвольными функциями, маршрутизатор не может гарантировать, что вы получите тот же объект при указании того же URL. Более того, часто это не так! URL-адрес содержит идентификатор ресурса, который является фиксированным, и преобразователи данных извлекают содержимое этого ресурса, которое часто меняется со временем.

Наконец, активированный маршрут предоставляет наблюдаемые queryParams и фрагменты. В отличие от других наблюдаемых, которые привязаны к определенному маршруту, параметры запроса и фрагмент используются для нескольких маршрутов.

Учитывая следующее:

И перейдя сначала к «/ inbox / 33 / messages / 44», а затем к «/ inbox / 33 / messages / 45», мы увидим:

url [{path: ‘messages’, params: {}}, {path: ‘44’, params: {}}]
url [{path: ‘messages’, params: {}}, {path: ‘45’, params: {}}]

Мы не часто прислушиваемся к изменениям URL-адресов, поскольку они находятся на слишком низком уровне. Один из практических вариантов использования - активация компонента по подстановочному маршруту. Поскольку в этом случае массив сегментов URL-адреса не является фиксированным, может быть полезно изучить его, чтобы показать пользователю разные данные.

Учитывая следующее:

И при переходе сначала к «/ inbox / 33 / messages; a = 1/44; b = 1», а затем к «/ inbox / 33 / messages; a = 2/45; b = 2» мы увидим

params {id: ‘44’, b: ‘1’}
params {id: ‘45’, b: ‘2’}

Прежде всего следует отметить, что параметр id - это строка (при работе с URL-адресами мы всегда работаем со строками). Во-вторых, маршрут получает только матричные параметры своего последнего сегмента URL. Поэтому параметра «а» нет.

Давайте настроим конфигурацию сверху, чтобы увидеть, как работают наблюдаемые данные.

Где MessageResolver определяется следующим образом:

Свойство data используется для передачи фиксированного объекта активированному маршруту. Оно не изменяется в течение всего времени существования приложения. Свойство разрешения используется для динамических данных.

Обратите внимание, что в конфигурации выше строка «message: MessageResolver» не сообщает маршрутизатору о необходимости создания резолвера. Он указывает маршрутизатору получить его с помощью внедрения зависимости. Это означает, что вам нужно где-то зарегистрировать MessageResolver в списке провайдеров.

Как только маршрутизатор получит преобразователь, он вызовет на нем метод «resolve». Метод может возвращать обещание, наблюдаемый или любой другой объект. Если возвращаемое значение является обещанием или наблюдаемым, маршрутизатор будет ждать завершения этого обещания или наблюдаемого, прежде чем продолжить активацию.

Сопоставитель не обязательно должен быть классом, реализующим интерфейс `Resolve`. Также это может быть функция:

Маршрутизатор объединяет разрешенные и статические данные в одно свойство, к которому вы можете получить доступ следующим образом:

При переходе сначала к «/ inbox / 33 / message / 44», а затем к «/ inbox / 33 / messages / 45» мы увидим

data {allowReplyAll: true, message: {id: 44, title: ‘Rx Rocks’, …}}
data {allowReplyAll: true, message: {id: 45, title: ‘Angular Rocks’, …}}

В отличие от других наблюдаемых, которые привязаны к определенному маршруту, параметры запроса и фрагмент используются для нескольких маршрутов.

Если вам это понравилось, нажмите ниже, чтобы другие люди увидели это здесь, на Medium. Подпишитесь на @victorsavkin, чтобы узнать больше об Angular.

Первоначально опубликовано на сайте vsavkin.com 31 октября 2016 г.