Skip to main content
Ben Nadel at cf.Objective() 2013 (Bloomington, MN) with: Adam Presley
Ben Nadel at cf.Objective() 2013 (Bloomington, MN) with: Adam Presley

Local Redirects Automatically Append The Non-Local Route Segments In Angular 4.4.4

By
Published in

Yesterday, Juri Strumpflohner taught me that a single route parameter can match multiple URL segments in Angular 4.4.4. I was planning on using this behavior to perform prefix-based redirects in my Angular application. But, as it turns out, I don't need to do this - at least not for local redirects (as opposed to absolute redirects). With a local redirect, any non-local route segments are automatically appended to the redirected route. This makes prefix-based redirecting almost too easy!

Run this demo in my JavaScript Demos project on GitHub.

The route configurations in Angular are defined using a hierarchy of partial URL patterns. At each level of this hierarchy, you can define static URL segments and value-capturing route parameters. You can also redirect to absolute locations (ie, redirects that begin with a "/"); or, you can redirect to local locations.

When redirecting to a local location within the hierarchy (ie, changing the routing behavior at-and-below the current routing node), the remaining, non-local route segments (ie, those not matched by the current configuration node) are automatically appended to the redirection. Or, in other words, a local redirect only affects the local portion of the overall route:

Local redirect in Angular 4.4.4 only affect the local segment in the URL, appending the rest of the URL to the redirect action.

As you can see, the only part of the URL affected by the "redirectTo" is the segment of the URL matched by the given node in the configuration hierarchy. This behavior makes it incredibly easy to redirect entire portions of the URL hierarchy because Angular will automatically append the remainder of the URL to any local redirect.

To see this behavior in action, I've taken my demo from yesterday and completely removed the route parameter "sink". Now, in my application module's route configuration, I simply redirect from "/a" to "/b/z", allowing any remaining portions of the URL to be appended automatically by Angular:

// 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 { BComponent } from "./b.component";

// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //

var routes: Routes = [
	// We're going to redirect all "/a" routes to the "/b/z" route. When redirecting to
	// a RELATIVE path (ie, not absolute), the remainder of the path (not matched by
	// the local pattern) will be AUTOMATICALLY appended to the redirect. As such, this
	// route configuration will replace any "/a" with "/b/z" in any route that begins
	// with the "/a" prefix (no need to create a "sink" route parameter).
	{
		path: "a",
		redirectTo: "b/z"
	},
	{
		path: "b/z",
		children: [
			// We're going to render all "/b/z"-prefix routes in the same component.
			{
				path: "**",
				component: BComponent
			}
		]
	}
];

// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //

@NgModule({
	bootstrap: [
		AppComponent
	],
	imports: [
		BrowserModule,
		RouterModule.forRoot(
			routes,
			{
				// Tell the router to use the HashLocationStrategy.
				useHash: true
			}
		)
	],
	declarations: [
		AppComponent,
		BComponent
	],
	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 {
	// ...
}

And, in my application component, I'm still providing a set of links that point to the "/a" subsystem of locations:

// Import the core angular services.
import { Component } from "@angular/core";

// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //

@Component({
	selector: "my-app",
	styleUrls: [ "./app.component.css" ],
	template:
	`
		<p>
			Try going to one of these <code>/a</code> prefix routes
			(which do not have materialized views):
		</p>

		<ul>
			<li><a routerLink="/a">/a</a></li>
			<li><a routerLink="/a/items">/a/items</a></li>
			<li><a routerLink="/a/items/4">/a/items/4</a></li>
			<li><a routerLink="/a/items/4/detail">/a/items/4/detail</a></li>
			<li><a routerLink="/a/items/4/detail" fragment="anchor">/a/items/4/detail#anchor</a></li>
			<li><a routerLink="/a/items/4/detail" [queryParams]="{ q: '1' }">/a/items/4/detail?q=1</a></li>
		</ul>

		<router-outlet></router-outlet>
	`
})
export class AppComponent {
	// ...
}

All of these "/a" links will be redirects to the "/b/z" prefix, which is rendered by the BComponent. The BComponent does nothing more than render the URL that's currently being rendered:

// Import the core angular services.
import { ActivatedRoute } from "@angular/router";
import { Component } from "@angular/core";
import { Router } from "@angular/router";
import { UrlSegment } from "@angular/router";

// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //

@Component({
	styleUrls: [ "./b.component.css" ],
	template:
	`
		<h3>
			B/Z-Prefix Component
		</h3>

		<p>
			You have navigated to <code>{{ url }}</code>
		</p>
	`
})
export class BComponent {

	public url: string;

	// I initialize the B-view component.
	constructor( activatedRoute: ActivatedRoute, router: Router ) {

		// As the user navigates through the "/a"-prefix routes, they will all be
		// redirected to the "/b/z"-prefix routes that are rendered by this component.
		// As that happens, this component will persist since we never navigate away
		// from it. As such, we have to listen for route changes to know when to update
		// the view.
		activatedRoute.url.subscribe(
			( urlSegments: UrlSegment[] ) : void => {

				this.url = router.url;

			}
		);

	}

}

Now, if we run this application in the browser and navigate to one of the "/a"-prefix links, we get the following output:

A local redirectTo will automatically append the non-local URL segments to the route redirectTo in Angular 4.4.4.

As you can see, when we click on one of the "/a"-prefix links, we are redirected to the "/b/z" route; and, any non-local portion of the remaining URL is automatically appended to the redirectTo action.

How great is that! This router behavior in Angular 4.4.4 should make it incredibly easy to perform prefix-based redirects when you need to map an entire subset of URLs onto another another subset. Of course, this only works for local redirects - if you are performing an absolute redirect, you still need to use a "sink" route-parameter as I did in my previous post.

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

Reader Comments

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