AngularJS window.onbeforeunload в одном контроллере запускается на другом контроллере

Вот моя проблема, у меня есть два представления (View1 и View2) и контроллер для каждого представления (Ctrl1 и Ctrl2). В View1 я пытаюсь предупредить пользователя, прежде чем он случайно покинет страницу, без сохранения изменений.

Я использую window.onbeforeunload, который работает нормально, но моя проблема в том, что, хотя у меня onbeforeunload только на одном контроллере, событие, похоже, запускается и на втором контроллере! Но у меня там даже не было этого мероприятия. Когда пользователь покидает страницу и есть несохраненные изменения, он получает предупреждение onbeforeunload, если пользователь отклоняет предупреждение и покидает страницу, возвращается ли он ко второму представлению, если пользователь сейчас пытается покинуть это другое представление, мы также получим предупреждение о несохраненных изменениях! Когда это даже не так! Почему это происходит?

Я также использую $ locationChangeStart, который работает нормально, но только тогда, когда пользователь изменяет маршрут, а не когда он обновляет браузер, нажимает «назад» или пытается закрыть его, поэтому я вынужден использовать onbeforeunload ( Если вы подойдете лучше, дайте мне знать). Приветствуется любая помощь или лучший подход!

//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 ...
}

Я попытался проверить текущий путь с помощью $ location.path (), чтобы предупреждать пользователя только тогда, когда он находится в представлении. Я хочу, чтобы запускался onbeforeunload, но это кажется своего рода «хакерским», решение заключалось бы в том, чтобы не выполнять onbeforeunload на другом контроллер вообще.

Я добавил $ location.path ()! = '/ view1' в первый, который, кажется, работает, но мне это кажется неправильным, к тому же у меня не только два представления, я есть несколько представлений, и это будет сложно, если задействовано больше контроллеров:

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();
    }
  });
}

person Poncho    schedule 27.08.2014    source источник


Ответы (4)


Отмените регистрацию события onbeforeunload, когда контроллер, который его определил, выходит за пределы области действия:

$scope.$on('$destroy', function() {
    delete window.onbeforeunload;
});
person Jawher    schedule 27.08.2014
comment
Именно то, что мне нужно! Спасибо! - person Poncho; 27.08.2014

Я просто попробовал описанное выше решение, но это не сработало. Даже ручной ввод delete window.onbeforeunload в консоли не приведет к удалению функции. Вместо этого мне нужно было установить для свойства значение undefined.

$scope.$on('$destroy', function(e){
  $window.onbeforeunload = undefined;
});
person Nick Fitzpatrick    schedule 17.12.2014
comment
Вероятно, потому что вы не можете удалить свойства, если они существуют в цепочке прототипов. Установка onbeforeunload = undefined сработала для меня, а удаление - нет. - person spig; 03.03.2015
comment
Точная проблема, с которой я столкнулся, выше ответ решил мою проблему !! Благодарность!! - person OmPrakash; 27.07.2017
comment
но если вы перезагружаете второй раз, событие beforeunload срабатывает, но не всплывает, угадайте, почему? - person AhammadaliPK; 29.05.2020
comment
То же самое для закрытия окна, в первый раз оно работает нормально, $ window.onbeforeunload. всплывающее окно появляется, когда вы нажимаете кнопку закрытия, но после перезагрузки, если вы нажимаете кнопку закрытия, оно попадает в событие, но не ожидает диалога подтверждения, диалог подтверждения выполняется, но возвращает false, любая идея? - person AhammadaliPK; 29.05.2020

Для тех из вас, кто использует angular-ui-router, вы должны использовать $stateChangeStart вместо $locationChangeStart , например

$scope.$on('$stateChangeStart', function (event){
  if (forbit){
    event.preventDefault()
  }
  else{  
    return
  }
})
person Santiago Angel    schedule 26.03.2015

В качестве обновления для всех, кто использовал angular-ui-router, я бы теперь передал $ transitions в ваш контроллер и использовал;

$transitions.onStart({}, function ($transition)
{
    $transition.abort();
    //return false;
});
person Shawson    schedule 10.04.2018