Reputation: 355
Angular 2 router guards can be defined in an array. for example:
<code>
canActivate: ['CanAlwaysActivateGuard','AuthGuard']
</code>
following are my questions:
Upvotes: 22
Views: 12190
Reputation: 57774
The guards will be executed one after the other (left to right). Guards don't wait for the guards that preceed them on the list to return a value. That means, for example:
boolean
or a UrlTree
), then the first one will execute first, and the second one will also always execute, regardless of whether the first guard returned true
or something else.Promise
or Observable
that doesn't immediately resolve), then both requests will always be fired.There's no native way to achieve this, but starting from Angular 15, you can write a pretty elegant generic wrapper that functional guards. For example, for synchronous guards it could look like this:
const orderedSyncGuards =
(guards) =>
(route, state) =>
guards.every(guard =>
inject(guard).canActivate(route, state));
const ROUTE = {
...
canActivate: [orderedSyncGuards([FirstGuard, SecondGuard])]
The code for async guards can be found here.
Upvotes: 0
Reputation: 4095
If you nest them, you can decide their order:
import { Routes } from '@angular/router';
const routes: Routes = [
{
path: '',
canActivate: [CanAlwaysActivateGuard],
children: [
{
path: '',
canActivate: [AuthGuard],
children: [
// (...)
],
}
],
}
];
An outer route guard is evaluated before any inner route guards.
Upvotes: 6
Reputation: 31
For the current version, you can use child router, for example:
{
path: 'your-path',
canActivate: [
CanAlwaysActivateGuard,
],
canActivateChild: [
AuthGuard,
],
children: [
]
}
Upvotes: 1
Reputation: 1101
You can also inherit the guard run super.canActivate()
before the AuthGuard.canActivate().
In this example
canActivate()
method in both classes returns aPromise
.
@Injectable({
providedIn: 'root',
})
class AuthGuard extends CanAlwaysActivateGuard implements CanActivate {
constructor(/*...*/) {
super(/*...*/);
}
async canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean | UrlTree> {
const result = await super.canActivate(next, state);
if (result === true) { // Check the expected result from the parent
return await this.logicOfYourAuthGuard(); // Run your logic and return it
}
return result; // Return the original result if logic of AuthGuard is not nedded
}
}
And then, in the route, use only AuthGuard
.
Other option is to use canActivateChild
, because canActivate
guards runs before canActivateChild
.
Upvotes: 2
Reputation: 156
I had this problem and came across your post in my search for answers. Finding none -- and having some time today -- I came up with a solution I thought you might be interested in. I created a "CompositeRouteGuard":
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import {Injectable, Injector} from '@angular/core';
import { Observable } from "rxjs";
@Injectable()
export class CompositeRouteGuard implements CanActivate {
constructor( protected router: Router,
protected injector: Injector ) {
}
canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable<boolean> {
var compositeCanActivateObservable: Observable<boolean> = Observable.of(true);
let routeGuards = route.data.routeGuards;
if(routeGuards){
for( var i = 0; i < routeGuards.length; i++ ){
let routeGuard = this.injector.get(routeGuards[i]);
let canActivateObservable = routeGuard.canActivate(route, state);
compositeCanActivateObservable = compositeCanActivateObservable.flatMap( (bool) => {
if(!bool){
return Observable.of(false);
}
else{
return canActivateObservable;
}
});
}
}
return compositeCanActivateObservable;
}
}
That requires a little extra configuration in your routes.ts. You need to add a "routeGuards" array to the data element of the route.
const routes: Routes = [
{
path: '...',
component: AComponent,
data: { routeGuards: [ FooRouteGuard, BarRouteGuard, BazRouteGuard ] },
canActivate: [ CompositeRouteGuard ]
},
...
I only cared about the canActivate action, but you should be able to easily extend this, to say "canDeactivate" (for example) if you need to.
Now my route guards run "in order" with "and" semantics (all must succeed for route to be activated).
Upvotes: 11
Reputation: 105459
what will be the order of execution for both the guards.
They will be run synchronously without waiting for each other.
if i want to execute AuthGuard only if CanAlwaysActivateGuard returns true, would that be possible.
No, it's not possible with current implementation. As a workaround you can create a wrapper guards to run your guards in order.
Also see this How to wait for guards in Angular.
Upvotes: 13