神刀安全网

Upgrading to the new Angular 2 router

When the first release candidate came out, the team behind Angular 2 released a reworked router and deprecated the old one. With the second release candidate they reworked it again (to solve numerous existing problems) incorporating feedback from the previous one and released it as version 3.0. At the same time they started to encourage people to use and test it to get more feedback.

In this tutorial I’ll show you how to upgrade from the deprecated router ( @angular/router-deprecated ) to version 3.0 . The inner workings of the new router won’t be described in detail. If you are interested, you can get your head around it easily from the updated official documentation , Viktor Savkin’s detailed blog post or the article on Thoughtram .

Configuring routes

In our example we are having a main route with a list of items and another route which shows the details of an item like in the Tour of Heroes tutorial .

Let’s look at our old router configuration described with a decorator on the main component.

// components/app/app.component.ts
import { Component } from '@angular/core';
import { RouteConfig, ROUTER_DIRECTIVES } from '@angular/router-deprected';

import { HeroesComponent } from './components/heroes/heroes.component';
import { HeroDetailComponent } from '../hero-detail/hero-detail.component';

@RouteConfig([
{
path: '/',
name: 'Heroes',
component: HeroesComponent,
useAsDefault: true
},
{
path: '/detail/:id',
name: 'HeroDetail',
component: HeroDetailComponent
}
])
@Component({
selector: 'hero-app',
template: '<router-outlet></router-outlet>',
directives: [ROUTER_DIRECTIVES]
})
export class AppComponent {}

We define two routes, Heroes for the default route and HeroDetail for displaying the details of a hero. We have to add a unique name to each route, which is used to address them when displaying links or redirecting.

The big change with the new router is that routing configuration is no longer bound to a specific component, instead it is accessible as a provider.

// routes.ts
import { provideRouter, RouterConfig } from '@angular/router';

import { HeroesComponent } from './components/heroes/heroes.component';
import { HeroDetailComponent } from './components/hero-detail/hero-detail.component';

export const appRoutes: RouterConfig = [
{ path: '', component: HeroesComponent, terminal: true },
{ path: 'detail/:id', component: HeroDetailComponent }
];

export const APP_ROUTER_PROVIDER = provideRouter(appRoutes);

When defining the new routes only two things need to be removed from the definition. The first one is the leading slash from the path property, the second one is the name property. From now on we will use the path as an identifier. We need to feed our new APP_ROUTER_PROVIDER to the application and replace the existing ROUTER_PROVIDERS imported from the deprecated package.

// main.ts
import { ROUTER_PROVIDERS } from '@angular/router-deprecated';
import { AppComponent } from './components/app/app.component';

bootstrap(AppComponent, [
ROUTER_PROVIDERS
]);

This setup will become:

// main.ts
import { APP_ROUTER_PROVIDER } from './routes';
import { AppComponent } from './components/app/app.component';

bootstrap(AppComponent, [
APP_ROUTER_PROVIDER
]);

The active route is placed inside the RouterOutlet element. It’s markup and setup won’t change. We only have to import it from the new package.

Now we can safely delete the @RouteConfig decorator from our AppComponent .

Linking to routes

The old way routes were identified by their names and you could pass values to the placeholders with object literals.

<a [routerLink]="['/Heroes']">Heroes</a>
<a [routerLink]="['/HeroDetail', { id: 1 }]">Captain America</a>

With the new router they are identified by their path and you have to skip the placeholder.

<a [routerLink]="['']">Heroes</a>
<a [routerLink]="['detail', 1]">Captain America</a>

This parameter transformation also affects the navigate method of the Router service.

// navigates to Captain America's page
this.router.navigate(['detail', 1]);

Route parameter access

On the HeroDetail route we need to access the identifier for the hero to be able to fetch it from a service. Parameter access was pretty straightforward with RouteParams as it held the parameters and could give them back through a getter method.

// components/hero-detail/hero-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { RouteParams } from '@angular/router-deprected';

import { HeroService } from '../../services/hero/hero.service'

@Component({ ... })
export class HeroDetailComponent implements OnInit {
constructor(private params: RouteParams, private heroService: HeroService) {}

ngOnInit() {
this.heroService.getHero(
this.params.get('id')
).subscribe((hero) => {
this.hero = hero;
});
}
}

Now RouteParams is replaced by ActivatedRoute . The key difference is that route parameters can be accessed through the params property as an Observable . The values in the Observable are objects with properties named after the route parameters.

// components/hero-detail/hero-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

import { HeroService } from '../../services/hero/hero.service'

@Component({ ... })
export class HeroDetailComponent implements OnInit {
constructor(private route: ActivatedRoute, private heroService: HeroService) {}

ngOnInit() {
this.route.params
.map(params => params.id)
.subscribe((id) => {
this.heroService.getHero(id).subscribe((hero) => {
this.hero = hero;
});
});
}
}

Guarding routes

When it comes to authentication we want to guard the components that are restricted from unauthenticated users. The old router gave a solution for this with lifecycle decorators ( @CanActivate , @CanDeactivate ).

// components/hero-detail/hero-detail.component.ts
import { Component } from '@angular/core';

import { CanActivate, ComponentInstruction } from '@angular/router-deprecated';

@Component({ ... })
@CanActivate((next: ComponentInstruction, prev: ComponentInstruction) => {
return isAuthTokenValid();
})
export class HeroDetailComponent { ... }

The guarding logic could be put inside a callback function.

It had several drawbacks. The biggest is that it didn’t have access to the application’s dependency injection. The second one is that you had to decorate every Component class with it what you wanted to guard. If you want to read more about authentication with the deprecated router read this article .

With the new router these problems solved. It has access to dependency injection, it is defined inside the route configuration and can be chainable.

// 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;
}
}

We can easily inject the AuthService and even the Router with which we can redirect the user to the login screen. The next step is to setup this guard on the route configuration.

// app.routes.ts
import { provideRouter, RouterConfig } from '@angular/router';

import { HeroesComponent } from './components/heroes/heroes.component';
import { HeroDetailComponent } from './components/hero-detail/hero-detail.component';
import { LoginComponent } from './components/login/login.component';
import { AuthGuard } from './auth-guard';

export const appRoutes: RouterConfig = [
{ path: '', component: HeroesComponent, terminal: true },
{ path: 'detail/:id', component: HeroDetailComponent, canActivate: [AuthGuard] },
{ path: 'login', component: LoginComponent },
];

export const APP_ROUTER_PROVIDER = provideRouter(appRoutes);

The property canActivate holds the guards as an array. This is the way they become chainable also.

Summary

With these little steps we managed to migrate our application from the deprecated router to the new one (version 3.0).

The parts we touched are

  • route configuration and setup
  • linking to routes in template and code
  • accessing route parameters
  • guarding routes from unauthenticated users

If you want to see the migration on a complete application, take a look at Dan Wahlin’s jumpstart (Typescript) or this TodoMVC implementation (Javascript) .

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Upgrading to the new Angular 2 router

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址