如何在AngularJS中在onbeforeunload事件中发送HTTP请求?

9
我有一个简单的Angular应用程序,使用ngRoute加载两个视图。当用户在视图之间导航或离开页面(刷新窗口、关闭选项卡或关闭浏览器)时,我需要在服务器上进行一些清理操作。
我的第一站是这里:在用户离开页面时显示AngularJS警报。它解决了用户在视图之间导航的情况。我已经像这样处理了清理操作:
$scope.$on('$locationChangeStart', function (event) {
    var answer = confirm("Are you sure you want to leave this page?")
    if (answer) {
        api.unlock($scope.id); //api is a service that I wrote. It uses angular $http service to handle communications and works in all other cases.
    } else {
        event.preventDefault();
    }
});

然而,我还无法处理用户离开页面的情况。根据上面的答案以及这篇谷歌小组帖子:https://groups.google.com/forum/#!topic/angular/-PfujIEdeCY,我尝试了以下方法:
window.onbeforeunload = function (event) {
    api.unlock($scope.id); //I don't necessarily need a confirmation dialogue, although that would be helpful. 
};

但是它没有起作用。然后我在这里阅读到:如何在beforeunload上执行ajax函数?,请求需要同步,在这里:如何使用AngularJS进行$http同步调用中提到Angular不支持这个(尽管这可能已经过时了)。
我还尝试直接调用$http(而不是服务),但那也不起作用。现在我被卡住了。任何建议/线索都将不胜感激。
提前致谢!

你可以在 onbeforeunload 处理程序中返回一个字符串,以显示如果用户现在导航离开,数据将会丢失的信息。 - Sebastian
2个回答

14

问题在于Angular总是使用$http服务进行异步调用(您无法更改该行为)。由于页面在$http服务有机会发出请求之前就已退出,因此您无法获得所需的结果。

如果您想要一个解决方法而不使用任何第三方库,请尝试以下代码:

var onBeforeUnload = function () {

    var data = angular.toJson($scope.id...);//make sure you $scope is legal here...
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("POST", "apiURL/unlock", false);//the false is for making the call synchronous
    xmlhttp.setRequestHeader("Content-type", "application/json");
    xmlhttp.setRequestHeader("Authorization", token);
    xmlhttp.send(data);
}

它将阻塞用户界面,直到请求完成,因此尽量使服务器快速响应,否则用户会注意到。


这仍然取决于您正在进行的服务器调用类型。我正在尝试类似的事情,即调用服务器以终止会话对象。但是遇到了一个问题,即用户仅重新加载当前页面:(,因此会话仍然需要处于活动状态。 - Dejan Vasic

8
解决方案是在onbeforeunload回调中触发你的$http调用后调用$rootScope.$digest();。原因是angular的$http方法将config包装在立即解决的promise中,这意味着ajax请求实际上直到下一个tick才会被触发。其他浏览器似乎允许在onbeforeunload tick之后再来一次tick,但IE11不行(我测试的唯一版本)。强制digest循环避免了等待下一个tick的需要。

这在Chrome上运行良好(版本51.0.2704.103(64位))。 - Erin Geyer

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