Wildcard Routes (**) Can Be Scoped To Route Sub-Trees In Angular 5.1.3
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:
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:
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.
Want to use code from this post? Check out the license.
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: 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.
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
@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.
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
@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.