AngularJS window.onbeforeunload in one controller is being triggered on another controller

angularjsonbeforeunload

Here is my problem, I have two views (View1 and View2) and a controller for each view (Ctrl1 and Ctrl2). In View1 I'm trying to warn the user before he leaves the page accidentally without saving changes.

I'm using window.onbeforeunload, which works just fine, but my problem is that even though I have onbeforeunload only on one controller, the event seems to be triggered in the second controller as well! But I don't have even have that event in there. When the user leaves a page and there are unsaved changes, he gets warned by the onbeforeunload, if the user dismiss the alert and leaves the page, is he taken back to the second view, if the user tries to leave now this other view, we would also get warned about unsaved changes! When that's not even the case! Why is this happening?

I'm also using $locationChangeStart, which works just fine, but only when the user changes the route, not when they refresh the browser, hit 'back', or try to close it, that's why I'm forced to use onbeforeunload (If you a better approach, please let me know). Any help or a better approach would be greatly appreciated!

//In this controller I check for window.onbeforeunload
app.controller("Ctrl1", function ($scope, ...)){
  window.onbeforeunload = function (event) {        

    //Check if there was any change, if no changes, then simply let the user leave
    if(!$scope.form.$dirty){
      return;
    }

    var message = 'If you leave this page you are going to lose all unsaved changes, are you sure you want to leave?';
    if (typeof event == 'undefined') {
      event = window.event;
    }
    if (event) {
      event.returnValue = message;
    }
    return message;
  }

  //This works only when user changes routes, not when user refreshes the browsers, goes to previous page or try to close the browser
  $scope.$on('$locationChangeStart', function( event ) {    
    if (!$scope.form.$dirty) return;
    var answer = confirm('If you leave this page you are going to lose all unsaved changes, are you sure you want to leave?')
    if (!answer) {
      event.preventDefault();
    }
  });
}

//This other controller has no window.onbeforeunload, yet the event is triggered here too!
app.controller("Ctrl2", function ($scope, ...)){
  //Other cool stuff ...
}

I tried checking the current path with $location.path() to only warn the user when he is in the view I want the onbeforeunload to be triggered, but this seems kinda 'hacky' the solution would be to not execute onbeforeunload on the other controller at all.

I added $location.path() != '/view1' in the first which seems to work, but it doesn't seem right to me, besides I don't only have two views, I have several views and this would get tricky with more controllers involved:

app.controller("Ctrl1", function ($scope, ...)){
  window.onbeforeunload = function (event) {        

    //Check if there was any change, if no changes, then simply let the user leave
    if($location.path() != '/view1' || !$scope.form.$dirty){
      return;
    }

    var message = 'If you leave this page you are going to lose all unsaved changes, are you sure you want to leave?';
    if (typeof event == 'undefined') {
      event = window.event;
    }
    if (event) {
      event.returnValue = message;
    }
    return message;
  }

  //This works only when user changes routes, not when user refreshes the browsers, goes to previous page or try to close the browser
  $scope.$on('$locationChangeStart', function( event ) {    
    if (!$scope.form.$dirty) return;
    var answer = confirm('If you leave this page you are going to lose all unsaved changes, are you sure you want to leave?')
    if (!answer) {
      event.preventDefault();
    }
  });
}

Best Answer

Unregister the onbeforeunload event when the controller which defined it goes out of scope:

$scope.$on('$destroy', function() {
    delete window.onbeforeunload;
});