Protecting Routes using Guards in Angular 2

来源:互联网 发布:讨厌大野智 知乎 编辑:程序博客网 时间:2024/04/28 10:45


http://blog.thoughtram.io/angular/2016/07/18/guards-in-angular-2.html


by Pascal Precht on Jul 18, 2016, last updated on Aug 11, 2016
7 minute read


In our last article, Routing in Angular 2 revisited, we talked about the latest changes in the router APIs. While we covered how to set up basic routes, access parameters and link to other components, we haven’t really talked about more sophisticated use cases like protecting routes.

Protecting routes is a very common task when building applications, as we want to prevent our users from accessing areas that they’re not allowed to access, or, we might want to ask them for confirmation when leaving a certain area. Angular 2’s router provides a feature called Guards that try to solve exactly that problem. In this article, we’d like to take a look at the different types of guards and how to implement them for actual use cases.

Want to see things in action first?

play_arrow Play videos

Guard Types

There are four different guard types we can use to protect our routes:

  • CanActivate - Decides if a route can be activated
  • CanActivateChild - Decides if children routes of a route can be activated
  • CanDeactivate - Decides if a route can be deactivated
  • CanLoad - Decides if a module can be loaded lazily

Depending on what we want to do, we might need to implement one or the other guard. In some cases, we even need to implement all of them. Let’s take a look at how to define guards.

Defining Guards

Guards can be implemented in different ways, but after all it really boils down to a function that returns eitherObservable<boolean>, Promise<boolean> or boolean. In addition, guards are registered using providers, so they can be injected by Angular when needed.

As Functions

To register a guard we need to define a token and the guard function. Here’s what a super simple guard implementation could look like:

@NgModule({  ...  providers: [    provide: 'CanAlwaysActivateGuard',    useValue: () => {      return true;    }  ],  ...})export class AppModule {}

As we can see, it’s really just a provider with some made up token that resolves to a guard function that returnstrue (if provider doesn’t mean anything to you, go and check out our article onDependency Injection in Angular 2). Since it’s always returning true, this guard is not protecting anything, as it will always activate the route that uses it. However, this is really just to demonstrate a guard implementation. We also notice that we’re using a string token, which works fine but what we really want is an OpaqueToken to not run into name collisions.

Once a guard is registered with a token, we can use it in our route configuration. The following route configuration has theCanAlwaysActivateGuard attached, which gets executed when routing to that specific route.

export const AppRoutes:RouterConfig = [  {     path: '',    component: SomeComponent,    canActivate: ['CanAlwaysActivateGuard']  }];

As we can see, all we need to do is to define a list of guard tokens that should be called. This also implies that we can have multiple guards protecting a single route. Guards are executed in the order they are defined on the route.

As Classes

Sometimes, a guard needs dependency injection capabilities. In these cases, it makes sense to define a guard as a class, because dependencies can then be simply injected. Let’s say we want to protect a route and have the user authenticate first. We might want to inject an AuthService to determine if the user is authenticated or not. A class guard would be a perfect fit.

When creating a guard class, we implement either the CanActivate, CanDeactivate, or CanActivateChild interface, which requires us to have a method canActivate(), canActivateChild(), orcanDeactivate() respectively. Those methods are pretty much the equivalent of a guard function in the previous scenario. The following snippet shows a simpleCanActivate guard implementation using classes.

import { Injectable } from '@angular/core';import { CanActivate } from '@angular/router';import { AuthService } from './auth.service';@Injectable()export class CanActivateViaAuthGuard implements CanActivate {  constructor(private authService: AuthService) {}  canActivate() {    return this.authService.isLoggedIn();  }}

Pretty straight forward. An injectable class with a canActivate() method that now has access to injected dependencies. Angular will call that method for us when a guard is implemented as a class. Just like the previous guard, this one needs to be registered as a provider:

@NgModule({  ...  providers: [    AuthService,    CanActivateViaAuthGuard  ]})export class AppModule {}

And can then be used on a route:

{   path: '',  component: SomeComponent,  canActivate: [    'CanAlwaysActivateGuard',    CanActivateViaAuthGuard  ]}

Deactivating Routes

We’ve now seen how CanActivate can work in different scenarios, but as mentioned earlier, we have a few more guard interfaces we can take advantage of.CanDeactivate gives us a chance to decide if we really want to navigateaway from a route. This can be very useful, if for example we want to prevent our users from losing unsaved changes when filling out a form and accidently clicking on a button to cancel the process.

The CanDeactive guard also has access to the instance of the active component. With this we can take the user experience even to a higher level. Instead of asking an (unwanted) user confirmation every time, we can do this conditionally by checking if change were made. In the sample below the CanDeactivateComponent implements a methods hasChanges(). This returns a boolean value indicating if the components has detected any changes. This can be done by checking the dirty state of the form, keeping track of the previous model and compare it with the current one, … What every fits your needs.

Implementing a CanDeactivate guard is very similar to implementing aCanActivate guard. All we have to do is to create again, either a function, or a class that implements theCanDeactivate interface. We can implement a super simple safety net for our users like this:

import { CanDeactivate } from '@angular/router';import { CanDeactivateComponent } from './app/can-deactivate';export class ConfirmDeactivateGuard implements CanDeactivate<CanDeactivateComponent> {  canDeactivate(target: CanDeactivateComponent) {    if(target.hasChanges()){        return window.confirm('Do you really want to cancel?');    }    return true;  }}

Even though, this is a very trivial implementation, there’s one thing that we didn’t see in the previous example.CanDeactivate<T> uses a generic, so we need to specify what component type we want to deactivate. Honestly, we’re not sure if this is a bug or not. But other than that, it’s very clear what’s going on here. We implement a method canDeactivate(), that is called by Angular’s router internally if needed. Last but not least, also this guard needs to be registered accordingly:

@NgModule({  ...  providers: [    ...    ConfirmDeactivateGuard  ]})export class AppModule {}

Conclusion

Guards are great. They enable us to protect certain routes or even protect the user from losing data. In addition, we can have multiple guards protecting a single route, which helps us implementing sophisticated use cases, where a chain of different checks is needed.


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 深圳车卖了车牌怎么办 a1驾照时期过了怎么办 b1驾照扣了12分怎么办 北京的驾照换证怎么办 b2汽车驾驶证年审过期几天怎么办 上海驾照到期人在外地怎么办 交警开的罚单交不了怎么办 珠海交警微信交罚单扣分怎么办 驾驶证违法罚款单子没有了怎么办 转账密码输错3次怎么办 汽车违章扣6分怎么办 汽车扣了72分年检怎么办 汽车扣了50分怎么办 汽车扣了15分怎么办 汽车扣了27分怎么办 汽车扣了40分怎么办 行驶证掉了怎么办 异地 高速上没带驾驶证行驶证怎么办 身份证驾驶证行驶证都丢了怎么办 驾驶证年审过期一个月怎么办 柴油车辆年检尾气不合格怎么办 驾驶证过期了5天怎么办 过了审车时间怎么办 骑车没带行驶证怎么办 轿车行驶证丢了怎么办 车子没年检被扣怎么办 上海车辆年检过期了怎么办 行驶证过期十天怎么办 行驶证盖章满了怎么办 驾照c证扣12分怎么办 两年小车忘年审怎么办 4年车检过期了怎么办 车检过期了1周怎么办 超过检车几天了怎么办 驾驶证扣两个6分怎么办 行驶证检验有效期过期怎么办 驾驶证被扣26分怎么办 c1驾驶证扣26分怎么办 驾驶证被扣6分后怎么办 c1驾照年审过期一天怎么办 审驾照时间过了怎么办