Past experience
While implementing default route (path: ''), most of of the articles and blogs use 'pathMatch'
with value 'full'
. When I was implementing the same in my previous project, I simply copied it due to time constraint and it worked. With basic research, I understood, ''
will match with other routes "always" and so that might create cyclic redirection if pathMatch: 'full'
is not defined.
Then I concluded, for empty paths, I just need to put
pathMatch: 'full'
to make it work.
{ path: '', component: HomeComponent, pathMatch: 'full' }
New Day, new project
Now I was starting a new project where I had multiple <router-outlets>
in nested form. And I wanted to make this app load faster by splitting codebase on route level. That is each route will have separate JavaScript chunks to keep main.js as small as possible and the initial load time will be faster.
// app-routing.module
const routes: Routes = [
{
path: '',
loadChildren: () => import('./modules/home/home.module').then(x => x.HomeModule),
pathMatch: 'full'
},
{ path: 'login', loadChildren: () => import('./modules/login/login.module').then(x => x.LoginModule) },
{ path: '404', loadChildren: () => import('./modules/errors/page-not-found/page-not-found.module').then(x => x.PageNotFoundModule) },
{ path: 'unauthorized', loadChildren: () => import('./modules/errors/unauthorised/unauthorised.module').then(x => x.UnauthorisedModule) },
{
path: 'network-error',
loadChildren: () => import('./modules/errors/network-error/network-error.module').then(x => x.NetworkErrorModule)
},
{ path: '**', redirectTo: '404' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
And inside HomeModule
I wanted to keep other routes that user can enter after the login process.
// home-routing.module.ts
const routes: Routes = [
{
path: '',
component: HomeComponent,
children: [
{ path: 'dashboard', loadChildren: () => import('../dashboard/dashboard.module').then(x => x.DashboardModule) },
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
{ path: 'simcards', loadChildren: () => import('../simcards/simcards.module').then(x => x.SimcardsModule) },
{ path: 'requests', loadChildren: () => import('../requests/requests.module').then(x => x.RequestsModule) }
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class HomeRoutingModule {}
I expected it to work just like before, as nothing much is changed. I only replaced component
, with loadChildren
, but it gave this error
ERROR Error: Uncaught (in promise): NoMatch$1: {}
at resolvePromise (zone-evergreen.js:1213)
at resolvePromise (zone-evergreen.js:1167)
at zone-evergreen.js:1279
at ZoneDelegate.invokeTask (zone-evergreen.js:406)
at Object.onInvokeTask (core.js:28522)
at ZoneDelegate.invokeTask (zone-evergreen.js:405)
at Zone.runTask (zone-evergreen.js:178)
at drainMicroTaskQueue (zone-evergreen.js:582)
I was trying to understand what went wrong. Completely frustrated and clueless, because I was supposed to build the pages and work on new feature, but here I am, stuck at this stupid route configuration.
After lot (a lot) of dubugging, i figured out the routes defined inside HomeModule is not recognised. (yeah, the error was not very friendly and intuitive, neither was it pointing to any specific file like Angular usually does). And this error was new for me.
I know how nested routes work and how lazy loading works. But this is the first time, I am mixing both of of them, using lazy loaded modules inside nested routes. Googling made me realize, there is not many articles where people use both. :'-( But luckily I found one or two that was pointing me to the pathMatch
keyword in the route configuration. I wanted to learn it properly this time, and this video helped me understand, as to why is pathMatch
used in the first place.
So eventually I updated app-routing with this:
//app-routing.module.ts
const routes: Routes = [
{
path: '',
loadChildren: () => import('./modules/home/home.module').then(x => x.HomeModule),
pathMatch: 'prefix' // 'full' will not trigger child routes
}, // more on pathMatch: https://www.youtube.com/watch?v=h33FECmtLAM
{ path: 'login', loadChildren: () => import('./modules/login/login.module').then(x => x.LoginModule) },
{ path: '404', loadChildren: () => import('./modules/errors/page-not-found/page-not-found.module').then(x => x.PageNotFoundModule) },
{ path: 'unauthorized', loadChildren: () => import('./modules/errors/unauthorised/unauthorised.module').then(x => x.UnauthorisedModule) },
{
path: 'network-error',
loadChildren: () => import('./modules/errors/network-error/network-error.module').then(x => x.NetworkErrorModule)
},
{ path: '**', redirectTo: '404' }
];
Conclusion
{ path: 'home', component: HomeComponent, pathMatch: 'full' }
If you have configured a route like above, Angular will check, only if full path in browser URL
is exactly equal to 'home' (example.com/home), then that component/module will be triggered. When you try navigating to 'home/child-route', the full path is now not matching with 'home', and this gives error.
use pathMatch: 'prefix', especially if that route
path
is supposed to load a module dynamically, which will have other routes
If you were wondering about this solution:
{ path: 'home', component: HomeComponent, pathMatch: 'full' }
{ path: 'home/child-route', component: HomeChildComponent, }
it does not work in the nested-route. The HomeChildComponent
will be rendered in parent (the one present in app.component.html), but I want the HomeChildComponent
to be rendered in <router-outlet>
defined in home.component.html
Hope this article save someone's time.
You got a better solution? I am listening. :-)
Top comments (2)
thank you!
This is a great insight to 'pathMatch' routing with lazyloading.