Angular CanActivateChildFn Example
February 26, 2024
On this page we will learn to create canActivateChild
route guard using CanActivateChildFn
in our angular application. Angular Route
provides canActivateChild
property that is assigned with array of CanActivateChildFn
instances.
CanActivateChildFn : Function signature used to create
canActivateChild
route guard.
mapToCanActivateChild : Function that creates array of
CanActivateChildFn
instances using injectable services.
1. CanActivateChildFn
CanActivateChildFn
is a signature of a function to create canActivateChild
route guard. Find its signature from Angular doc.
(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot) => Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree;
1. childRoute is the instance of
ActivatedRouteSnapshot
that contains the information about currently loaded route.
2. state is the instance of
RouterStateSnapshot
that represents the state of the router at a moment in time.
Returns :
Returns Observable, boolean or UrlTree.
1. To create the canActivateChild route guard, create the instances of
CanActivateChildFn
and assign its array to canActivateChild
property of Route
interface. Find the canActivateChild
property signature.
interface Route { canActivateChild?: Array<CanActivateChildFn> ------ }
CanActivateChildFn
in array, return true. If any of the instance returns false, navigation is cancelled.
3. If
CanActivateChildFn
returns UrlTree
, then current route navigation is cancelled and a new navigation starts to that UrlTree
.
4. To create instances of
CanActivateChildFn
, we can also use mapToCanActivateChild
function that shortens the code writing for canActivateChild route guard.
2. Using CanActivateChildFn
Step-1 : To perform canActivateChild route guard, I am creating an injectable service.@Injectable({ providedIn: 'root' }) export class PermissionService { userCanActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { if (this.isPermitted()) { return true; } console.log(state.url); return false; } isPermitted() { return true; } }
userCanActivateChild()
will return true if user is permitted to access that route. If return value is false, user will not be able to access that route.
Step-2 : Instantiate
CanActivateChildFn
by using service PermissionService
class. Use inject
to inject service.
const permissionCanActivateChildFn: CanActivateChildFn = ( route: ActivatedRouteSnapshot, state: RouterStateSnapshot, ) => inject(PermissionService).userCanActivateChild(route, state); export const ROUTES: Routes = [ { path: 'books', component: BookComponent, canActivateChild: [permissionCanActivateChildFn], children: [ { path: 'detail/:id', component: BookDetailComponent, } ] } ];
canActivateChild
is the property of Route
interface that is assigned with an array of CanActivateChildFn
. The child route /books/detail/11 will load BookDetailComponent
. We can guard all the children of /books path using canActivateChild
. If permissionCanActivateChildFn returns true, user will be able to access BookDetailComponent
otherwise not.
Step-3 : If we pass more than one instances of
CanActivateChildFn
, user will be able to access children only when all instances of CanActivateChildFn
return true. If any of the instance returns false, route guard will not allow to access the children routes.
To use multiple
CanActivateChildFn
instances, find one more instance of CanActivateChildFn
.
const roleCanActivateChildFn: CanActivateChildFn = ( route: ActivatedRouteSnapshot, state: RouterStateSnapshot, ) => inject(UserService).roleCanActivateChild();
CanActivateChildFn
.
{ path: 'books', component: BookComponent, canActivateChild: [ permissionCanActivateChildFn, roleCanActivateChildFn ], children: [ ] }
UserService
class as below.
@Injectable({ providedIn: 'root' }) export class UserService { roleCanActivateChild(): boolean { if (this.isRoleEditor()) { return true; } return false; } isRoleEditor() { return true; } }
3. Using mapToCanActivateChild
mapToCanActivateChild
makes it easy to create canActivateChild route guard. Just pass an array of injectable classes containing canActivateChild()
function to mapToCanActivateChild()
and it will return an array of CanActivateChildFn
instances.
{ path: 'books', component: BookComponent, canActivateChild: mapToCanActivateChild([PermissionService, UserService]), children: [ ] }
PermissionService
and UserService
classes must a have functions named as canActivateChild
. User will be able to access children routes only if canActivateChild
function of all classes of array return true.
Find the service classes containing
canActivateChild()
function name used in our demo.
@Injectable({ providedIn: 'root' }) export class PermissionService { canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { ------ return true; } } @Injectable({ providedIn: 'root' }) export class UserService { canActivateChild(): boolean { ------ return true; } }
4. Configuring Router
1. In standalone application, our routing components need to importRouterModule
.
2. Router is configured using
provideRouter
as below.
export const APP_CONFIG: ApplicationConfig = { providers: [ provideRouter(ROUTES, withComponentInputBinding()), ] };
withComponentInputBinding
enables the application to access URL parameters using @Input()
decorator directly.