Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Madeline Johnsen
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Madeline Johnsen@madjohnsen )

$route redirectTo Will Pass-Through Current Route Params In AngularJS

By Ben Nadel on

After my demonstration yesterday, that the redirectTo operation in AngularJS routing wouldn't break the back button, I wanted to dig into redirectTo a bit more. It's easy to redirect to a static string; but, what about redirecting to a dynamic path? As it turns out, AngularJS supports this implicitly. When you redirectTo another path, the route parameters are automatically passed-through (and interpolated into) the target route.


 
 
 

 
 
 
 
 

Run this demo in my JavaScript Demos project on GitHub.

To see this in action, I've created a demo in which we have three different route redirect configurations:

  • /users/:userID ==> /users/:userID/detail
  • /users/:userID/:notFound* ==> /users/:userID
  • otherwise ==> /home

The last one - otherwise - we've seen before. It's just a catch-all for missing routes that redirects to the homepage. But, the first two are new. Notice that both the target routes contain named parameters (:userID). This parameter is automatically passed-through from the matching route (ex, /users/1) to the target, redirectTo route (ex, /users/1/detail).

The second redirect contains an eager parameter match. Normally, a parameter would only match up until the next "/" character; however, by appending a "*" to the parameter name, it makes it eager which means that it will match as much of that path as it can, including any "/" characters. This allows us to create a route that "partially matches" the current location path. This can be super useful if you need to handle old links that may no longer be valid but may still be "in the wild".

  • <!doctype html>
  • <html ng-app="Demo">
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • $route redirectTo Will Pass-Through Current Route Params In AngularJS
  • </title>
  • </head>
  • <body ng-controller="AppController">
  •  
  • <h1>
  • $route redirectTo Will Pass-Through Current Route Params In AngularJS
  • </h1>
  •  
  • <p>
  • <a href="#/home">Home</a>
  • &mdash;
  • <a href="#/users/1">User 1</a>
  • &mdash;
  • <a href="#/users/2/detail">User 2</a>
  • &mdash;
  • <a href="#/users/3/old-broken-link">User 3 (broken)</a>
  • </p>
  •  
  • <p>
  • <strong>Action</strong>: {{ routeAction }}
  • </p>
  •  
  •  
  • <!-- Load scripts. -->
  • <script type="text/javascript" src="../../vendor/angularjs/angular-1.3.8.min.js"></script>
  • <script type="text/javascript" src="../../vendor/angularjs/angular-route-1.3.8.min.js"></script>
  • <script type="text/javascript">
  •  
  • // Create an application module for our demo.
  • var app = angular.module( "Demo", [ "ngRoute" ] );
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // I setup the routes for the application.
  • app.config(
  • function( $routeProvider ) {
  •  
  • $routeProvider
  • .when(
  • "/home",
  • {
  • action: "home"
  • }
  • )
  •  
  • // For convenience (and flexibility), we're going to let the rest of
  • // the app link to the userID; but, in reality, we always want to
  • // redirect to the user detail page.
  • .when(
  • "/users/:userID",
  • {
  • redirectTo: "/users/:userID/detail"
  • }
  • )
  •  
  • // These are the "good" user routes.
  • .when(
  • "/users/:userID/detail",
  • {
  • action: "users.detail"
  • }
  • )
  • .when(
  • "/users/:userID/phone",
  • {
  • action: "users.phone"
  • }
  • )
  •  
  • // This is a catch-all for user-based routes. Routing is matched in
  • // the same order in which it was defined. As such, if we get this
  • // far, none of the earlier user-based routes matched. Redirect the
  • // user to the detail page. This kind of catch-all could be useful in
  • // situations in which you have old / broken links "in the wild" and
  • // you want the app to handle them gracefully.
  • // --
  • // NOTE: The "*" operator, after a named parameter, makes the
  • // parameter an eager matcher. As such, it will match as much of the
  • // path as it can, which in our case, is the rest of the path.
  • .when(
  • "/users/:userID/:notFound*",
  • {
  • redirectTo: "/users/:userID"
  • }
  • )
  •  
  • // And, if nothing else matches, just redirect to the home.
  • .otherwise({
  • redirectTo: "/home"
  • })
  • ;
  •  
  • }
  • );
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // I control the root of the application.
  • app.controller(
  • "AppController",
  • function( $scope, $route, $routeParams, $location ) {
  •  
  • $scope.routeAction = null;
  •  
  • // I listen for route-change events.
  • $scope.$on(
  • "$routeChangeSuccess",
  • function handleRouteChangeEvent( event ) {
  •  
  • var current = $route.current;
  •  
  • console.log(
  • "ROUTE CHANGE: [ %s ] .. [ Path: %s ]",
  • current.originalPath,
  • $location.path()
  • );
  •  
  • // If the current route doesn't contain an action, then it will,
  • // in all likelihood, be redirected to another route that does
  • // contain a valid action (configured in $routeProvider).
  • // --
  • // NOTE: This is not a native part of routing - this is because
  • // my route object contains an "action" key in the configuration
  • // in all cases in which a valid route is matched.
  • if ( ! current.action ) {
  •  
  • console.warn( "Route does not contain an action." );
  •  
  • }
  •  
  • // Store our current action for output.
  • $scope.routeAction = current.action;
  •  
  • }
  • );
  •  
  • }
  • );
  •  
  • </script>
  •  
  • </body>
  • </html>

When I open this page and click through the links, I'm redirected from one route to another. And, as I do this, the current :userID value is automatically piped into the redirecting route. You can see this in the console logging:


 
 
 

 
 route redirectTo operations will automatically pass-through named parameters from the current route to the target route. 
 
 
 

This is such a great feature. Once again, I'm super impressed with the way the AngularJS team has crafted their code. With the route parameters being automatically passed-through to the target, redirectTo route, it should make redirecting super simple and intuitive.




Reader Comments

This presents a problem (or at least it does not help) in the following situation:

(article is a subroute of education)

.when('/education',{
templateUrl : 'pages/education.html',
controller : 'eduController'
}).
when('/education/:blogArticleTitle',{
templateUrl: 'pages/article.html',
controller: 'articleController'
})

When i go to this url /education/myarticle, i make an AJAX call to a processing page that pulls from the DB a page with that title. If no suck page is found it returns an error object.
I then check the object i get and if it's an error object, i want to make a redirect back to the "education" page with a parameter that lets me know i found nothing at the subroute.

Any ideas?

Reply to this Comment

Hi Ben,

In your example, the status codes returned would be 200, right? What if I you want to have the browser return a 301 status code on the first page for SEO purposes, before it redirects to the new permanent address.

I have a route (.when('/:routeparam1/:routeparam2/:routeparam3' which I want redirect to /:routeparam1/:routeparam2. I have the redirect set up but we want to redirect current pages to the new URLs with 301 permanent redirects (as opposed to 302 temporary redirects). Does anyone know how this can be achieved? The site uses $routeProvider and is on IIS.

Here's my boiled down redirect code from the app config (there are query parameters possibly also sent but I've removed that code here):
.when('/:routeparam1/:routeparam2/:routeparam3',
{
redirectTo: function(routeParams, path, search)
{
return "/" + routeParams.routeparam1 + "/" + routeParams.routeparam2;
}
})
.when('/:routeparam1/:routeparam2'........

Thanks

Reply to this Comment

I have a problem-
I am working on an application where by clicking on a link i have to redirect it to a specific tab on another page. Can you please help? I am using simple ngrouting for this no state routing.

Reply to this Comment

@Rohit,

If this link always opens a specific tab, could you pass the hard-coded tabname on the ng-click to the controller function? I'm sure there are better ways, but just one suggestion:

ng-click="GoToMyPage('Tab2')"

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
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.