diff --git a/src/ngRoute/route.js b/src/ngRoute/route.js
index 4ead323e8baf..8dcb2bd718de 100644
--- a/src/ngRoute/route.js
+++ b/src/ngRoute/route.js
@@ -2,10 +2,11 @@
/* global shallowCopy: false */
-// There are necessary for `shallowCopy()` (included via `src/shallowCopy.js`).
+// `isArray` and `isObject` are necessary for `shallowCopy()` (included via `src/shallowCopy.js`).
// They are initialized inside the `$RouteProvider`, to ensure `window.angular` is available.
var isArray;
var isObject;
+var isDefined;
/**
* @ngdoc module
@@ -22,10 +23,17 @@ var isObject;
*
*
*/
- /* global -ngRouteModule */
-var ngRouteModule = angular.module('ngRoute', ['ng']).
- provider('$route', $RouteProvider),
- $routeMinErr = angular.$$minErr('ngRoute');
+/* global -ngRouteModule */
+var ngRouteModule = angular.
+ module('ngRoute', []).
+ provider('$route', $RouteProvider).
+ // Ensure `$route` will be instantiated in time to capture the initial `$locationChangeSuccess`
+ // event (unless explicitly disabled). This is necessary in case `ngView` is included in an
+ // asynchronously loaded template.
+ run(instantiateRoute);
+var $routeMinErr = angular.$$minErr('ngRoute');
+var isEagerInstantiationEnabled;
+
/**
* @ngdoc provider
@@ -44,6 +52,7 @@ var ngRouteModule = angular.module('ngRoute', ['ng']).
function $RouteProvider() {
isArray = angular.isArray;
isObject = angular.isObject;
+ isDefined = angular.isDefined;
function inherit(parent, extra) {
return angular.extend(Object.create(parent), extra);
@@ -287,6 +296,47 @@ function $RouteProvider() {
return this;
};
+ /**
+ * @ngdoc method
+ * @name $routeProvider#eagerInstantiationEnabled
+ * @kind function
+ *
+ * @description
+ * Call this method as a setter to enable/disable eager instantiation of the
+ * {@link ngRoute.$route $route} service upon application bootstrap. You can also call it as a
+ * getter (i.e. without any arguments) to get the current value of the
+ * `eagerInstantiationEnabled` flag.
+ *
+ * Instantiating `$route` early is necessary for capturing the initial
+ * {@link ng.$location#$locationChangeStart $locationChangeStart} event and navigating to the
+ * appropriate route. Usually, `$route` is instantiated in time by the
+ * {@link ngRoute.ngView ngView} directive. Yet, in cases where `ngView` is included in an
+ * asynchronously loaded template (e.g. in another directive's template), the directive factory
+ * might not be called soon enough for `$route` to be instantiated _before_ the initial
+ * `$locationChangeSuccess` event is fired. Eager instantiation ensures that `$route` is always
+ * instantiated in time, regardless of when `ngView` will be loaded.
+ *
+ * The default value is true.
+ *
+ * **Note**:
+ * You may want to disable the default behavior when unit-testing modules that depend on
+ * `ngRoute`, in order to avoid an unexpected request for the default route's template.
+ *
+ * @param {boolean=} enabled - If provided, update the internal `eagerInstantiationEnabled` flag.
+ *
+ * @returns {*} The current value of the `eagerInstantiationEnabled` flag if used as a getter or
+ * itself (for chaining) if used as a setter.
+ */
+ isEagerInstantiationEnabled = true;
+ this.eagerInstantiationEnabled = function eagerInstantiationEnabled(enabled) {
+ if (isDefined(enabled)) {
+ isEagerInstantiationEnabled = enabled;
+ return this;
+ }
+
+ return isEagerInstantiationEnabled;
+ };
+
this.$get = ['$rootScope',
'$location',
@@ -791,3 +841,11 @@ function $RouteProvider() {
}
}];
}
+
+instantiateRoute.$inject = ['$injector'];
+function instantiateRoute($injector) {
+ if (isEagerInstantiationEnabled) {
+ // Instantiate `$route`
+ $injector.get('$route');
+ }
+}
diff --git a/test/ngRoute/directive/ngViewSpec.js b/test/ngRoute/directive/ngViewSpec.js
index daab5f26c987..66d2653108ce 100644
--- a/test/ngRoute/directive/ngViewSpec.js
+++ b/test/ngRoute/directive/ngViewSpec.js
@@ -1027,3 +1027,34 @@ describe('ngView animations', function() {
));
});
});
+
+describe('ngView in async template', function() {
+ beforeEach(module('ngRoute'));
+ beforeEach(module(function($compileProvider, $provide, $routeProvider) {
+ $compileProvider.directive('asyncView', function() {
+ return {templateUrl: 'async-view.html'};
+ });
+
+ $provide.decorator('$templateRequest', function($timeout) {
+ return function() {
+ return $timeout(angular.identity, 500, false, '');
+ };
+ });
+
+ $routeProvider.when('/', {template: 'Hello, world!'});
+ }));
+
+
+ it('should work correctly upon initial page load',
+ // Injecting `$location` here is necessary, so that it gets instantiated early
+ inject(function($compile, $location, $rootScope, $timeout) {
+ var elem = $compile('')($rootScope);
+ $rootScope.$digest();
+ $timeout.flush(500);
+
+ expect(elem.text()).toBe('Hello, world!');
+
+ dealoc(elem);
+ })
+ );
+});
diff --git a/test/ngRoute/routeSpec.js b/test/ngRoute/routeSpec.js
index 1d6037a84f9d..5a0780f5efbf 100644
--- a/test/ngRoute/routeSpec.js
+++ b/test/ngRoute/routeSpec.js
@@ -1,5 +1,58 @@
'use strict';
+describe('$routeProvider', function() {
+ var $routeProvider;
+
+ beforeEach(module('ngRoute'));
+ beforeEach(module(function(_$routeProvider_) {
+ $routeProvider = _$routeProvider_;
+ $routeProvider.when('/foo', {template: 'Hello, world!'});
+ }));
+
+
+ it('should support enabling/disabling automatic instantiation upon initial load',
+ inject(function() {
+ expect($routeProvider.eagerInstantiationEnabled(true)).toBe($routeProvider);
+ expect($routeProvider.eagerInstantiationEnabled()).toBe(true);
+
+ expect($routeProvider.eagerInstantiationEnabled(false)).toBe($routeProvider);
+ expect($routeProvider.eagerInstantiationEnabled()).toBe(false);
+
+ expect($routeProvider.eagerInstantiationEnabled(true)).toBe($routeProvider);
+ expect($routeProvider.eagerInstantiationEnabled()).toBe(true);
+ })
+ );
+
+
+ it('should automatically instantiate `$route` upon initial load', function() {
+ inject(function($location, $rootScope) {
+ $location.path('/foo');
+ $rootScope.$digest();
+ });
+
+ inject(function($route) {
+ expect($route.current).toBeDefined();
+ });
+ });
+
+
+ it('should not automatically instantiate `$route` if disabled', function() {
+ module(function($routeProvider) {
+ $routeProvider.eagerInstantiationEnabled(false);
+ });
+
+ inject(function($location, $rootScope) {
+ $location.path('/foo');
+ $rootScope.$digest();
+ });
+
+ inject(function($route) {
+ expect($route.current).toBeUndefined();
+ });
+ });
+});
+
+
describe('$route', function() {
var $httpBackend,
element;