Angular canMatch Guard Example

By Arvind Rai, January 01, 2024
On this page, we will learn Angular canMatch route guard. canMatch, a property of Route interface, is an array of CanMatchFn that determines if the current user is allowed to match the route. If canMatch route guard is not used, any route can match.
CanMatchFn is a function signature to create canMatch guard. If CanMatchFn returns true, navigation continues. If CanMatchFn returns false, the Route is skipped and look for other matching routes.
The signature of CanMatchFn is as below.
1. For Observable :
(route: Route, segments: UrlSegment[])
               => Observable<boolean | UrlTree> 
2. For Promise :
(route: Route, segments: UrlSegment[]) 
             => Promise<boolean | UrlTree> 
3. For boolean :
(route: Route, segments: UrlSegment[]) => boolean; 
4. For UrlTree :
(route: Route, segments: UrlSegment[]) => UrlTree; 

Using CanMatchFn

In my demo application, using /productdetails path a user can get product details. Products can be related to consumer and industry. Using /productdetails path, user accesses either consumer products or industrial products and the decision is taken by canMatch route guard.
I have two components for the product i.e. ConsumerProductsComponent for consumer and IndustrialProductsComponent for industry.
In my example I have created two functions for CanMatchFn.
const canMatchCProduct: CanMatchFn = (route: Route, segments: UrlSegment[]) => {
    return inject(ProductGuards).isConsumer(inject(ProductService), route, segments);
};
const canMatchEProduct: CanMatchFn = (route: Route, segments: UrlSegment[]) => {
    return inject(ProductGuards).isIndustry(inject(ProductService), route, segments);
};

export const routes: Routes = [
    {
        path: 'productdetails',
        component: ConsumerProductsComponent,
        canMatch: [canMatchCProduct]
    },
    {
        path: 'productdetails',
        component: IndustrialProductsComponent,
        canMatch: [canMatchEProduct]
    }
]; 

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes)]
}; 
1. If canMatchCProduct of first route returns true, for the /productdetails path user will access ConsumerProductsComponent.
2. If canMatchCProduct of first route returns false, route is skipped and look for other matching routes i.e. IndustrialProductsComponent and canMatchEProduct will execute. If canMatchEProduct returns true, user will access IndustrialProductsComponent for /productdetails path.
3. If for /productdetails path, no component matches then exception will be thrown as below.
ERROR Error: NG04002: Cannot match any routes. URL Segment: 'productdetails' 
We can avoid such error by creating a component that can be accessed when all canMatch for the /productdetails path returns false.
Add one more path to route array.
{
    path: 'productdetails',
    component: AnyProductComponent
} 

Components and Services
Find the components and services used in demo application.
@Injectable({
   providedIn: 'root'
})
export class ProductGuards {
   isConsumer(productService: ProductService, route: Route, segments: UrlSegment[]): boolean {
      return false;
   }
   isIndustry(productService: ProductService, route: Route, segments: UrlSegment[]): boolean {
      return true;
   }
}

@Injectable({
   providedIn: 'root'
})
export class ProductService {

}

@Component({
	standalone: true,
	imports: [],
	template: 'Consumer Product Details'
})
export class ConsumerProductsComponent {

} 

@Component({
	standalone: true,
	imports: [],
	template: 'Industrial Product Details'
})
export class IndustrialProductsComponent {

} 
Output
Industrial Product Details 

canMatch vs canActivate

1. canMatch route guard uses CanMatchFn function signature and canActivate route guard uses CanActivateFn function signature.
2. The order of route guard execution is that first canMatch executes then canActivate for a route.
3. canActivate controls whether a user has access to a route and canMatch controls if the route can be matched.
4. If canActivate returns false, user cannot access path, no matter canMatch returns true.
5. If canMatch and canActivate both returns true, only when user will be able to access that path.
6. canMatch route guard executes first and if returns false, route is skipped without executing canActivate guard function.

Suppose we have two routes with same path as productdetails and both routes loads different components as ConsumerProductsComponent and IndustrialProductsComponent and both routes uses canMatch and canActivate guards.
const canMatchConsumer: CanMatchFn = (route: Route, segments: UrlSegment[]) => {
    console.log("canMatchConsumer");
    return false;
};
const canMatchIndustry: CanMatchFn = (route: Route, segments: UrlSegment[]) => {
    console.log("canMatchIndustry");
    return true;
};
const canActivateProduct: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
    console.log("canActivateProduct");
    return true;
}

export const routes: Routes = [
    {
        path: 'productdetails',
        component: ConsumerProductsComponent,
        canMatch: [canMatchConsumer],
        canActivate: [canActivateProduct]
    },
    {
        path: 'productdetails',
        component: IndustrialProductsComponent,
        canMatch: [canMatchIndustry],
        canActivate: [canActivateProduct]
    },
]; 
Output
canMatchConsumer
canMatchIndustry
canActivateProduct 
When user access /productdetails path, following steps take place.
a. First Angular routing executes canMatch: [canMatchConsumer] for first route associated with ConsumerProductsComponent. As canMatchConsumer returns false so canActivate: [canActivateProduct] will not execute for this component path. Route will be skipped for the next matching path.
b. Now routing will execute canMatch: [canMatchIndustry] for second route associated with IndustrialProductsComponent that returns true. As canMatch returns true, so canActivate: [canActivateProduct] will execute for this component that also returns true. Hence for the /productdetails path, user will access IndustrialProductsComponent.

References

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI







©2024 concretepage.com | Privacy Policy | Contact Us