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

41,945

Solution 1

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

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

Solution 2

I just attempted the above solution, and that wasn't working for me. Even manually typing delete window.onbeforeunload in the console wouldn't remove the function. I needed to set the property to undefined instead.

$scope.$on('$destroy', function(e){
  $window.onbeforeunload = undefined;
});

Solution 3

For those of you using angular-ui-router you would use is $stateChangeStart instead of $locationChangeStart, e.g.

$scope.$on('$stateChangeStart', function (event){
  if (forbit){
    event.preventDefault()
  }
  else{  
    return
  }
})

Solution 4

As an update to this, for anyone used angular-ui-router I'd now pass $transitions into your controller and use;

$transitions.onStart({}, function ($transition)
{
    $transition.abort();
    //return false;
});
Share:
41,945

Related videos on Youtube

Poncho
Author by

Poncho

Updated on June 06, 2020

Comments

  • Poncho
    Poncho almost 4 years

    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();
        }
      });
    }
    
  • Poncho
    Poncho over 9 years
    Exactly what I needed! Thank you!
  • spig
    spig about 9 years
    Probably because you can't delete properties if they exist in the prototype-chain. Setting onbeforeunload = undefined worked for me while delete didn't.
  • OmPrakash
    OmPrakash almost 7 years
    Exact problem I faced, Above answer solved my issue!! thanks!!
  • AhammadaliPK
    AhammadaliPK almost 4 years
    but if you reload the second time , the beforeunload event is hitting but not showing pop up ,any guess why ?
  • AhammadaliPK
    AhammadaliPK almost 4 years
    Same for closing window, first time it is working fine , $window.onbeforeunload . pop up is coming when you click close button , but after reload , if you click close button , it is hitting the event but not waiting for confirm dialogue , confirm dialogue executes but returns false , any idea ?