Protecting Routes using Guards in Angular 2
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?
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 either
Observable<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 returns
true
(if provider
doesn’t mean anything to you, go and check out our article on
Dependency 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 the
CanAlwaysActivateGuard
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()
, or
canDeactivate()
respectively. Those methods are pretty much the equivalent of a guard function in the previous scenario. The following snippet shows a simple
CanActivate
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 navigate
away 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 a
CanActivate
guard. All we have to do is to create again, either a function, or a class that implements the
CanDeactivate
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.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通