From 549143128b6141149f685b083411f3df4af76087 Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Thu, 2 Oct 2014 21:46:50 -0400 Subject: [PATCH 1/2] feat(ngRoute): allow cancelling $route updates via $routeChangeEvent Previously, one could perform this feat by listening to both $locationChangeStart as well as $routeChangeStart, and being careful to update the route correctly. Now, it's possible to simply preventDefault() on the $beforeRouteChange event in order to prevent location and route from being updated. While this does not cancel $routeChangeStart, it does ensure that the correct location is restored, and provides the same information as $routeChangeStart. Closes #5855 Closes #5714 Closes #5581 --- src/ngRoute/route.js | 28 +++++++++++++++++++ test/ngRoute/routeSpec.js | 59 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/src/ngRoute/route.js b/src/ngRoute/route.js index 44da57d64fdf..3f1e2b27b487 100644 --- a/src/ngRoute/route.js +++ b/src/ngRoute/route.js @@ -364,6 +364,21 @@ function $RouteProvider(){ * */ + /** + * @ngdoc event + * @name $route#$beforeRouteChange + * @eventType broadcast on root scope + * + * @description + * Broadcasted during locationChangeStart, will cancel $locationChangeStart + * if preventDefault() is called, thus preventing subsequent $routeChange + * events. + * + * @param {Object} angularEvent Synthetic event object. + * @param {Route} nextRoute route information of the future route. + * @param {Route} currentRoute current route information. + */ + /** * @ngdoc event * @name $route#$routeChangeStart @@ -469,6 +484,7 @@ function $RouteProvider(){ } }; + $rootScope.$on('$locationChangeStart', beforeUpdateRoute); $rootScope.$on('$locationChangeSuccess', updateRoute); return $route; @@ -582,6 +598,18 @@ function $RouteProvider(){ } + // $locationChangeStart handler. Dispatches $beforeRouteChange, and cancels $locationChangeStart + // if the user cancels the $beforeRouteChange event. + function beforeUpdateRoute(event, next, current) { + var nextRoute = parseRoute(); + var lastRoute = $route.current; + + if ($rootScope.$broadcast('$beforeRouteChange', nextRoute, lastRoute).defaultPrevented) { + event.preventDefault(); + } + } + + /** * @returns {Object} the current active route, by matching it against the URL */ diff --git a/test/ngRoute/routeSpec.js b/test/ngRoute/routeSpec.js index 8a0a370f0615..92d00941cadf 100644 --- a/test/ngRoute/routeSpec.js +++ b/test/ngRoute/routeSpec.js @@ -741,6 +741,65 @@ describe('$route', function() { expect($exceptionHandler.errors).toEqual([myError]); }); }); + + + it('should pass nextRoute and currentRoute to $beforeRouteChange', function() { + module(function($routeProvider) { + $routeProvider.when('/a', { + template: '

route A

' + }).when('/b', { + template: '

route B

' + }); + }); + + inject(function($location, $route, $rootScope) { + var beforeRouteChangeCalled = false; + $location.path('/a'); + $rootScope.$digest(); + + $rootScope.$on('$beforeRouteChange', function(event, nextRoute, currentRoute) { + beforeRouteChangeCalled = true; + expect(nextRoute.template).toBe('

route B

'); + expect(currentRoute.template).toBe('

route A

'); + }); + + $location.path('/b'); + $rootScope.$digest(); + + expect(beforeRouteChangeCalled).toBe(true); + }); + }); + + + it('should cancel $locationChange when $beforeRouteChange is cancelled', function() { + module(function($routeProvider) { + $routeProvider.when('/a', { + template: '

route A

' + }).when('/b', { + template: '

route B

' + }); + }); + + inject(function($location, $route, $rootScope) { + var didChangeLocation = false; + $location.path('/a'); + $rootScope.$digest(); + + $rootScope.$on('$beforeRouteChange', function(event, nextRoute, currentRoute) { + event.preventDefault(); + }); + $rootScope.$on('$locationChangeSuccess', function() { + didChangeLocation = true; + }); + + $location.path('/b'); + $rootScope.$digest(); + + expect(didChangeLocation).toBe(false); + expect($location.path()).toBe('/a'); + expect($route.current.template).toBe('

route A

'); + }); + }); }); From 37c35efd5a2344d0cb6bb181cd092a671a1cecef Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Thu, 2 Oct 2014 21:52:10 -0400 Subject: [PATCH 2/2] test(ngRoute): fix wrong assertion in ngRoute suite Previously, this was asserting that $beforeRouteChange was never called. Prior to the preceeding commit, $beforeRouteChange didn't exist --- however it is called during $locationChangeStart. It looks like this test should be asserting that $routeChangeStart is never fired. --- test/ngRoute/routeSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ngRoute/routeSpec.js b/test/ngRoute/routeSpec.js index 92d00941cadf..8b9ede967b3b 100644 --- a/test/ngRoute/routeSpec.js +++ b/test/ngRoute/routeSpec.js @@ -196,7 +196,7 @@ describe('$route', function() { event.preventDefault(); }); - $rootScope.$on('$beforeRouteChange', function(event) { + $rootScope.$on('$routeChangeStart', function(event) { throw new Error('Should not get here'); });