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

Ability to cancel $routeChangeStart event #5581

Closed
yentheo opened this issue Dec 31, 2013 · 17 comments
Closed

Ability to cancel $routeChangeStart event #5581

yentheo opened this issue Dec 31, 2013 · 17 comments

Comments

@yentheo
Copy link

yentheo commented Dec 31, 2013

Hi, I have an issue which seems to occur more often in the community.
It's the same as #592 and multiple stackoverflow posts (http://stackoverflow.com/questions/14895016 - http://stackoverflow.com/questions/16344223 etc)
Some of these issues were resolved by using locationChangeStart.

It would work too for me, if not for the next problem.

I'm making an app with some standard pages like a home-page and a contact-page. Then we have some kind of wizard. The routes look like this.

$routeProvider.
    when('/home', { controller: 'homeController', templateUrl: '/home.html'}).
    when('/contact', { controller: 'contactController', templateUrl: '/contact.html'}).
    when('/wizardPage1', { controller: 'wizardPageOneController', templateUrl: '/wizardPage1.html', wizard: true}).
    when('/wizardPage2', { controller: 'wizardPageTwoController', templateUrl: '/wizardPage2.html', wizard: true}).
    when('/wizardPageSave', { controller: 'wizardPageSaveController', templateUrl: '/wizardPageSave.html', wizard: true}).
    otherwise({ redirectTo: '/home' });

Now I can complete the wizard and save on the WizardSavePage. But what I want is when I'm on WizardPage2 and I navigate to home or contact, I want to show a confirmation "You have unsaved changes. Do you want to continue" or something.

The easiest code would be:

$rootScope.$on("$routeChangeStart", function (event, next, current) {
    if (!wizardService.saved && current.wizard && !next.wizard) {
        if (!confirm("You have unsaved changes. Do you want to continue?")) {
            event.preventDefault();
        }
    }
});

But this does not work, obviously. I could do this with locationChangeStart but then I lose all the route data.

What would be the best way to make something like this?

Thanks in advance!

@ghost ghost assigned IgorMinar Jan 3, 2014
@IgorMinar
Copy link
Contributor

This is not ideal, but you could just listen on $routeChangeStart and keep references to current and next route. Then when the $locationChangeStart event is broadcasted you can use the kept references to determine if you want to abort or not.

$routeChangeStart always fires before $locationChangeStart.

@yentheo
Copy link
Author

yentheo commented Jan 3, 2014

You suggest something like this:

var currentRoute, nextRoute;

$rootScope.$on("$routeChangeStart", function (event, next, current) {
    currentRoute = current.$$route;
    nextRoute = next.$$route;
});

$rootScope.$on("$locationChangeStart", function (event, next, current) {
    if (!wizardService.saved && currentRoute.wizard && !nextRoute.wizard) {
        if (!confirm("You have unsaved changes. Do you want to continue?")) {
            event.preventDefault();
        }
    }
});

I thought there may be some functionality on the routeProvider to get the route data based on a url. Something like:

var routeData = $routeProvider.getRoute('/someUrl/5');

Thanks for your answer, btw.

@yentheo
Copy link
Author

yentheo commented Jan 3, 2014

I have just given this a try... It did not work. $locationChangeStart fires before $routeChangeStart.
I should add that I don't use $location.path to navigate but I always use hyperlinks.

Maybe that changes something in the order?

@khellang
Copy link
Contributor

khellang commented Jan 7, 2014

👍 For the proposed fix. Too bad I can't use it right away 😄

caitp added a commit to caitp/angular.js that referenced this issue Jan 9, 2014
This change allows ngRoute route changes to be cancelled using $event.preventDefault.

This enables route changes to be aborted at the discretion of the application.

Route changes may not be cancelled if forceReload is set to true.

Closes angular#5581
@caitp
Copy link
Contributor

caitp commented Jan 9, 2014

I was bored and decided to push a clean patch for this @yoerlemans --- but if you reopen a PR I'll take a look at it and close mine.

caitp added a commit to caitp/angular.js that referenced this issue Jan 9, 2014
This change allows ngRoute route changes to be cancelled using $event.preventDefault.

This enables route changes to be aborted at the discretion of the application.

Route changes may not be cancelled if forceReload is set to true.

Closes angular#5581
caitp added a commit to caitp/angular.js that referenced this issue Jan 10, 2014
This change allows ngRoute route changes to be cancelled using $event.preventDefault.

This enables route changes to be aborted at the discretion of the application.

Route changes may not be cancelled if forceReload is set to true.

Closes angular#5581
caitp added a commit to caitp/angular.js that referenced this issue Jan 10, 2014
This change allows ngRoute route changes to be cancelled using $event.preventDefault.

This enables route changes to be aborted at the discretion of the application.

Route changes may not be cancelled if forceReload is set to true.

Closes angular#5581
caitp added a commit to caitp/angular.js that referenced this issue Jan 11, 2014
This change allows ngRoute route changes to be cancelled using $event.preventDefault.

This enables route changes to be aborted at the discretion of the application.

Route changes may not be cancelled if forceReload is set to true.

Closes angular#5581
@jakubsikora
Copy link

@IgorMinar, @yoerlemans so... any other alternative for this? How can I stop the change after the next route has been parsed? As the parseRoute API is not exposed, is my only alternative to copy that code into my app and parse the paths in $locationChangeStart by myself? I need the route definition for checking things like permissions.

@sinelaw
Copy link
Contributor

sinelaw commented Jan 27, 2014

+1

@khellang
Copy link
Contributor

For anyone who wants to reject a route change (e.g. for authentication/authorization purposes), as a workaround for this issue, check out this plnkr.

@mikehayesuk
Copy link

+1 👍

@romul
Copy link

romul commented Apr 11, 2014

+1 for the proposed fix.

@etorres
Copy link

etorres commented Apr 19, 2014

+1 clean and nice solution

@jamesmorgan
Copy link

+1 👍

@geddski
Copy link
Contributor

geddski commented May 22, 2014

#4192 will solve this.

@guptapriyank
Copy link

+1 helped me too

@swlasse
Copy link

swlasse commented Jun 10, 2014

Thanks for the workaround. Are there any plans for when the fix for this issue will be available in one of the stable builds?

@pencilcheck
Copy link

@khellang's solution works for me. Thanks.

@OakBehringer
Copy link

Building on the example provided by @khellang , here is an demo of how you can pass rights/roles to the route provider.

http://embed.plnkr.co/gfF8c0ggklNh9F1c71sF/preview

@btford btford removed the gh: issue label Aug 20, 2014
caitp added a commit to caitp/angular.js that referenced this issue Oct 3, 2014
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
caitp added a commit to caitp/angular.js that referenced this issue Oct 3, 2014
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
tbosch added a commit to tbosch/angular.js that referenced this issue Oct 8, 2014
Calling `preventDefault()` on a `$routeChangeStart` event will
prevent the route change and also call `preventDefault` on the `$locationChangeStart` event, which prevents the location change as well.

BREAKING CHANGE:

Order of events has changed.
Previously: `$locationChangeStart` -> `$locationChangeSuccess` 
  -> `$routeChangeStart` -> `$routeChangeSuccess`

Now: `$locationChangeStart` -> `$routeChangeStart` 
  -> `$locationChangeSuccess` ->  -> `$routeChangeSuccess`

Fixes angular#5581
Closes angular#5714
@tbosch tbosch closed this as completed in f4ff11b Oct 8, 2014
bullgare pushed a commit to bullgare/angular.js that referenced this issue Oct 9, 2014
Calling `preventDefault()` on a `$routeChangeStart` event will
prevent the route change and also call `preventDefault` on the `$locationChangeStart` event, which prevents the location change as well.

BREAKING CHANGE:

Order of events has changed.
Previously: `$locationChangeStart` -> `$locationChangeSuccess`
  -> `$routeChangeStart` -> `$routeChangeSuccess`

Now: `$locationChangeStart` -> `$routeChangeStart`
  -> `$locationChangeSuccess` ->  -> `$routeChangeSuccess`

Fixes angular#5581
Closes angular#5714
Closes angular#9502
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.