Aug 19th, 2017 - written by Kimserey with .
Few weeks ago I spoke about the functionality of the Angular Router http://kimsereyblog.blogspot.com/2017/05/attribute-route-in-asp-net-core.html. It was a brief overview of all the router features but one of the feature was not totally explain, the CanActivate
feature. From there a question emerged, what is the difference between CanActivate and CanActivateChild?. Today I will answer this question and at the same time discussing extra behaviours of the router.
Sample is available on my GitHub.
As we saw in my previous post, CanActivate
is a interface with a single function canActivate(...)
.
1
2
3
4
5
6
@Injectable()
export class GuardTest implements CanActivate, CanActivateChild {
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return of(true);
}
}
And is used on the route:
1
2
3
4
5
6
{
path: 'guards/:id',
canActivate: [
GuardTest
]
}
Similarly CanActivateChild
is a single function canActivateChild(...)
.
1
2
3
4
5
6
7
8
@Injectable()
export class GuardTest implements CanActivate, CanActivateChild {
canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return of(true);
}
}
And can be used on the route:
1
2
3
4
5
6
{
path: ':something/guards',
canActivateChild: [
GuardTest
]
}
In order to understand the difference, we first need to understand how the router activate a particular route.
Let’s consider the following routes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
path: 'guards/:something',
canActivate: [
GuardTest
],
children: [
{
path: ':somethingelse',
component: GuardComponent,
canActivate: [
Guard2Test
]
}
]
}
This is a single route which when activated, instantiate the GuardComponent.
The first time /guards/x/y is activated, canActivate
from GuardTest
on the parent /guards/x
is invoked and then canActivate
on the child Guard2Test
get invoked.
From /guards/x/y
, if we try to activate the route /guards/s/t
, both guards get activated as if we completely changed route.
From /guards/x/y
, if we try to activate the route /guards/x/z
, __the parent will not invoke canActivate
, only the child Guard2Test
will get invoked.
There lies the difference between CanActivate and CanActivateChild.
If we need to run a guard when only the child route changes, we need to use CanActivateChild
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
path: 'guards/:something',
canActivateChild: [
GuardTest
],
children: [
{
path: ':somethingelse',
component: GuardComponent,
canActivate: [
Guard2Test
]
}
]
}
GuardTest
will still run even though coming from guards/x/y
and trying to activate guards/x/z
.
It used to be possible to redirect using the router within the guard. But this behaviour has changed.
Now the guard invoke is expected to complete before any navigation. Meaning the Observable<boolean>
will need to complete (regardless of the result), in order for a navigation to occur.
For example this following sample will never redirect:
1
2
3
4
5
canActivate(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
// this will never redirect and will get stuck
this.router.navigate(['/']);
return Observable.never();
}
In 2. we accessed the same route /guards/:something/:somethingelse
but using different values.
The resulting route displayed the GuardComponent
.
Angular router is optimized to reuse the same component when the route is re-entered with new data.
The consequence of this behaviour is that the constructor and OnInit function are only called once, the first time the component is instantiated.
Another important point is that a new value is pushed to the Observable
route params. Therefore is the component is accessible through multiple routes, it is better to use the route params as Observable
instead of the snapshot.
Today we saw what were the differences between the Angular route CanActivate
guard and CanActivateChild
guard. We also saw in which situation we could end up in a deadlock when the guard never end and the navigations never occur. Lastly we saw in which situation components were instantiated and when new values on the route params observable get pushed. Hope this post was helpful, if you have any question leave it here or hit me on Twitter @Kimserey_Lam. See you next time!