Концептуальный разбор маршрутизатора Angular2 с примерами

  • подключение и базовая настройка
    • use hash
  • стейты (states)
  • специальные директивы роутера
  • параметры стейта
  • параметры запроса(query params)
  • статические параметры стейта
  • перенаправление на другой стейт
  • события (events)
  • хуки (guards)
  • резолв асинхронных данных
  • вложенные стейты (nested states)
  • множественные вью (multiple views)
  • ленивая загрузка (lazy loading)

Подключение и базовая настройка

Для использования маршрутизатора нам необходимо в первую очередь импортировать его модуль:

import { RouterModule } from '@angular/router'

и прописать в зависимостях:

[javascript]
imports: [

RouterModule,

],
[/javascript]

После чего мы можем настроить состояния(стейты). Добавим корневой стейт:

[javascript]
const routes = [
{path: ”, component: HomeComponent},
];
[/javascript]

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

[javascript]
const routes = [
{path: ”, component: HomeComponent},
{ path: ‘user’, component: UsersComponent},
];
[/javascript]

Теперь, чтобы подключить описание стейтов к модулю роутера сделаем:

[javascript]
imports: [

RouterModule.forRoot(routes),

],
[/javascript]

По умолчанию используются HTML5 пути, но если мы хотим переключить на хеш, то нам нужно указать дополнительный параметр:

[javascript]
imports: [

RouterModule.forRoot(routes, {useHash: true}),

],
[/javascript]

Специальные директивы роутера

RouterOutlet – для задание места вывода представления:

[html]
<router-outlet></router-outlet>;
[/html]

RouterLink – хелпер для удобного вывода ссылки:

[html]
<a routerLink="user">User</a>
[/html]

RouterLinkActive – для выделения(задания специального css класса) активных(указывающих на текущий стейт) ссылок:

[html]
<a routerLink="user" routerLinkActive="active-class">User</a>
[/html]

также можно задавать параметр

[html]
[routerLinkActiveOptions]="{exact: true}"
[/html]

Параметры стейта

Определяем параметр в настройках стейта:

[javascript]
{ path: ‘user/:userId’, component: UsersComponent},
[/javascript]

Получаем этот параметр с помощью специального сервиса ActivatedRoute:

[javascript]
constructor(private route: ActivatedRoute) {
this.route.params.subscribe(params => console.log(params.userId));
}
[/javascript]

также можно подписаться на конкретный параметр с помощью метода pluck:

[javascript]
constructor(private route: ActivatedRoute) {
this.route.params.pluck(‘userId’).subscribe(userId => console.log(userId));
}
[/javascript]

Параметры запроса(query params)

Кроме параметров стейта, можем также добавлять в url произвольное количество параметров запроса:

[html]
<a routerLink="search" [queryParams]="{ city: ‘Amsterdam’ }">Filter by Amsterdam</a>
[/html]

либо если изменяем стейт с помощью метода navigate:

[javascript]
this.router.navigate([‘/search’], { queryParams: { city:’Amsterdam’ } });
[/javascript]

Чтобы получить параметры стейта подписываемся на queryParams свойство:

[javascript]
constructor(private route: ActivatedRoute) {
this.route.queryParams.subscribe(params => console.log(params.city));
}
[/javascript]

Cтатические параметры стейта

Стейт также можно дополнять статическими параметрами:

[javascript]
{ path: ‘user’, component: UsersComponent,
data: {userName: ‘John’}},
[/javascript]

И получить доступ через сервис ActivatedRoute:

[javascript]
constructor(private route: ActivatedRoute) {
this.route.data.subscribe(console.log);
}
[/javascript]

Перенаправление на другой стейт

Перенаправление может быть либо автоматическим (постоянный редирект) либо условным (по какому-то действию).

Чтобы сделать автоматический редирект в настройках стейта мы прописываем свойство redirectTo, где указываем стейт, на который хотим перенаправить:

[javascript]
const routes = [
{ path: ”, redirectTo: ‘dashboard’, pathMatch: ‘full’ },
{ path: ‘dashboard’, component: DashboardComponent },
];
[/javascript]

обратите также внимание, что в случае path: ” мы должны указать строгую стратегию разбора URL – pathMatch: ‘full’.

Для условного перенаправления мы используем методы router.navigate() и router.navigateByUrl():

[javascript]
router.navigate([‘users’, userId]);
router.navigateByUrl(`users/${userId}`);
[/javascript]

– отличие только в том, что в первом случае вы подаете как параметр набор команд, а во втором строку URL.

Cобытия роутера

Мы можем подписываться не только на данные, но и на события происходящие в роутере, в этом нам поможет сервис Router:

[javascript]
constructor(router: Router) {
router.events.subscribe((event: Event) => {
if (event instanceof NavigationStart) {
//…
}
});
}
[/javascript]

кроме события NavigationStart мы также можем слушать:

  • NavigationEnd
  • NavigationCancel
  • NavigationError
  • RoutesRecognized

Хуки (guards)

На ряду с событиями в роутере есть хуки(guards), которые в отличие от событий выполняются ДО действия и в данном случае мы можем вклиниться в поток выполнения, и если нужно остановить выполнение события.

Существуют следующие хуки:

  • CanActivate – определяет возможность загрузки стейта

  • CanActivateChild – аналогично предыдущего только для вложенного стейта

  • CanDeactivate – запускается при смене стейта, хорошим примером будет подтверждение не сохраненных данных перед уходом со страницы:

    CanDeactivate(){
          return window.confirm("You have unsaved changes. Still want to leave?");
    }
    
  • CanLoad – определяет может ли стейт быть загружен асинхронно

  • Resolve – для резолвинга данных до загрузки стейта (см. отдельный пункт о резолвинге данных)

Для реализации хука нам нужно 2 момента: определить его в стейте:

[javascript]
{ path: ‘user’, component: UsersComponent, canActivate: AuthService},
[/javascript]

и создать сам хук-сервис, который будет реализовывать соотвествующий интерфейс:

[javascript]
import {CanActivate} from ‘@angular/router’;
export class AuthService implements CanActivate{
canActivate(): boolean {
return true;
}
}
[/javascript]

В методе canActivate мы можем возвращать как просто булевое значение, так и асинхронные Promise и Observable.

Резолвинг асинхронных данных

Для подгрузки асинхронных данных до загрузки стейта также используется хук – Resolve:

[javascript]
{ path: ‘user’, component: UsersComponent, resolve:{ user: UserDataResolveService }},
[/javascript]

теперь создадим резолв-сервис:

[javascript]
import { Resolve } from ‘@angular/router’;
export class UserDataResolveService implements Resolve<any> {
resolve() {
return { name: ‘Bob’ };
}
}
[/javascript]

и можно будет подписаться на эти данные, также как и на статические:

[javascript]
constructor(route: ActivatedRoute) {
route.data.subscribe(data => {
console.log(data.user);
});
}
[/javascript]

Вложенные стейты (nested states)

Для задание вложенных/дочерних стейтов мы используем свойство children, в котором определяем массив:

[javascript]
{ path: ‘user’, children: [
{path: ”, component: UserProfileComponent},
{path: ‘settings’, component: UserSettingsComponent}
]},
[/javascript]

синтаксис вложенных стейтов идентичен основным. Вложенные стейты также могут иметь свои вложенные.

Множественные вью (multiple views)

Роутер ангуляра поддерживает множественные вью, то есть наличие нескольких именованных RouterOutlet компонентов:

[html]
<router-outlet></router-outlet>
<router-outlet name="popup"></router-outlet>
[/html]

Неименованный – основной. Теперь для настройки прописываем специальное свойство outlet:

[javascript]
{ path: ‘user’, component: HomeComponent },
{ path: ‘user’, component: UsersComponent, outlet: ‘popup’},
[/javascript]

Ссылка routerLink будет выглядеть для такого случая следующим образом:

[html]
<a [routerLink]="[{ outlets: { primary: ‘user’, popup: ‘user’ } }]"></a>
[/html]

а если мы хотим деактивировать второстепенный аутлет:

[html]
<a [routerLink]="[{ outlets: { popup: null } }]">Close user</a>
[/html]

Ленивая загрузка (lazy loading)

Роутер также дает нам возможность организовать отложенную(ленивую) загрузку модулей. Для этого нам необходимо прописать в стейт специальное свойство loadChildren и указать в нем путь и имя модуля, который собираемся загрузить:

[javascript]
{ path: ‘lazy’, loadChildren: ‘./lazy/lazy.module#LazyModule’ }
[/javascript]

и инициализировать RouterModule внутри LazyModule:

[javascript]
const routes: Routes = [
{ path: ”, component: AdminComponent }
];

@NgModule({
imports: [
CommonModule,
RouterModule.forChild(routes)
],
declarations: [AdminComponent]
})
export class LazyModule { }
[/javascript]

мы можем определить сколько захотим состояний для LazyModule, но я для простоты определил один, который загрузит компонент AdminComponent.