$route Must Be Injected In Order To Enable The $routeChangeSuccess Event In AngularJS
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> — | |
<a href="#/bar">Bar</a> — | |
<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"> | |
— | |
$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:

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.
Want to use code from this post? Check out the license.
Reader Comments
Thanks a lot ^_^
I spent hours scratching my head before finding this post
@Jimmy,
Ha ha, glad to have helped :D
Thank you soooo much for this!
I kept wondering about why did the routeChangeSuccess event not fire till I found this post. Thanks!
It worked for me. Thanks. (from China)
Thanks for the tip, this got my Angular controller working :)
Glad this is still helping people :D That pleases me greatly!