Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 003ac6a

Browse files
fix(): allow to be included in an asynchronously loaded template
1 parent dc954f2 commit 003ac6a

File tree

2 files changed

+112
-18
lines changed

2 files changed

+112
-18
lines changed

src/ngRoute/route.js

+25-16
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,17 @@ var isObject;
2525
/* global -ngRouteModule */
2626
var ngRouteModule = angular.module('ngRoute', ['ng'])
2727
.provider('$route', $RouteProvider)
28-
.factory('$$trackLocationChanges', ['$rootScope', $$trackLocationChanges])
28+
.factory('$$trackLocationChanges', ['$rootScope', '$location', $$trackLocationChanges])
2929
.run(['$$trackLocationChanges', function($$trackLocationChanges) {
3030
$$trackLocationChanges.start();
3131
}]),
3232

3333
$routeMinErr = angular.$$minErr('ngRoute');
3434

35-
function $$trackLocationChanges($rootScope) {
35+
function $$trackLocationChanges($rootScope, $location) {
3636
var removeStartHandler, removeSuccessHandler;
3737
var service = {
38+
events: [],
3839
start: function() {
3940
removeStartHandler = $rootScope.$on('$locationChangeStart', storeStartEvent);
4041
removeSuccessHandler = $rootScope.$on('$locationChangeSuccess', storeSuccessEvent);
@@ -47,13 +48,12 @@ function $$trackLocationChanges($rootScope) {
4748

4849
return service;
4950

50-
function storeStartEvent(e) {
51-
service.startEvent = e;
52-
delete service.successEvent;
51+
function storeStartEvent(e, url) {
52+
service.events.push({ startEvent: e, locationPath: $location.path(), locationSearch: $location.search() });
5353
}
5454

5555
function storeSuccessEvent(e) {
56-
service.successEvent = e;
56+
service.events[service.events.length-1].successEvent = e;
5757
}
5858
}
5959

@@ -557,7 +557,7 @@ function $RouteProvider() {
557557
};
558558

559559
$rootScope.$evalAsync(function() {
560-
prepareRoute(fakeLocationEvent);
560+
prepareRoute(fakeLocationEvent, $location.path(), $location.search());
561561
if (!fakeLocationEvent.defaultPrevented) commitRoute();
562562
});
563563
},
@@ -587,13 +587,22 @@ function $RouteProvider() {
587587
}
588588
};
589589

590-
if ($$trackLocationChanges.successEvent) {
591-
prepareRoute($$trackLocationChanges.startEvent);
592-
commitRoute($$trackLocationChanges.successEvent);
590+
var eventPair;
591+
while(eventPair = $$trackLocationChanges.events.pop()) {
592+
// find the most recent success event
593+
if (eventPair.successEvent) {
594+
prepareRoute(eventPair.startEvent, eventPair.locationPath, eventPair.locationSearch);
595+
// if the start event is not prevented then commit the change and escape
596+
// otherwise try the previous location change
597+
if (!eventPair.startEvent.defaultPrevented) {
598+
commitRoute(eventPair.successEvent);
599+
break;
600+
}
601+
}
593602
}
594603
$$trackLocationChanges.stop();
595604

596-
$rootScope.$on('$locationChangeStart', prepareRoute);
605+
$rootScope.$on('$locationChangeStart', function(e) { return prepareRoute(e, $location.path(), $location.search()); });
597606
$rootScope.$on('$locationChangeSuccess', commitRoute);
598607

599608
return $route;
@@ -632,10 +641,10 @@ function $RouteProvider() {
632641
return params;
633642
}
634643

635-
function prepareRoute($locationEvent) {
644+
function prepareRoute($locationEvent, locationPath, locationSearch) {
636645
var lastRoute = $route.current;
637646

638-
preparedRoute = parseRoute();
647+
preparedRoute = parseRoute(locationPath, locationSearch);
639648
preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route
640649
&& angular.equals(preparedRoute.pathParams, lastRoute.pathParams)
641650
&& !preparedRoute.reloadOnSearch && !forceReload;
@@ -795,13 +804,13 @@ function $RouteProvider() {
795804
/**
796805
* @returns {Object} the current active route, by matching it against the URL
797806
*/
798-
function parseRoute() {
807+
function parseRoute(locationPath, locationSearch) {
799808
// Match a route
800809
var params, match;
801810
angular.forEach(routes, function(route, path) {
802-
if (!match && (params = switchRouteMatcher($location.path(), route))) {
811+
if (!match && (params = switchRouteMatcher(locationPath, route))) {
803812
match = inherit(route, {
804-
params: angular.extend({}, $location.search(), params),
813+
params: angular.extend({}, locationSearch, params),
805814
pathParams: params});
806815
match.$$route = route;
807816
}

test/ngRoute/directive/ngViewSpec.js

+87-2
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,7 @@ describe('ngView', function() {
696696
});
697697

698698
describe('ngView in async template', function() {
699+
699700
beforeEach(module('ngRoute'));
700701
beforeEach(module(function($compileProvider, $provide, $routeProvider) {
701702
$compileProvider.directive('asyncView', function() {
@@ -709,22 +710,106 @@ describe('ngView', function() {
709710
});
710711

711712
$routeProvider.when('/', {template: 'Hello, world!'});
713+
$routeProvider.when('/one', {template: 'One'});
714+
$routeProvider.when('/two', {template: 'Two'});
712715
}));
713716

714717

715718
it('should work correctly upon initial page load',
716719
// Injecting `$location` here is necessary, so that it gets instantiated early
717720
inject(function($compile, $location, $rootScope, $timeout) {
718721
var elem = $compile('<async-view></async-view>')($rootScope);
722+
719723
$rootScope.$digest();
720-
$timeout.flush(500);
724+
expect(elem.text()).toBe('');
721725

726+
$timeout.flush(500);
722727
expect(elem.text()).toBe('Hello, world!');
723728

724729
dealoc(elem);
725730
})
726731
);
727-
});
732+
733+
it('should cope with multiple location changes before the template arrives', function() {
734+
inject(function($compile, $location, $rootScope, $timeout) {
735+
var elem = $compile('<async-view></async-view>')($rootScope);
736+
737+
$rootScope.$digest();
738+
expect(elem.text()).toBe('');
739+
740+
$location.path('one');
741+
$rootScope.$digest();
742+
expect(elem.text()).toBe('');
743+
744+
$location.path('two');
745+
$rootScope.$digest();
746+
expect(elem.text()).toBe('');
747+
748+
$timeout.flush(500);
749+
expect(elem.text()).toBe('Two');
750+
751+
dealoc(elem);
752+
});
753+
});
754+
755+
it('should use the previous location change if the latest is prevented via $location event', function() {
756+
inject(function($compile, $location, $rootScope, $timeout) {
757+
var preventDefault;
758+
759+
$rootScope.$on('$locationChangeStart', function(e) {
760+
if (preventDefault) e.preventDefault();
761+
});
762+
763+
var elem = $compile('<async-view></async-view>')($rootScope);
764+
765+
$rootScope.$digest();
766+
expect(elem.text()).toBe('');
767+
768+
preventDefault = false;
769+
$location.path('one');
770+
$rootScope.$digest();
771+
expect(elem.text()).toBe('');
772+
773+
774+
preventDefault = true;
775+
$location.path('two');
776+
$rootScope.$digest();
777+
expect(elem.text()).toBe('');
778+
779+
$timeout.flush(500);
780+
expect(elem.text()).toBe('One');
781+
782+
dealoc(elem);
783+
});
784+
});
785+
786+
it('should use the previous location change if the latest is prevented via $route event', function() {
787+
inject(function($compile, $location, $rootScope, $timeout) {
788+
789+
$rootScope.$on('$routeChangeStart', function(e, next, current) {
790+
if (next.$$route.originalPath == '/two') e.preventDefault();
791+
});
792+
793+
var elem = $compile('<async-view></async-view>')($rootScope);
794+
795+
$rootScope.$digest();
796+
expect(elem.text()).toBe('');
797+
798+
$location.path('one');
799+
$rootScope.$digest();
800+
expect(elem.text()).toBe('');
801+
802+
803+
$location.path('two');
804+
$rootScope.$digest();
805+
expect(elem.text()).toBe('');
806+
807+
$timeout.flush(500);
808+
expect(elem.text()).toBe('One');
809+
810+
dealoc(elem);
811+
});
812+
}); });
728813

729814
describe('animations', function() {
730815
var body, element, $rootElement;

0 commit comments

Comments
 (0)