1. Знакомство с Guard-ами
Guard-ы (от английского "guard" — "страж", "охранник") — это специальные сервисы, которые Angular вызывает перед активацией маршрута, чтобы решить: разрешить или запретить переход на этот маршрут. Это как охранник на входе в клуб: он смотрит, есть ли у вас билет, и решает, пустить или нет. Если билет (или условие) есть — проходите, если нет — гуляйте дальше.
Guard-ы позволяют:
- Ограничивать доступ к маршрутам (например, только для авторизованных пользователей).
- Предупреждать пользователя о несохранённых данных при уходе со страницы.
- Выполнять проверки, загрузку данных или перенаправления до того, как компонент маршрута появится на экране.
В Angular есть несколько типов guard-ов, но сегодня мы рассмотрим самый популярный и базовый — CanActivate.
CanActivate: принцип работы и назначение
CanActivate — это guard, который решает, можно ли активировать (открыть) конкретный маршрут. Он особенно полезен для защиты "закрытых" страниц (например, профиля пользователя, личного кабинета, страницы администрирования).
sequenceDiagram
participant User
participant AngularRouter
participant CanActivateGuard
participant ProtectedRoute
User->>AngularRouter: Навигация на защищенный маршрут
AngularRouter->>CanActivateGuard: Вызов CanActivate-guard
CanActivateGuard->>CanActivateGuard: Выполнение логики проверки
alt Возвращает true
CanActivateGuard-->>AngularRouter: Разрешить переход (true)
AngularRouter->>ProtectedRoute: Активировать маршрут
ProtectedRoute-->>User: Отобразить компонент
else Возвращает false / UrlTree
CanActivateGuard-->>AngularRouter: Запретить переход (false) / Перенаправить (UrlTree)
AngularRouter-->>User: Блокировать/Перенаправить пользователя
end
Когда вызывается CanActivate?
- Каждый раз, когда пользователь переходит по маршруту, к которому привязан этот guard.
- Guard может вернуть либо true (разрешить переход), либо false (запретить), либо UrlTree (перенаправить), либо Promise/Observable этих значений (для асинхронных проверок).
2. Как создать Guard: пошагово
Angular CLI помогает создавать guard-ы буквально одной командой. Но давайте разберём всё по шагам.
Шаг 1. Генерируем guard через CLI
ng generate guard auth
После выполнения команды появится файл auth.guard.ts. В нём уже будет заготовка класса:
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private router: Router) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> {
// Здесь будет логика проверки
return true;
}
}
Шаг 2. Реализуем логику проверки
Чаще всего guard обращается к сервису авторизации, чтобы узнать: пользователь вошёл в систему или нет.
Пример простого AuthService
@Injectable({
providedIn: 'root'
})
export class AuthService {
isLoggedIn = false; // В реальном приложении — логика проверки токена, куки и т.д.
login() {
this.isLoggedIn = true;
}
logout() {
this.isLoggedIn = false;
}
}
Дорабатываем AuthGuard
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): boolean | UrlTree {
if (this.authService.isLoggedIn) {
return true; // Доступ разрешён
} else {
// Можно вернуть false или сделать редирект на страницу логина
return this.router.createUrlTree(['/login']);
}
}
}
Что происходит:
Если пользователь авторизован (isLoggedIn), возвращаем true — маршрут активируется.
Если нет — возвращаем UrlTree, Angular делает перенаправление на /login.
Подключение Guard-а к маршруту
Guard-ы не работают "сами по себе". Их нужно явно указать в настройках маршрута.
const routes: Routes = [
{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
{ path: 'login', component: LoginComponent }
];
Теперь, когда пользователь попытается открыть /dashboard, Angular сначала вызовет AuthGuard.canActivate(). Если guard вернёт true, компонент DashboardComponent появится на экране. Если false или UrlTree, произойдёт редирект.
3. Базовый пример: защита маршрута
Давайте соберём всё воедино.
Простой AuthService
@Injectable({
providedIn: 'root'
})
export class AuthService {
isLoggedIn = false;
login() {
this.isLoggedIn = true;
}
logout() {
this.isLoggedIn = false;
}
}
AuthGuard
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private auth: AuthService, private router: Router) {}
canActivate(): boolean | UrlTree {
return this.auth.isLoggedIn ? true : this.router.createUrlTree(['/login']);
}
}
Маршруты
const routes: Routes = [
{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
{ path: 'login', component: LoginComponent }
];
Использование в шаблоне
<!-- app.component.html -->
<a routerLink="/dashboard">Личный кабинет</a>
<a routerLink="/login">Войти</a>
<router-outlet></router-outlet>
Теперь, если пользователь не авторизован, при попытке открыть /dashboard его автоматически перебросит на /login.
4. Полезные нюансы
Асинхронные guard-ы: работа с Observable и Promise
В реальных приложениях часто нужно делать асинхронные проверки (например, запросить сервер: действителен ли токен?).
Guard может возвращать не только boolean, но и Promise/Observable<boolean|UrlTree>.
canActivate(): Observable<boolean | UrlTree> {
return this.auth.checkTokenOnServer().pipe(
map(isValid => isValid ? true : this.router.createUrlTree(['/login']))
);
}
Важно: Angular дождётся завершения Observable/Promise и только потом решит, пускать пользователя или нет.
Кратко о других типах guard-ов
- CanActivateChild — для защиты дочерних маршрутов.
- CanDeactivate — спрашивает, можно ли уйти с текущего компонента (например, если есть несохранённые данные).
- CanLoad — разрешает или запрещает загрузку ленивого модуля.
- CanMatch — новый guard для кастомной проверки соответствия маршрута.
Сегодня мы фокусируемся только на CanActivate, но имейте в виду — их целый набор для разных сценариев.
5. Типичные ошибки при работе с Guard-ами
Ошибка №1: Guard не срабатывает, потому что не добавлен в маршрут.
Одна из самых частых ситуаций: guard реализован, но забыли прописать его в свойстве canActivate нужного маршрута. Angular будет обходить его стороной, как если бы охранника забыли поставить на вход.
Ошибка №2: Ошибки в логике проверки.
Например, возвращаете всегда true или всегда false, забыли сделать редирект, и пользователь "зависает" на пустой странице. Всегда проверяйте, что возвращаете нужное значение.
Ошибка №3: Несоответствие возвращаемого типа.
Метод canActivate должен возвращать boolean, UrlTree, Promise или Observable. Если вернуть что-то иное (например, строку или объект), Angular выдаст ошибку.
Ошибка №4: Guard не инжектирует нужные сервисы.
Забыли добавить нужный сервис (например, AuthService) в конструктор guard-а — получите ошибку DI (Dependency Injection).
Ошибка №5: Асинхронный guard не завершает Observable/Promise.
Если Observable не завершится (например, вы не вызвали complete() или не вернули значение), переход по маршруту "зависнет" навсегда.
Ошибка №6: Guard работает, но пользователь всё равно может попасть на страницу через прямую ссылку или при обновлении страницы.
Это случается, если логика проверки реализована только на клиенте. Для критически важных маршрутов всегда дублируйте проверки на сервере.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ