Skip to main content
Ben Nadel at InVision In Real Life (IRL) 2018 (Hollywood, CA) with: Michelle Kong and Johnathan Hunt
Ben Nadel at InVision In Real Life (IRL) 2018 (Hollywood, CA) with: Michelle Kong Johnathan Hunt

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

By
Published in Comments (6)

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.

Want to use code from this post? Check out the license.

Reader Comments

1 Comments

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

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel