Skip to content

Commit

Permalink
feat(ngRoute): allow cancelling $route updates via $routeChangeEvent
Browse files Browse the repository at this point in the history
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 angular#5855
Closes angular#5714
Closes angular#5581
  • Loading branch information
caitp committed Oct 3, 2014
1 parent cad9560 commit 80ed319
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 0 deletions.
28 changes: 28 additions & 0 deletions src/ngRoute/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,21 @@ function $RouteProvider(){
* </example>
*/

/**
* @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
Expand Down Expand Up @@ -469,6 +484,7 @@ function $RouteProvider(){
}
};

$rootScope.$on('$locationChangeStart', beforeUpdateRoute);
$rootScope.$on('$locationChangeSuccess', updateRoute);

return $route;
Expand Down Expand Up @@ -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
*/
Expand Down
59 changes: 59 additions & 0 deletions test/ngRoute/routeSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: '<p>route A</p>'
}).when('/b', {
template: '<p>route B</p>'
});
});

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('<p>route B</p>');
expect(currentRoute.template).toBe('<p>route A</p>');
});

$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: '<p>route A</p>'
}).when('/b', {
template: '<p>route B</p>'
});
});

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('<p>route A</p>');
});
});
});


Expand Down

0 comments on commit 80ed319

Please sign in to comment.