Как отправить HTTP-запрос перед выгрузкой в ​​AngularJS?

У меня есть простое угловое приложение с двумя представлениями, загружаемыми с помощью 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();
    }
});

Однако мне не удалось обработать случай, когда пользователь покидает страницу. Следуя приведенному выше ответу и этому сообщению в группах Google: 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. 
};

Но это не сработало. Затем я прочитал здесь: Как выполнить функцию ajax onbeforeunload? что запрос должен быть синхронным. a> что Angular не поддерживает это (хотя это может быть устаревшим).

Я также пытался вызвать $http напрямую (без службы), но это тоже не сработало. На данный момент я застрял. Любые предложения / выводы будут действительно оценены.

Заранее спасибо!


person Levon Tamrazov    schedule 10.02.2014    source источник
comment
Вы можете показать информацию о том, что данные будут потеряны, если пользователь сейчас уйдет, просто верните строку внутри обработчика onbeforeunload.   -  person Sebastian    schedule 11.09.2014


Ответы (2)


Проблема в том, что 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);
}

Он будет блокировать пользовательский интерфейс до тех пор, пока запрос не будет выполнен, поэтому постарайтесь сделать сервер быстрым, иначе пользователь это заметит.

person Jose Ch.    schedule 21.09.2014
comment
Это все еще действительно зависит от того, какой тип вызова сервера вы делаете. Я пытаюсь сделать то же самое, когда я вызываю сервер, чтобы убить объект сеанса. Но столкнулся с проблемой, когда пользователь просто перезагружает текущую страницу :( поэтому сеанс все еще должен быть активным. - person Dejan Vasic; 05.02.2015

Решение состоит в том, чтобы вызвать $rootScope.$digest(); после запуска ваших $http вызовов в обратном вызове onbeforeunload. Причина в том, что методы angular $http оборачивают конфигурацию в немедленно разрешенное обещание, что означает, что запрос ajax фактически не запускается до следующего тика. Другие браузеры, по-видимому, допускают еще один тик после onbeforeunload тика, но не IE11 (единственная версия IE, в которой я тестировал). Принудительный цикл дайджеста позволяет избежать необходимости ждать следующего тика.

person Jason Rust    schedule 07.07.2016
comment
Это работало в Chrome (версия 51.0.2704.103 (64-разрядная версия)) - person Erin Geyer; 08.08.2016