Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Jim Priest
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Jim Priest@thecrumb )

ngRoute Exposes The Original Route Definition Through Prototypal Inheritance In AngularJS

By Ben Nadel on

With the ngRoute module in AngularJS, you can put anything you want in the original route definition object. These custom properties will then be exposed on the current route as the user navigates around your application. One thing to note, however, is that the original route definition object is exposed on the prototype chain. As such, you are limited in how you can check for the existing of route configuration options.


 
 
 

 
 
 
 
 

Run this demo in my JavaScript Demos project on GitHub.

When you define your routes, in AngularJS, the ngRoute module makes a copy of your route definition and stores it internally. Then, as the user navigates around the application, ngRoute parses the current location, maps it onto a stored path, and then exposes the current state as an "extension" of the route definition:


 
 
 

 
 The route configuration object is exposed on the prototype of the current route in AngularJS / ngRoute. 
 
 
 

Because of this structure, you can run into problems if you use the often-loved .hasOwnProperty() method to check for route options. This is because .hasOwnProperty() only looks at the "current" object on the prototype chain. To work within the confines of this structure, we can use JavaScript's "in" operator to check for keys anywhere in the route's prototype chain (including the current object).

To see this in action, I've put together a tiny demo that has two routes. One of the route definition objects contains an "action" setting; the other does not. We'll use both the .hasOwnProperty() method and the "in" operator to check for its existence:

  • <!doctype html>
  • <html ng-app="Demo">
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • ngRoute Exposes The Original Route Definition Through Prototypal Inheritance In AngularJS
  • </title>
  • </head>
  • <body ng-controller="AppController">
  •  
  • <h1>
  • ngRoute Exposes The Original Route Definition Through Prototypal Inheritance In AngularJS
  • </h1>
  •  
  • <p>
  • <a href="#/a">Route A</a>
  • &mdash;
  • <a href="#/b">Route B</a>
  • </p>
  •  
  • <p>
  • <strong>Action</strong>: {{ routeAction || 'Noop' }}
  • </p>
  •  
  •  
  • <!-- Load scripts. -->
  • <script type="text/javascript" src="../../vendor/angularjs/angular-1.3.13.min.js"></script>
  • <script type="text/javascript" src="../../vendor/angularjs/angular-route-1.3.13.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(
  • "/a",
  • {
  • action: "Get your ass to mars!"
  • }
  • )
  • .when(
  • "/b",
  • {
  • // Route B doesn't have an "action" value.
  • }
  • )
  • ;
  •  
  • }
  • );
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // I control the root of the application.
  • app.controller(
  • "AppController",
  • function( $scope, $route ) {
  •  
  • $scope.routeAction = null;
  •  
  • // I listen for route-change events.
  • $scope.$on(
  • "$routeChangeSuccess",
  • function handleRouteChangeEvent( event ) {
  •  
  • var current = $route.current;
  •  
  • console.info( "Looking at \"%s\"", current.originalPath );
  •  
  • // Let's examine how the action variable is exposed on the
  • // current route - this is a value that we define in the original
  • // route definition - it is not something that AngularJS is
  • // calculating on a per-route-change basis.
  • console.log( "action[ in ]:", ( "action" in current ) );
  • console.log( "action[ hasOwnProperty ]:", current.hasOwnProperty( "action" ) );
  •  
  • // Let's look at how params is exposed. This value is calculated
  • // by AngularJS, on route change, and contains an aggregate of the
  • // route params and the location.search params.
  • console.log( "params[ in ]:", ( "params" in current ) );
  • console.log( "params[ hasOwnProperty ]:", current.hasOwnProperty( "params" ) );
  •  
  • // Let's look at how pathParams is exposed. This value is
  • // calculated by AngularJS, on route change, and contains the
  • // variable mappings defined by the route path.
  • console.log( "pathParams[ in ]:", ( "pathParams" in current ) );
  • console.log( "pathParams[ hasOwnProperty ]:", current.hasOwnProperty( "pathParams" ) );
  •  
  • // Store our current action for output.
  • $scope.routeAction = current.action;
  •  
  • }
  • );
  •  
  • }
  • );
  •  
  • </script>
  •  
  • </body>
  • </html>

We're also logging out a few other properties - params and pathParams - to see where they show up in the prototype chain. When we run the above code and toggle back and forth between the routes, we get the following output:


 
 
 

 
 Checking the route configuration object, in ngRoute, using the in operator vs. hasOwnProperty(). 
 
 
 

As you can see, the "action" route configuration property can only be properly tested using the "in" operator; the .hasOwnProperty() method fails to find it.

NOTE: The current route object also contains a "locals" property, used with route resolution, but I'm not logging that out in the demo.

This is a really minor point, and one that you may never have to worry about, depending on how you leverage the ngRoute module. But, if you do use custom configuration options, understanding the structure now can safe you a lot of debugging time in the future. I know for experience.




Reader Comments

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.