无法使用Angular服务处理onbeforeunload事件

4
我在我的服务中有一个 onbeforeUnload 事件的回调函数。在我的 app.run 块中,我写下了以下代码:

window.onbeforeunload = Services.beforeWindowClose;

这个方法是在一个服务中定义的:
this.beforeWindowClose = function (event) {
            var currentState = $state.current.name;
            if (currentState !== constant.LOGOUT) {
                $rootScope.$broadcast("close");
                if (Utilities.isSavePending)
                    event.returnValue = constant.MSG;
            }
        }

在我的控制器中我有:

$scope.$on("close", function () {
            Utilities.isSavePending = vm.save; //sets it to true
        })

现在,在Angular中,由于事件是同步的,这段代码应该在窗口关闭时弹出一个弹窗。然而,它直接关闭了我的窗口。

我的意图是,每当用户关闭窗口时,我会触发一个事件并查看我的控制器中是否有任何未保存的数据。如果有一些未保存的数据,则浏览器不应关闭,并给出一个弹窗,而如果没有未保存的数据,则浏览器应关闭。

我做错了什么或者理解有误吗?


你是说你没有收到浏览器默认的确认对话框吗?预期的行为和问题不是很清楚。 - charlietfl
另外,尝试使用 return constant.MSG,因为一些浏览器需要这样做。 - dloeda
是的,charlietfl,你理解得没错。我没有收到弹出窗口。预期的行为是只有当isSavePending变为true时才会弹出窗口。 - Saurabh Tiwari
@dloeda 回复的消息也没有帮助。 - Saurabh Tiwari
你确认双重检查了两个条件是否正确,并且 event.returnValue = constant.MSG; 代码正在执行吗? - dloeda
@dloeda 这正是问题所在。一旦事件被触发,控制器中的值就会被正确设置。此后,我期望浏览器来检查我的 if(Utilities.isSavePending) 条件。浏览器确实会到这里来,但悬停在 if(Utilities.isSavePending) 上却没有显示任何内容,然后 if(Utilities.isSavePending) 就从未执行过,浏览器就直接关闭了。 - Saurabh Tiwari
1个回答

6
在您的模块run函数中,必须以以下方式声明beforeunload事件:
.run(['$window', 'Utilities', function($window, Utilities) {  
  $window.addEventListener('beforeunload', function(e)         
     // Place code here
  };       
});

不要这样:

.run(['$window', 'Utilities', function($window, Utilities) {  
    window.onbeforeunload = function() {         
     // Place code here
    };
}]);

这里有一个片段,可以使用Angular的onbeforeunload事件。

注意:根据您的浏览器,片段在您单击“保存项目”按钮并尝试关闭此窗口后将无法工作。然后,您需要将代码粘贴到自己的项目中。

附加信息

最近的HTML规范现在防止自定义弹出消息,而是显示通用消息。

因此,始终可以防止导航,但不再可能指定自定义消息。

这在IE11上始终有效,但不应持续太久(直到下一次更新)。

关于此的HTML规范: https://html.spec.whatwg.org/multipage/browsers.html#unloading-documents

Chrome / Safari文档: https://www.chromestatus.com/feature/5349061406228480

angular.module('app', []);

angular
  .module('app')
  .controller('ExampleController', ['$scope', 'Utilities', 'ItemService', function($scope, Utilities, ItemService) {
    // Expose Utilities to make pending state available in template
    $scope.Utilities = Utilities;
    // Use item service to save our item
    $scope.save = function() {
      ItemService.saveItem();
    }
    $scope.fireCloseEvent = function() {
      $scope.$emit('close');
    }
    $scope.$on('close', function(event) {
      Utilities.toggleSavePending();
    });
  }])
  .factory('ItemService', ['Utilities', function(Utilities) {
    return {
      saveItem: function() {
        // ...
        // Toggle global save pending state
        Utilities.toggleSavePending();
      }
    }
  }])
  .factory('Utilities', function() {
    var savePending = false;

    return {
      toggleSavePending: function() {
        savePending = !savePending;
      },
      isSavePending: function() {
        return savePending;
      }
    }
  })
  .run(['$window', 'Utilities', function($window, Utilities) {

    $window.addEventListener('beforeunload', function(e) {
      // If save pending state is truthy, prevent browser window from closing
      if (Utilities.isSavePending()) {
        var message = 'Warning! Save pending...';
        e = e || window.event;
        if (e) {
          e.returnValue = message;
        }
        return message;
      }
    });

  }]);
<!doctype html>
<html lang="en" ng-app="app">

<head>
  <meta charset="utf-8">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.min.js"></script>
  <script src="script.js"></script>
</head>

<body ng-controller="ExampleController">

  <button ng-click="save()" ng-disabled="Utilities.isSavePending()">Save item</button>
  <button ng-click="fireCloseEvent()">Fire Close Event</button>
  <div ng-if="Utilities.isSavePending()">A message should be displayed when you close the window</div>
</body>

</html>


感谢您的回答。我已经在许多地方成功地使用了该事件。但是,在这种情况下,我希望弹出窗口仅在应用程序中存在一些更改数据时显示,我使用服务变量跟踪这些更改。但出于某种原因,最终的 if(Utilities.isSavePending) 条件似乎根本不起作用。 - Saurabh Tiwari
你在模块run的函数中注入了Utilities吗? - Stephane Janicaud
@SaurabhTiwari 这是我的最终更新,包括解耦的代码(ItemService、Utilities、Controller)。 - Stephane Janicaud
如果我尝试在onbeforeunload回调函数内使用事件机制来获取“save”状态,相同的代码就无法工作。 - Saurabh Tiwari
1
你必须使用 $window.addEventListener('beforeunload', function(e) 来监听 beforeunload 事件,否则它将无法正常工作。回答已更新。 - Stephane Janicaud
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接