Angular CanActivateChildFn Example

By Arvind Rai, 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; 
Parameters :
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>
  ------
} 
2. canActivateChild route guard allows to navigate route only when all instances of 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;
  }
} 
The function 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(); 
Now find the code snippet to use multiple instances of CanActivateChildFn.
{
  path: 'books',
  component: BookComponent,
  canActivateChild: [
     permissionCanActivateChildFn,
     roleCanActivateChildFn
  ],
  children: [
  ]
} 
We can create 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 import RouterModule.
2. Router is configured using provideRouter as below.
export const APP_CONFIG: ApplicationConfig = {
  providers: [
    provideRouter(ROUTES, withComponentInputBinding()),
  ]
}; 
3. withComponentInputBinding enables the application to access URL parameters using @Input() decorator directly.

5. References

6. Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us