Как использовать Guards (CanActivate, CanDeactivate)?

В Angular Guards — это специальные сервисы, реализующие интерфейсы CanActivate, CanDeactivate, CanLoad, CanActivateChild, Resolve, которые позволяют управлять доступом к маршрутам, отклонять переходы, предзагружать данные или подтверждать выход со страницы. Guards выступают как "стражи маршрутов", и работают до того, как Angular активирует маршрут.

CanActivate

Назначение

Проверяет, можно ли активировать (перейти к) определённому маршруту. Часто используется для защиты маршрутов от неавторизованных пользователей.

Интерфейс

interface CanActivate {
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | Promise<boolean> | boolean;
}

Пример реализации

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if (this.authService.isLoggedIn()) {
return true;
}
this.router.navigate(\['/login'\]);
return false;
}
}

Применение в маршрутах

{
path: 'dashboard',
component: DashboardComponent,
canActivate: \[AuthGuard\]
}

CanDeactivate

Назначение

Контролирует возможность покинуть маршрут. Например, используется для предупреждения при незаполненной форме или несохранённых данных.

Интерфейс

interface CanDeactivate<T> {
canDeactivate(
component: T,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState?: RouterStateSnapshot
): Observable<boolean> | Promise<boolean> | boolean;
}

Пример интерфейса компонента

export interface CanComponentDeactivate {
canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}

Пример компонента

@Component({...})
export class EditProfileComponent implements CanComponentDeactivate {
hasUnsavedChanges = true;
canDeactivate(): boolean {
return !this.hasUnsavedChanges || confirm('У вас есть несохранённые изменения. Покинуть страницу?');
}
}

Пример CanDeactivate guard

@Injectable({ providedIn: 'root' })
export class ConfirmExitGuard implements CanDeactivate<CanComponentDeactivate> {
canDeactivate(component: CanComponentDeactivate): boolean {
return component.canDeactivate ? component.canDeactivate() : true;
}
}

Применение в маршрутах

{
path: 'edit',
component: EditProfileComponent,
canDeactivate: \[ConfirmExitGuard\]
}

Совместное использование CanActivate и CanDeactivate

{
path: 'editor',
component: EditorComponent,
canActivate: \[AuthGuard\],
canDeactivate: \[ConfirmExitGuard\]
}

CanActivateChild

Назначение

Ограничивает доступ к дочерним маршрутам.

Пример

@Injectable({ providedIn: 'root' })
export class ChildGuard implements CanActivateChild {
constructor(private authService: AuthService) {}
canActivateChild(): boolean {
return this.authService.hasChildAccess();
}
}

Применение

{
path: 'admin',
component: AdminComponent,
canActivateChild: \[ChildGuard\],
children: \[
{ path: 'users', component: UsersComponent },
{ path: 'roles', component: RolesComponent }
\]
}

CanLoad

Назначение

Предотвращает загрузку ленивого модуля, если условие не выполнено. Это важно, чтобы вообще не загружать код недоступных разделов.

Пример

@Injectable({ providedIn: 'root' })
export class AuthLoadGuard implements CanLoad {
constructor(private authService: AuthService) {}
canLoad(): boolean {
return this.authService.isAdmin();
}
}

Применение

{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
canLoad: \[AuthLoadGuard\]
}

В отличие от CanActivate, CanLoad блокирует саму загрузку JS-чанка, а не только отображение компонента.

Возврат значений

Guards могут возвращать:

  • true — разрешить переход.

  • false — отклонить переход.

  • UrlTree — перенаправить.

Пример с UrlTree

canActivate(): UrlTree {
return this.router.createUrlTree(\['/access-denied'\]);
}

Асинхронные Guards

Guards могут быть async:

canActivate(): Observable<boolean> {
return this.authService.checkSession(); // HTTP или локальный storage
}

Поддерживаются Promise, Observable, а также combineLatest, switchMap и другие RxJS-операторы.

Общая структура

@NgModule({
imports: \[RouterModule.forRoot(\[
{
path: 'secure',
component: SecureComponent,
canActivate: \[AuthGuard\],
canDeactivate: \[ConfirmExitGuard\],
canActivateChild: \[ChildGuard\],
canLoad: \[AuthLoadGuard\]
}
\])\],
exports: \[RouterModule\]
})
export class AppRoutingModule {}

Проверка очередности

При маршрутизации Angular вызывает guards в следующем порядке:

  1. canLoad (для ленивых модулей).

  2. canActivate / canActivateChild.

  3. canDeactivate (при уходе с маршрута).

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