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()