This is not really a bug, it's a nuisance:
It's a common use case in an application to prompt the user when a route change is detected with the current page editing a dirty record.
To do this we need to add a listener for $locationChangeStart event. This listener gets called with the event and the new url that would be navigated to.
In this event we preventDefault and then prompt the user if they really want to navigate away from a dirty record. If they say "yes" then we have a problem because the new url parameter passed to our handler is actually the entire path and if we feed it to $location.path(newUrl) it fails because it's the entire url, not just the part that $location.path expects.
This means we need to resort to some possibly sketchy parsing along these lines:
http://stackoverflow.com/questions/20303073/angularjs-location-path-with-full-url
It would be nice to be able to pass the newUrl parameter from the $locationChangeStart event directly to the $location.path() method.
Or, alternatively, if $location.path was smart enough to recognize and handle this situation.
It does kinda suck doesn't it? A way to specify the full url would be much easier. I expect there's a good reason why this isn't an exposed feature, though --- one reason would be to avoid issues with urls not containing the app base, although I suspect that's not the real reason.
Anyways, I'd be happy to review a PR implementing a nicer way to do this.
@WhatFreshHellIsThis i was trying to understand the issue. i wandered around and wrote a test case. http://plnkr.co/edit/rMEXrFoxDNPeD88GcV5z?p=preview with help of some available blog posts.
But i realized in the above test-case atlease i don't need to specifically set the location.path(newUrl) to allow going to the new location. where i can just put up a confirm dialog and pause the redirection and based on user input we can decide weather to prevent this change of location or go to the new url .
Am i doing something wrong in understanding the case you are pointing ?
Solution i was thinking:
But still it would be good if location.path can handle the full urls.
I was thinking https://github.com/angular/angular.js/blob/master/src/ng/location.js#L419 as the second function is pre-processor of the path passed.
We can check if the path passed in the function is a full url and process the full url to get the path which should be set .
Options to check if it is a full url:
Options to preprocess the url if its a full url.
@samuelharden It is a little unintuitive that the full URL doesn't work as expected. Nonetheless, the solution is not too tricky:
$location.path(nextUrl.substring($location.absUrl().length - $location.url().length));
... simply subtract the length of the absolute URL from the base URL.
In context of $locationChangeStart, with event.preventDefault():
var isLeaving = false;
$scope.$on('$locationChangeStart', function(ev, nextUrl) {
if (!isLeaving) {
$timeout(function() {
isLeaving = true;
$location.path(nextUrl.substring($location.absUrl().length - $location.url().length));
$scope.$apply();
}, 1000, false);
ev.preventDefault();
}
});
Yup already posted that workaround @bguiz if you look at the first post. It's not hard at all to solve but it is unintuitive, inconsistent and should be fixed.
I agree with the comments above. It is a bit weird the angular provides the whole URL internally if it only expects you to use a part of it. Either the event should only send the appropriate URI or the $location.path should accept a full URL.
@abobwhite :+1: I also agree.
I ran into the same issue and found that you can work around the inability to specify full paths by using:
$rootScope.$on('$locationChangeStart', function (event) {
var path = $location.path();
if ($rootScope.formDirty) {
SweetAlert.swal({
title: 'Still Editing',
text: 'You have unsaved changes. Are you sure you want to leave?',
type: 'warning',
showCancelButton: true,
confirmButtonColor: '#F8BB86',
confirmButtonText: 'Yes'
}, function (isConfirm) {
if (isConfirm) {
$rootScope.formDirty = false;
$location.path(path);
}
});
event.preventDefault();
}
});
I found that capturing the $location.path() initially was enough to use it in the callback.
I ended up using the workaround by @bguiz.
@esetnik . Thanks. Getting the path initially worked for me.
@esetnik solution works and I think it is cleaner than the one from @bguiz. Thanks!
Probably it is better to use $location.url() instead of $location.path() - to preserve search and hash parts. But that depends on what you want to do.
I ran into the same issue and found that you can work around the inability to specify full paths by using:
$rootScope.$on('$locationChangeStart', function (event) { var path = $location.path(); if ($rootScope.formDirty) { SweetAlert.swal({ title: 'Still Editing', text: 'You have unsaved changes. Are you sure you want to leave?', type: 'warning', showCancelButton: true, confirmButtonColor: '#F8BB86', confirmButtonText: 'Yes' }, function (isConfirm) { if (isConfirm) { $rootScope.formDirty = false; $location.path(path); } }); event.preventDefault(); } });I found that capturing the
$location.path()initially was enough to use it in the callback.
@esetnik
This makes perfect sense, but for some reason when my sweetAlert pops up, the background is already on the next page that user wanted to go.
Any idea how I can prevent that? I dont want screen to switch before user confirms.
Thanks
@mustafabereket sorry I don鈥檛 use angular anymore so I won鈥檛 be too helpful with your question. The preventDefault should be sufficient to prevent the route change so I鈥檓 not sure why your background would be changing.
Most helpful comment
I ran into the same issue and found that you can work around the inability to specify full paths by using:
I found that capturing the
$location.path()initially was enough to use it in the callback.