Search code examples
javascriptangulartypescriptangular-router

How to correctly handle 404 when using modular routing in Angular 10


I'm failing to properly handle 404 pages in an Angular 10 app using modular routing.

My code structure is as follows:

|> app
|-- app.module.ts
|-- app-routing.module.ts
|-- app.component{ts, spec.ts, scss, html}
|-> pages
|--- pages.module.ts
|--> home-page
|---- home-page.module.ts
|---- home-page-routing.module.ts
|---> home-page
|----- home-page.component{ts, spec.ts, scss, html}
|--> test-page
|---- test-page.module.ts
|---- test-page-routing.module.ts
|---> test-page
|----- test-page.component{ts, spec.ts, scss, html}

The (truncated) global app-routing module has 404 handler configured as such:

...
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';

const routes: Routes = [
  { path: '', redirectTo: '/home', pathMatch: 'full' },

  // If the following line is not commented out,
  // You are unable to navigate to either the 'Home' or 'Test' pages.
  // { path: '**', component: PageNotFoundComponent },
];

...

The problem I'm having is that Angular matches the global 404 handler in ./app/app-routing.module.ts & resolves to that route.

Here's a short stackblitz that provides an example of my experience (Note: Stackblitz sample is actually running Ng12, while our app is running Ng10 - both exhibit the same behavior)

My goal is to get a global 404 handler OR redirect working, while continuing to keep all our page routes defined in their respective module-routing files.

Can this be done, if so - how?


Solution

  • I looked at your Stackblitz and the issue is that the AppRoutingModule is imported before the PagesModule.

    You should import the PagesModule first because their routes have higher priority.

      imports: [
        BrowserModule,
        RouterModule,
        FormsModule,
        PagesModule,
        AppRoutingModule,
      ],
      declarations: [AppComponent],
      bootstrap: [AppComponent],
    })
    export class AppModule {}
    

    Another (better) solution would be to lazy load the module:

    const routes: Routes = [
      { path: '', redirectTo: '/home', pathMatch: 'full' },
      {
        path: '',
        loadChildren: () =>
          import('./pages/pages.module').then((m) => m.PagesModule),
      },
      { path: '**', component: PageNotFoundComponent },
    ];
    

    This way you wont have to import the whole PagesModule into your AppModule.