Миграция Angular2 на новый Router 3.0
Во время альфа и бета версий Angular2 router-deprecated вполне справлялся со своими задачами. С появлением RC1 вышел и переработанный router 2.0, затем в вериси RC2 снова выходит новая версия роутера — 3.0. Полная его переработка была связана с тем, что возникли серьезные проблемы, такие как глубокая линковка в lazy-loaded секциях. Итак, рассмотрим основные изменения.
Импортирование
Раньше было так:
//old
import { RouteConfig, ROUTER_DIRECTIVES } from '@angular/router-deprected';
Теперь так:
//new
import { Routes, RouterModule } from '@angular/router';
Также в boot.ts
заменяем определение ROUTER_PROVIDERS
С router-deprecated
выглядело так:
// old boot.ts
import { ROUTER_PROVIDERS } from '@angular/router-deprecated';
import { AppComponent } from './components/app/app.component';
bootstrap(AppComponent, [
ROUTER_PROVIDERS
]);
Теперь должно выглядить следующим образом
// new boot.ts
import { APP_ROUTER_PROVIDER } from './routes';
import { AppComponent } from './components/app/app.component';
bootstrap(AppComponent, [
APP_ROUTER_PROVIDER
]);
Конфигурация
//old
import { Component } from '@angular/core';
import { RouteConfig, ROUTER_DIRECTIVES } from '@angular/router-deprected';
import { ActionsComponent } from './components/actions.component';
import { BooActionComponent } from '../actions/boo-actions.component';
@RouteConfig([
{
path: '/',
name: 'Actions',
component: ActionsComponent,
useAsDefault: true
},
{
path: '/boo/:id',
name: 'BooAction',
component: HeroDetailComponent
}
])
@Component({
selector: 'action-app',
template: '<router-outlet></router-outlet>',
directives: [ROUTER_DIRECTIVES]
})
export class AppComponent {}
Новый роутер теперь не привязан к какому либо компоненту, и теперь выступает в роли провайдера.
//new routes.ts
import { provideRouter, RouterConfig } from '@angular/router';
import { ActionsComponent } from './components/actions.component';
import { HeroDetailComponent } from './components/actions/boo-actions.component';
export const appRoutes: RouterConfig = [
{ path: '', component: BooComponent, terminal: true },
{ path: 'boo/:id', component: BooActionComponent }
];
export const APP_ROUTER_PROVIDER = provideRouter(appRoutes);
Роутер все также биндится к <router-outlet></router-outlet>
, но необходимость в декораторе @RouteConfig
отпала.
При определении пути теперь не нужно писать его имя, и сам путь пишется без слеша в начале.
Привязка к маршрутам
<a [routerLink]="['/Home']">Home</a>
<a [routerLink]="['/Boo', { id: 1 }]">Boo</a>
Мы видим, что каждый путь устанавливался по его имени и передаваемым в него параметрам, в виде объекта. В новом же роутере идентифицируется по пути, без начального слеша, и параметр передается без объекта.
<a [routerLink]="['']">Home</a>
<a [routerLink]="['boo', 1]">Boo</a>
Те же самые изменения потерпел вызов navigate
.
//old
this._router.navigate(['Error',{ error: 404 }]);
//new
this.router.navigate(['error', 404]);
Доступ к параметрам пути
//old components/actions/boo-action.component.ts
import { Component, OnInit } from '@angular/core';
import { RouteParams } from '@angular/router-deprected';
import { BooService } from '../../services/boo.service'
@Component({ ... })
export class BooActionComponent implements OnInit {
constructor(private params: RouteParams, private booService: BooService) {}
ngOnInit() {
this.booService.getBoo(
this.params.get('id')
).subscribe(boo => this.boo = boo);
}
}
Во-первых, RouteParams
был заменен на ActivatedRoute
.
Во-вторых, свойство params
теперь выступает как observable
, и получить значения можно только через коллбэк.
//new components/actions/boo-action.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { BooService } from '../../services/boo.service'
@Component({ ... })
export class BooActionComponent implements OnInit, OnDestroy {
public boo: string
private booSubscription: Subscription
constructor(private route: ActivatedRoute, private booService: BooService) {}
ngOnInit() {
this.booSubscription = this.route.params
.map(params => params.id)
.flatMap(id => this.booService.getBoo(id))
.subscribe(boo => this.boo = boo);
}
ngOnDestroy() {
this.booSubscription.unsubscribe();
}
}
Желательно подписываться в ngOnInit
методе и отписываться в ngOnDestroy
Защита путей
Проверки аутидентификации на старом роутере можно было добиться при помощи @CanActivate, @CanDeactivate
декораторов.
// components/actions/boo-aciton.component.ts
import { Component } from '@angular/core';
import { CanActivate, ComponentInstruction } from '@angular/router-deprecated';
@Component({ ... })
@CanActivate((next: ComponentInstruction, prev: ComponentInstruction) => {
return isAuthTokenValid();
})
export class BooActionComponent { ... }
В новом роутере это определено внутри конфигурации и доступно через DI.
// auth-guard.ts
import { Injectable } from '@angular/core';
import {
CanActivate,
Router,
ActivatedRouteSnapshot,
RouterStateSnapshot
} from '@angular/router';
import { AuthService } from './services/auth/auth.service';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if (this.authService.isLoggedIn()) {
return true;
}
this.router.navigate(['login']);
return false;
}
}
Можно просто сделать инъекцию AuthService
и маршрутизатор сам направит пользователя на страницу входа.
// routes.ts
import { provideRouter, RouterConfig } from '@angular/router';
import { ActionsComponent } from './components/actions.component';
import { BooActionComponent } from './components/actions/boo-action.component';
import { LoginComponent } from './components/login/login.component';
import { AuthGuard } from './auth-guard';
export const appRoutes: RouterConfig = [
{ path: '', component: ActionsComponent, terminal: true },
{ path: 'boo/:id', component: BooActionComponent, canActivate: [AuthGuard] },
{ path: 'login', component: LoginComponent },
];
export const APP_ROUTER_PROVIDER = provideRouter(appRoutes);