Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at cf.Objective() 2009 (Minneapolis, MN) with: Samer Sadek
Ben Nadel at cf.Objective() 2009 (Minneapolis, MN) with: Samer Sadek@samer_sadek )

Wildcard Routes (**) Can Be Scoped To Route Sub-Trees In Angular 5.1.3

By Ben Nadel on

In the Angular router, you can use the wildcard path - ** - as a catch-all route to render things like a "Not Found" view or to redirect the user back to the root of the application. But, from the documentation, and from many of the Angular Router demos that I've seen, one aspect that's usually omitted (or unclear) is that an application can have more than one wildcard route. And, that a wildcard route can be scoped to a sub-tree of an application. As such, I wanted to put together a quick demo to showcase this wildcard route feature in Angular 5.1.3.


 
 
 

 
 
 
 
 

Run this demo in my JavaScript Demos project on GitHub.

Most of the code in this Angular 5.1.3 demo is immaterial - it just lays the ground-work for router-outlets and rendered views. The only important part of this application is the Router configuration in the application module:

  • // Import the core angular services.
  • import { BrowserModule } from "@angular/platform-browser";
  • import { NgModule } from "@angular/core";
  • import { RouterModule } from "@angular/router";
  • import { Routes } from "@angular/router";
  •  
  • // Import the application components and services.
  • import { AppComponent } from "./app.component";
  • import { AViewComponent } from "./a-view.component";
  • import { BViewComponent } from "./b-view.component";
  • import { SubViewComponent } from "./sub-view.component";
  •  
  • // ----------------------------------------------------------------------------------- //
  • // ----------------------------------------------------------------------------------- //
  •  
  • var routes: Routes = [
  • {
  • path: "app",
  • children: [
  • {
  • path: "a",
  • component: AViewComponent,
  • children: [
  • {
  • path: "sub",
  • component: SubViewComponent
  • },
  •  
  • // This is a WILDCARD CATCH-ALL route that is scoped to the "/app/a"
  • // route prefix. It will only catch non-matching routes that live
  • // within this portion of the router tree.
  • {
  • path: "**",
  • redirectTo: "/app/a"
  • }
  • ]
  • },
  • {
  • path: "b",
  • component: BViewComponent,
  • children: [
  • {
  • path: "sub",
  • component: SubViewComponent
  • }
  • ]
  • }
  • ]
  • },
  •  
  • // Redirect from the root to the "/app" prefix (this makes other features, like
  • // secondary outlets) easier to implement later on.
  • {
  • path: "",
  • pathMatch: "full",
  • redirectTo: "app"
  • },
  •  
  • // This is the WILDCARD CATCH-ALL route that is scoped to the entire application. It
  • // will catch any request that is not matched by an earlier route definition.
  • {
  • path: "**",
  • redirectTo: "/app"
  • }
  • ];
  •  
  • @NgModule({
  • bootstrap: [
  • AppComponent
  • ],
  • imports: [
  • BrowserModule,
  • RouterModule.forRoot(
  • routes,
  • {
  • // Tell the router to use the HashLocationStrategy.
  • useHash: true,
  • enableTracing: true
  • }
  • )
  • ],
  • declarations: [
  • AppComponent,
  • AViewComponent,
  • BViewComponent,
  • SubViewComponent
  • ],
  • providers: [
  • // CAUTION: We don't need to specify the LocationStrategy because we are setting
  • // the "useHash" property in the Router module above.
  • // --
  • // {
  • // provide: LocationStrategy,
  • // useClass: HashLocationStrategy
  • // }
  • ]
  • })
  • export class AppModule {
  • // ...
  • }

As you can see, there are two places in which I am using the wildcard (**) route:

  • In the root of route tree.
  • In the sub-section of the route tree scoped to "/app/a".

The root one redirects the user back to the root of the application. And, the sub-section one redirects the user back to the root of the "/app/a" sub-tree. One thing to note about any use of the "**" catch-all path is that it has to be the only segment in the path - it cannot be a substring of the path:

  • path: "**" // <--- This works.
  • path: "prefix/**" // <--- This DOES NOT work.

That's why I needed the sub-tree "**" to be a child of the "a" parent segment, not a sibling to it.

That said, if we run this demo and navigate to the "/app/c" path, which does not exist in the route configuration, we get the following redirect:


 
 
 

 
 Using the ** wildcard route handler at the root of an Angular 5.1.3. application. 
 
 
 

As you can see, when we redirect to the non-existing route "/app/c", the root-level "**" catch-all route matches as the last possible match and then redirects the user back to the root of the application.

Now, if we navigate to a non-existing route within the "/app/a" sub-tree, we get the following redirect:


 
 
 

 
 Using the ** wildcard route-handler in a sub-tree of the router configuration in an Angular 5.1.3 application. 
 
 
 

As you can see, this time, the missing route is handled by the "/app/a" level "**" catch-all and redirects us back to the root of the "/app/a" sub-tree, not to the root of the application.

From a modularity stand-point, this makes a lot of sense - as you plug different routing modules into the Angular application, each module should be able to define and handle its own missing-route logic. But, it's a point that is easy to miss when you're reading through the Odyssean tome that is the Angular Router documentation. I've been digging into the router for months now, and this feature only recently became evident to me. So, hopefully this helps others who may have been confused on the matter in Angular 5.1.3.



Looking For A New Job?

Ooops, there are no jobs. Post one now for only $29 and own this real estate!

100% of job board revenue is donated to Kiva. Loans that change livesFind out more »

Reader Comments

@All,

In this post, I talked about this use of "**" as being good for "modularity". But, I broke that modularity by using an absolute path in the redirect:

redirectTo: "/app/a"

This ends-up coupling the feature-module to the overall URL architecture of the application. However, it doesn't have to be this way. In a quick follow-up post, I demonstrate (after just discovering it) that that a "**" catch-all route can use a *relative* redirect:

path: "**"
redirectTo: ""

... read more about it here: https://www.bennadel.com/blog/3401-wildcard-routes-can-redirect-relative-to-their-urltree-location-in-angular-5-1-3.htm

I wish I had thought to test this yesterday.

Reply to this Comment

I have tried by putting path: "prefix/#" and it is getting redirecting to random link in google is there any way to handle the same

Reply to this Comment

@Vaibhav,

I have no idea why you are getting redirected to a random page on Google. It sounds like that might be a browser issue (like maybe you have your browser configured to use "I'm Feeling Lucky" search results and your URL is somehow hooking into that). I've not seen this behavior before.

Reply to this Comment

Just what I needed. I am doing a app that consist of 2 "sub apps". Being able to have multiple default routes is great, thanks

Reply to this Comment

@Johan,

Very cool - glad this was helpful. The Router really does make some powerful strides towards being able to keep modules very encapsulated. There are some rough patches to get through; but, each release of Angular seems to chip away at the issues.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.