Question

I've got a form where I want to prevent the user from navigating away without saving their data back to the server. I borrowed code from here that lets me add a listener to the $locationChangeStartevent. I put the following code in the controller for my form, and lo and behold, nothing happened.

    var cancelRouteListener = $rootScope.$on('$locationChangeStart', checkForUnsavedData);

    function checkForUnsavedData (event, newURL) {
        if (!$scope.form.$dirty) {
            return;
        }

        var proceed = $window.confirm('The page has unsaved changes. Do you want to continue?');

        if (proceed) {
            cancelRouteListener();
            $location.path(newURL);
        }

        event.preventDefault();
        return;
    }

I could see the code reaching the call to $location.path(), but the page navigation didn't change. I then, out of desperation, tried wrapping that line of the code in a call to $rootScope.$apply():

$rootScope.$apply(function () {
    $location.path(newURL);
}

Lo and behold, that worked. I've got a few questions:

  1. Why did I need to use the .$apply() function? I'd have thought the code would run in the context of $rootScope.
  2. Do I really need to attach this to $rootScope? I only need this listener when the form's route is active.
Was it helpful?

Solution

First, don't you mean $apply()? Second, why would you use $rootScope to kick off a digest cycle instead of $scope within the controller. Third, I don't think you're using the correct logic to achieve the desired functionality. the event $locationChangeStart signals the start of a location change within the Angular Application. $window.confirm() will halt that navigation temporarily while calling event.preventDefault() will stop the navigation all together. Therefore, you should wait for confirmation from $window.confirm(). If the use clicks cancel, proceed will be false. If proceed is false, call event.preventDefault(), otherwise, do nothing. There is absolutely no need for you to call $location.path() when the application is already in the process of navigating to that page.

I've created a Plunk of the scenario here:

http://plnkr.co/edit/qfRVDtIlKqQ1p0pZ4oZ1?p=preview

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top