Angular CanActivateFn Example
February 25, 2024
On this page we will learn to use CanActivateFn
in our Angular application. Angular CanActivateFn
provides functional approach to write code for canActivate
route guard.
To perform
canActivate
route guard, we need to know following points.
1. CanActivateFn : Signature of a function to perform
canActivate
route guard.
2. canActivate : A property of
Route
interface that accepts an array of CanActivateFn
instances.
3. mapToCanActivate : Maps injectable classes containing a method named as
canActivate
to an array of equivalent CanActivateFn
.
1. CanActivateFn
CanActivateFn
is a signature of a function that will be used by canActivate
guard on a Route
.
Find the
CanActivateFn
signature from Angular doc.
(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree;
route : ActivatedRouteSnapshot instance that contains informations about a route of the currently loaded component.
state : RouterStateSnapshot instance that represents the state of router at that time.
Returns :
CanActivateFn
returns boolean, UrlTree or Observable.
1.
canActivate
is a property of Route
interface. canActivate
accepts an array of CanActivateFn
to determine if the current user is allowed to activate the component.
2. If
CanActivateFn
returns true, navigation is allowed otherwise navigation is cancelled.
3. If
CanActivateFn
returns UrlTree, the current navigation is cancelled and new navigation begins.
4. If no
CanActivateFn
is provided to canActivate
then by default any user can activate the route.
2. Using CanActivateFn
For the demo application, I am usingcanActivate
guard with a route that will be activated only when user is logged in.
Step-1 : Create a function with
CanActivateFn
signature.
const loginCanActivate: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => inject(LoginService).canActivate(route, state);
loginCanActivate
is the instance of CanActivateFn
.
Step-2 :
canActivate
is the property of Route
. The canActivate
is an array of CanActivateFn
. So assign loginCanActivate
to the route which needs to be guard.
export const ROUTES: Routes = [ { path: 'bookdetail', component: BookComponent, canActivate: [loginCanActivate] } ];
LoginService
class can be defined as below.
@Injectable({ providedIn: 'root' }) export class LoginService { constructor(private router: Router) { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { if (this.isUserLoggedIn()) { return true; } else { this.router.navigate([this.getLoginUrl()]); } console.log(state.url); return false; } isUserLoggedIn() { return true; } getLoginUrl() { return "/login"; } }
canActivate()
method with two parameters as ActivatedRouteSnapshot
and RouterStateSnapshot
. The return type of canActivate()
method is boolean and if this method returns true, user will be able to visit that route and if return value is false, user will not be able to visit that route.
Our
LoginService
class contains a method with name canActivate()
but it is not necessary to keep this name. We can use any name.
Find the sample code. Here I will create a method named as
userCanActivate
as below.
@Injectable({ providedIn: 'root' }) export class LoginService { userCanActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { ------ return true; } }
userCanActivate
method to instantiate CanActivateFn
.
const loginCanActivate: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => inject(LoginService).userCanActivate(route, state);
RouterModule
as below.
@Component({ ------ imports: [RouterModule], })
provideRouter
to configure routes.
export const APP_CONFIG: ApplicationConfig = { providers: [ provideRouter(ROUTES), ] };
3. Using Multiple CanActivateFn
Here I will createcanActivate
guard that will be activated only when user is logged in and in active state.
As we know that canActivate
accepts an array of CanActivateFn
. Here I will create two instances of CanActivateFn
, one service to check login status and another service to check user active status.
Step-1 : In my code, I am creating
loginCanActivate
and userCanActivate
as instances of CanActivateFn
.
const loginCanActivate: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => inject(LoginService).canActivate(route, state); const userCanActivate: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => inject(UserService).canActivate(); export const ROUTES: Routes = [ { path: 'bookdetail', component: BookComponent, canActivate: [loginCanActivate, userCanActivate] } ];
canActivate
guard will allow to navigate /bookdetail route only when both CanActivateFn
instances as loginCanActivate
and userCanActivate
return true. If any of these CanActivateFn
instances returns false, user will not be able to visit that route.
Step-2 : I have already created the login service above. We can define
UserService
class as following.
@Injectable({ providedIn: 'root' }) export class UserService { canActivate(): boolean { if (this.isUserActive()) { return true; } return false; } isUserActive() { return true; } }
4. mapToCanActivate
1.mapToCanActivate
maps an array of injectable classes having method named as canActivate
to an array of CanActivateFn
instances.
2.
mapToCanActivate
shortens the code to create instances of CanActivateFn
.
3. It is used as following.
export const ROUTES: Routes = [ { path: 'bookdetail', component: BookComponent, canActivate: mapToCanActivate([LoginService, UserService]) }, ];
canActivate
that should have a return type matching to CanActivateFn
return type signature. If the parameters of this canActivate
method contains ActivatedRouteSnapshot
and RouterStateSnapshot
, they will be injected by mapToCanActivate
function. In our demo application, I have injectable services as
LoginService
and UserService
having canActivate
method.
@Injectable({ providedIn: 'root' }) export class LoginService { canActivate(): boolean { ------ return true; } } @Injectable({ providedIn: 'root' }) export class UserService { canActivate(): boolean { ------ return true; } }
mapToCanActivate
, do not have canActivate
method, then code will not compile.
5. Conclusion
To implement canActivate route guard, useCanActivateFn
signature. If each instance of CanActivateFn
returns true only when user will be able to activate that route. We can also use mapToCanActivate
function that will shorten the code to implement canActivate route guard. mapToCanActivate
uses injectable services containing a method named as canActivate.