Angular

Параметры, resolvers и guards

URL /users/42 должен открыть страницу пользователя с id 42. Но прежде чем компонент покажет данные, нужно решить несколько вопросов: имеет ли посетитель право сюда заходить, успели ли загрузиться данные пользователя, откуда компонент возьмёт число 42. Angular разносит эти задачи по специальным точкам жизненного цикла маршрута: guard решает, пускать ли, resolver грузит данные до показа компонента, а параметр маршрута прилетает прямо во вход компонента. Всё это - обычные функции, исполняемые в контексте инжектора.

  • Защита приватных разделов: guard CanActivateFn проверяет авторизацию и редиректит на логин, если сессии нет
  • Страницы товара и профиля: id из URL приходит во вход компонента через withComponentInputBinding
  • Предзагрузка данных: resolver подтягивает запись до отрисовки, чтобы компонент не мигал пустым состоянием
  • Ролевой доступ: route data хранит требуемую роль, а общий guard сверяет её с правами пользователя
  • Подтверждение ухода со страницы: CanDeactivateFn спрашивает про несохранённые изменения формы

Предварительные знания

  • Базовая маршрутизация: provideRouter, Routes, router-outlet, routerLink
  • Функция inject() и контекст инжектора
  • Сигналы и входы компонента через input()

Параметры маршрута через входы

Динамический сегмент в пути объявляется двоеточием: path: 'users/:id'. При переходе на /users/42 значение 42 становится доступным как параметр id. Классически его читали из ActivatedRoute через подписку. Современный способ короче: с фичей withComponentInputBinding параметр прилетает прямо во вход компонента с тем же именем.

withComponentInputBinding пробрасывает во входы не только параметры пути (:id), но и query-параметры (?tab=info), статические route data и результаты resolvers - всё по совпадению имени входа с ключом. Параметры пути всегда строки, число при необходимости приводится в самом компоненте.

Сделав id входом-сигналом, удобно построить производное состояние: computed на основе id() или resource, перезапрашивающий данные при смене id. Тогда переход с /users/42 на /users/43 без размонтирования компонента сам обновит данные.

Что нужно сделать, чтобы параметр маршрута :id приходил прямо во вход input компонента?

Функциональные guards

Guard решает, можно ли активировать маршрут. В современном Angular guard - это обычная функция типа CanActivateFn, а не класс. Она возвращает true (пускать), false (запретить) или UrlTree (перенаправить). Возврат можно завернуть в Observable или Promise для асинхронной проверки. Функция исполняется в контексте инжектора, поэтому внутри работает inject().

Редирект делается не императивным router.navigate, а возвратом UrlTree из router.createUrlTree. Это важно: возвращая UrlTree, guard атомарно отменяет текущую навигацию и стартует новую, без гонок и мигания промежуточного состояния. Возврат false просто блокирует переход, оставляя пользователя где был.

  • CanActivateFn — Решает, пускать ли на маршрут. Самый частый guard: проверка авторизации, прав, наличия данных.
  • CanDeactivateFn — Решает, можно ли уйти с маршрута. Типично: подтверждение ухода при несохранённых изменениях формы.

Функциональные guards заменили старые классы на основе интерфейса CanActivate, которые объявлены устаревшими. Функции проще тестировать и не требуют декоратора @Injectable: достаточно обычной функции, читающей зависимости через inject().

Guard CanActivateFn должен запретить доступ и отправить неавторизованного пользователя на /login. Какой возврат предпочтителен?

Resolvers и route data

Resolver предзагружает данные до активации маршрута. Маршрутизатор ждёт завершения resolver, и только потом отрисовывает компонент - тот сразу получает готовые данные и не мигает пустым экраном. Как и guard, resolver - это функция (ResolveFn), исполняемая в контексте инжектора.

route data - статический объект, привязанный к маршруту в конфигурации. В отличие от resolver, он не вычисляется, а задаётся заранее: требуемая роль, заголовок страницы, флаги поведения. Общий guard или сервис читают data, чтобы вести себя по-разному на разных маршрутах без отдельной функции под каждый.

Resolver блокирует навигацию до своего завершения, поэтому долгий запрос внутри него задержит переход и создаст ощущение зависания. Для тяжёлых данных часто лучше показать компонент сразу и грузить асинхронно через resource, оставив resolver для быстрых критичных данных.

Чем route data отличается от результата resolver?

Связь с другими темами

Урок расширяет базовую маршрутизацию защитой и данными. Дальше - оптимизация загрузки:

  • Angular Router: маршруты — Параметры, guards и resolvers навешиваются на маршруты из предыдущего урока
  • Ленивая загрузка маршрутов — Guards и resolvers работают и с лениво загружаемыми маршрутами

Итог

  • Параметры маршрута (path: 'users/:id') приходят во вход компонента, если включить withComponentInputBinding в provideRouter
  • Функциональный guard - это функция типа CanActivateFn, возвращающая boolean, UrlTree или их Observable/Promise; редирект делается возвратом router.createUrlTree
  • Resolver предзагружает данные до активации маршрута, и компонент получает их готовыми через тот же input-биндинг
  • route data - статический объект на маршруте для конфигурации: требуемой роли, заголовка, флагов
  • Guards и resolvers исполняются в контексте инжектора, поэтому внутри них работает inject() для доступа к сервисам

Связанные уроки

  • ng-22-router-intro — Базовые маршруты и router-outlet - основа, на которую навешиваются параметры, guards и resolvers
  • ng-20-hierarchical-di — Функциональные guards и resolvers исполняются в контексте инжектора и используют inject()
Параметры, resolvers и guards

0

1

Войти