Angular canMatch Guard Example
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>
Promise
:
(route: Route, segments: UrlSegment[]) => Promise<boolean | UrlTree>
boolean
:
(route: Route, segments: UrlSegment[]) => boolean;
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 bycanMatch
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)] };
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'
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 { }
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] }, ];
canMatchConsumer canMatchIndustry canActivateProduct
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
.