Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Ray Camden and Liz Frederick
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Ray Camden@cfjedimaster ) and Liz Frederick@lizign )

$route Must Be Injected In Order To Enable The $routeChangeSuccess Event In AngularJS

By Ben Nadel on

Earlier, when I was exploring the use of the $location service to provide state-transformation triggers in AngularJS, I noticed something interesting about the $route service; the $routeChangeSuccess event won't fire until at least one component in the application has required the $route service.


 
 
 

 
 
 
 
 

Run this demo in my JavaScript Demos project on GitHub.

To demonstrate this, I've created a Controller that logs both the $locationChangeSuccess and the $routeChangeSuccess events. This controller, however, doesn't inject the $route service. Instead, I have another, optional, sub-controller which does nothing but inject the $route service (which forces AngularJS to call the $routeProvider).

  • <!doctype html>
  • <html ng-app="Demo">
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • $route Must Be Injected In Order To Enable The $routeChangeSuccess Event In AngularJS
  • </title>
  •  
  • <link rel="stylesheet" type="text/css" href="./demo.css"></link>
  • </head>
  • <body ng-controller="AppController">
  •  
  • <h1>
  • $route Must Be Injected In Order To Enable The $routeChangeSuccess Event In AngularJS
  • </h1>
  •  
  • <!--
  • These link are here just to change the $location path, which should
  • trigger a $routeChangeSuccess event.
  • -->
  • <p>
  • <a href="#/foo">Foo</a> &mdash;
  • <a href="#/bar">Bar</a> &mdash;
  • <a href="#/baz">Baz</a>
  • </p>
  •  
  • <p>
  • <a ng-click="includeSubController()">Include the $route service</a>
  •  
  • <!--
  • This conditionally-included controller does nothing but force
  • AngularJS to instantiate and inject the $route service.
  • -->
  • <span
  • ng-if="showingSubController"
  • ng-controller="SubController">
  • &mdash;
  • $route is now included.
  • </span>
  • </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 configure the route provider.
  • app.config(
  • function( $routeProvider ) {
  •  
  • $routeProvider.when( "/:thing", {} );
  •  
  • }
  • );
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // I control the root of the application.
  • app.controller(
  • "AppController",
  • function( $scope, $routeParams, $location ) {
  •  
  • // I determine if the sub-controller is visible.
  • $scope.showingSubController = false;
  •  
  • // I log out the changes to the location as the user navigates around
  • // the AngularJS application.
  • $scope.$on(
  • "$locationChangeSuccess",
  • function handleLocationChangeEvent( event ) {
  •  
  • console.log( "Location Change:", $location.path() );
  •  
  • }
  • );
  •  
  • // I log out changes to the route as the user navigates around the
  • // AngularJS application.
  • $scope.$on(
  • "$routeChangeSuccess",
  • function handleRouteChangeEvent( event ) {
  •  
  • console.log( "Route Change:", $routeParams );
  •  
  • }
  • );
  •  
  •  
  • // ---
  • // PUBLIC METHODS.
  • // ---
  •  
  •  
  • // I toggle the sub-controller container which will cause the
  • // sub-controller to be instantiated.
  • $scope.includeSubController = function() {
  •  
  • $scope.showingSubController = true;
  •  
  • };
  •  
  • }
  • );
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // This controller does nothing in the demo except for require $route which
  • // forces AngularJS to call the $route provider and instantiate the routing
  • // service which will, in turn, enable the $routeChangeSuccess event.
  • app.controller(
  • "SubController",
  • function( $route ) {
  •  
  • console.warn( "$route has been injected, like a boss!" );
  •  
  • }
  • );
  •  
  • </script>
  •  
  • </body>
  • </html>

If I navigate around the app, then include the sub-controller (thereby instantiating the $route service), and then navigate around a bit more, I get the following console output:


 
 
 

 
 $route must be injected for the $routeChangeSuccess event to be enabled. 
 
 
 

As you can see, before the $route is injected, only the $locationChangeSuccess event is triggered. It is only after the sub-controller is instantiated - and with it the $route service - that the $routeChangeSuccess event is triggered.

If you dig into the ngRoute module source code, this makes sense; it's the route service that starts watching for $location changes. As such, the $route service must be instantiated before any route events are triggered. Furthermore, you can see, from the source code, that the $route service and the $routeParams service are independently defined, which is why my demo can inject the $routeParams without causing the $route to be instantiated.




Reader Comments

Thank you soooo much for this!
I kept wondering about why did the routeChangeSuccess event not fire till I found this post. Thanks!

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.