Skip to main content
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Vicky Ryder
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Vicky Ryder ( @fuzie )

Wildcard Routes (**) Can Redirect Relative To Their UrlTree Location In Angular 5.1.3

By on

Yesterday, I demonstrated that wildcard routes (**) can be scoped to a route sub-tree in Angular 5.1.3. I described this behavior as good for "modularity" in that it made it possible for a feature module to handle invalid URLs within its own local logic. But, the "redirectTo" route configuration that I used in the demo was anything but "modular" - it redirected to an absolute path in the application. As a quick follow-up post, I wanted to demonstrate that a wildcard route can redirect to a URL that is relative to its place in the UrlTree. This allows feature modules to contain truly modular route definitions in Angular 5.1.3.

Run this demo in my JavaScript Demos project on GitHub.

In yesterday's post, I had a "**" catch-all segment that used an absolute URL redirect:

  • /app/a/(path: **) => (redirectTo: "/app/a") => /app/a

This worked; but, of course, coupled my local URL logic to the fact that the entire application was below some "/app" prefix. To decouple the local portion of the URL tree from the app prefix, we can change the "redirectTo" value to use a location-relative path:

  • /app/a/(path: **) => (redirectTo: "") => /app/a
  • /app/a/(path: **) => (redirectTo: "sub") => /app/a/sub

By using a redirectTo value of "" (empty string), we can place the user at the local-root of the router sub-tree. And, by using a redirectTo value of "sub", we can place the user at the "sub" path of the router sub-tree. The point is, the "redirectTo" value can be used to define a segment that is relative to its parent segment.

To see this in action, I've modified the router configuration in yesterday's demo to use a relative path in the "**" catch-all route. Nothing else has changed:

// 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. And, by redirecting to the
					// empty string (redirectTo: ""), we'll be leaving the user at the
					// local root of the "./a" sub-tree. This way, we can catch missing
					// local routes without having to know the location of the sub-tree
					// in the overall route architecture.
					{
						path: "**",
						redirectTo: ""
						// NOTE: Using (redirectTo: "sub") would have redirected to "sub"
						// route.
					}
				]
			},
			{
				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, the catch-all route (**) is redirecting the user to the "" segment. Which will leave them in the root of the "/app/a" UrlTree. And, if we run this in the browser and try to navigate to the invalid route within the "/app/a" sub-tree, we get the following output:

The wildcard route (**) can redirect to a relative location in the UrlTree.

As you can see, the use of the relative (redirectTo: "") left the user in the local root of the "/app/a" sub-tree. We were able to handle the feature module redirect logic without coupling the feature module to the overall URL architecture of the application.

I should have mentioned this yesterday. But, to be honest, I didn't even think that this would work. I only thought to test it after I had already posted yesterday's demo. And, when I tried it this morning, I was surprised and delighted that it worked.

Want to use code from this post? Check out the license.

Reader Comments

1 Comments

Thanks, Ben,

have visited your page a couple times now - very helpful! I find Google's own documentation --extremely-- tiresome, cumbersome and failing in so many other ways (especially when it's 5am and I am short in patience, as it is right now^^)

How often do you go to programming/tech events? Seems you hang out daily :D

Thanks again.
M

15,688 Comments

@Mark,

Thanks for the kind words! It is much appreciated. I know what you mean about the Angular docs. There are a lot of them; it can be hard to find the one specific feature details that are you are looking for. I'm glad that we community members can help fill in some of the little gaps :D

I've been trying to go to more events lately, specifically Meet Ups. Actually, not for the past month or two; but, I'm hoping to get back into it again.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel