Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at TechCrunch Disrupt (New York, NY) with: Aaron Foss and Mark C. Webster
Ben Nadel at TechCrunch Disrupt (New York, NY) with: Aaron Foss@aaronfoss ) and Mark C. Webster@markcwebster )

Using "replaceUrl" In Order To Honor The Back-Button While Chaining Absolute Redirects In Angular 7.2.13

By Ben Nadel on

Over the weekend, I took a look at using a transient component to side-step the limitations of the Router, allowing absolute and local redirects to be chained in Angular 7.2.13. In that post, I stated that the downside of using a transient component for the redirect was the router.navigateByUrl() method broke the Browser's natural "Back Button" behavior. Which is true, by default. However, we can easily honor the Browser's Back Button behavior by adding the "replaceUrl" property to the Navigation Extras of our router.navigateByUrl() call. As such, I wanted to put together a super quick follow-up post in Angular 7.2.13.


 
 
 

 
 
 
 
 

Run this demo in my JavaScript Demos project on GitHub.

View this code in my JavaScript Demos project on GitHub.

To quickly recap my previous post, the Angular Router doesn't allow local redirects to be performed after absolute redirects. This is "by design"; and, something that I can't really explain. So, to get around this Router limitation, I created a transient component that allows the "redirectTo" functionality to be moved out of the native Router configuration and into a Router.navigateByUrl() method call.

To demonstrate this, I created a Router configuration that would bounce the user across multiple absolute redirects followed by a local redirect:

/ping --> /pong --> /people --> ./list

Here is the relevant Router configuration:

  • RouterModule.forRoot(
  • [
  • {
  • path: "people",
  • children: [
  • // NOTE: Local redirect from (/people) to (/people/list).
  • {
  • path: "",
  • pathMatch: "full",
  • redirectTo: "list"
  • },
  • {
  • path: "list",
  • component: PeopleListComponent
  • }
  • ]
  • },
  •  
  • // SCENARIO: We have an old route for "/users" that we now want to
  • // redirect to the "/people" route so that old links continue to work.
  • // Only, this WILL BREAK because the "/people" route has a local redirect
  • // to the "list" end-point. This WILL NOT BE HONORED as local redirects
  • // are never followed after an absolute redirect.
  • {
  • path: "users",
  • redirectTo: "/people" // <---- WILL NOT WORK (like we want it to).
  • },
  •  
  • // To get AROUND the ABSOLUTE REDIRECT limitation, we can use a transient
  • // component. While the in-built "redirectTo" command won't allow for
  • // subsequent local redirects, the Router.navigateByUrl() method will.
  • // Meaning, an absolute URL navigation can be followed by local redirects
  • // in the Router configuration. To demonstrate, we'll chain a few
  • // absolute redirects that will eventually consume the local "people"
  • // redirect (to people/list).
  • {
  • path: "ping",
  • component: AbsoluteRedirectComponent,
  • data: {
  • redirectTo: "/pong" // <--- absolute redirect to FOLLOWING route.
  • }
  • },
  • {
  • path: "pong",
  • component: AbsoluteRedirectComponent,
  • data: {
  • redirectTo: "/people" // <--- absolute redirect to PEOPLE route.
  • }
  • }
  • ],
  • {
  • // Tell the router to use the hash instead of HTML5 pushstate.
  • useHash: true,
  •  
  • // Enable the Angular 6+ router features for scrolling and anchors.
  • scrollPositionRestoration: "enabled",
  • anchorScrolling: "enabled",
  • enableTracing: false
  • }
  • )

As you can see, in the "ping" and "pong" routes, I have a "redirectTo" property; but, it's not part of the native configuration. Instead, it's in the "data" property where it will be consumed by the AbsoluteRedirectComponent.

Now, in my previous post, the AbsoluteRedirectComponent executed the Router.navigateByUrl() method as follows:

  • public ngOnInit() : void {
  •  
  • console.warn( "Absolute redirect to:", this.redirectTo );
  • // NOTE: We could have performed the .navigateByUrl() in the constructor.
  • // However, doing so would have emitted a "navigation canceled" event. By waiting
  • // until the init method, we allow the previous navigation to complete before we
  • // start the new navigation. This feel more in alignment with the way the built-
  • // in "redirectTo" property works.
  • this.router.navigateByUrl( this.redirectTo );
  •  
  • }

This brought the user to the correct URL; however, if the user tried to navigate "Back" through the browser's history, they would have been immediately forced forward by the redirect.

To get around this, we can tell the Router to "replace the URL" during that navigation. This will replace the current state in the Browser's History API rather than pushing a new state onto the queue:

  • public ngOnInit() : void {
  •  
  • console.warn( "Absolute redirect to:", this.redirectTo );
  • // NOTE: We could have performed the .navigateByUrl() in the constructor.
  • // However, doing so would have emitted a "navigation canceled" event. By waiting
  • // until the init method, we allow the previous navigation to complete before we
  • // start the new navigation. This feel more in alignment with the way the built-
  • // in "redirectTo" property works.
  • this.router.navigateByUrl(
  • this.redirectTo,
  • // By replacing the current URL in the history, we keep the Browser's Back
  • // Button behavior in tact. This will allow the user to easily navigate back
  • // to the previous URL without getting caught in a redirect.
  • {
  • replaceUrl: true
  • }
  • );
  •  
  • }

With this change in place, if we open up the Angular application in the browser and then invoke the "ping-pong-people" link, we get the following output:


 
 
 

 
 Maintaining the browser's natural Back Button behavior even while chaining absolute and local redirects in Angular 7.2.13. 
 
 
 

As you can see, with the "replaceUrl" property added to the Navigation Extra option of our Router.navigatebyUrl() method call, we don't persist the intermediary redirects to the Browser's history. This will provide a more intuitive Back Button experience for the user.

For completeness, here's the full AbsoluteRedirectComponent code:

  • // Import the core angular services.
  • import { ActivatedRoute } from "@angular/router";
  • import { Component } from "@angular/core";
  • import { Router } from "@angular/router";
  •  
  • // ----------------------------------------------------------------------------------- //
  • // ----------------------------------------------------------------------------------- //
  •  
  • @Component({
  • selector: "my-absolute-redirect",
  • template:
  • `
  • <em>Redirecting to <code>{{ redirectTo }}</code></em>
  • `
  • })
  • export class AbsoluteRedirectComponent {
  •  
  • public redirectTo: string;
  •  
  • private router: Router;
  •  
  • // I initialize the absolute-redirect component.
  • constructor(
  • activatedRoute: ActivatedRoute,
  • router: Router
  • ) {
  •  
  • this.router = router;
  • this.redirectTo = activatedRoute.snapshot.data.redirectTo;
  •  
  • }
  •  
  • // ---
  • // PUBLIC METHODS.
  • // ---
  •  
  • // I get called after the inputs have been found for the first time.
  • public ngOnInit() : void {
  •  
  • console.warn( "Absolute redirect to:", this.redirectTo );
  • // NOTE: We could have performed the .navigateByUrl() in the constructor.
  • // However, doing so would have emitted a "navigation canceled" event. By waiting
  • // until the init method, we allow the previous navigation to complete before we
  • // start the new navigation. This feel more in alignment with the way the built-
  • // in "redirectTo" property works.
  • this.router.navigateByUrl(
  • this.redirectTo,
  • // By replacing the current URL in the history, we keep the Browser's Back
  • // Button behavior in tact. This will allow the user to easily navigate back
  • // to the previous URL without getting caught in a redirect.
  • {
  • replaceUrl: true
  • }
  • );
  •  
  • }
  •  
  • }

With this new configuration, the AbsoluteRedirectComponent gives us the ability to chain both absolute and local redirects while still honoring the browser's "natural" Back Button experience. This is exactly what we need in order to maintain old application links and other types of internal route shortcuts and aliases in Angular 7.2.13.



Reader Comments

Hey, I think I asked this to you before and it was a long time ago: what tool do you use to create your mockups? I love the font and the arrows.

Reply to this Comment

@Rob,

So, historically, I've used Adobe Fireworks (formerly Macromedia Fireworks) to create my illustrations. But, more and more, I've been trying to move to InVision Studio. You can tell which are which because my Studio-based illustrations have no bold text.

The font I use is called "Coming Soon" and is a free font: https://fonts.google.com/specimen/Coming+Soon

The problem is, it only has one weight, "Regular". This is why my Studio versions (like the one in this post) don't have any Bold text. Studio won't "fake" a bold font-face, like Adobe Fireworks will. I really should find a font-family with more style choices, so that I can more fully embrace Studio. But, I really like "Coming Soon" :D

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.