Как определить событие нажатия кнопки возврата в браузере с помощью angular?


79

Можно ли определить, что пользователь вошел на страницу с помощью кнопки возврата истории в своем браузере? Предпочтительно я хочу обнаружить это действие с помощью angular.js.

Я не хочу использовать угловую маршрутизацию. Он также должен работать, если пользователь отправляет форму, и после успешной отправки на сервер и перенаправления это также должно быть возможно, если пользователь вернется к форме с помощью кнопки возврата в браузере.

Ответы:


120

Вот решение с Angular.

myApp.run(function($rootScope, $route, $location){
   //Bind the `$locationChangeSuccess` event on the rootScope, so that we dont need to 
   //bind in induvidual controllers.

   $rootScope.$on('$locationChangeSuccess', function() {
        $rootScope.actualLocation = $location.path();
    });        

   $rootScope.$watch(function () {return $location.path()}, function (newLocation, oldLocation) {
        if($rootScope.actualLocation === newLocation) {
            alert('Why did you use history back?');
        }
    });
});

Я использую блок запуска, чтобы запустить это. Сначала я сохраняю фактическое местоположение в $ rootScope.actualLocation, затем слушаю $ locationChangeSuccess и, когда это происходит, я обновляю actualLocation новым значением.

В $ rootScope я наблюдаю за изменениями в пути к местоположению, и если новое местоположение равно previousLocation, это связано с тем, что $ locationChangeSuccess не был запущен, что означает, что пользователь использовал историю обратно.


1
Благодаря! Выглядит отлично. Надо завтра детально проверить. В частности, я должен проверить, обновляется ли также $ rootScope.actualLocation, если местоположение изменено без использования угловых маршрутов, а "обычных" гиперссылок. Как вы думаете, он будет работать и с обычными гиперссылками?
Thomas Kremmel

4
о да. никогда раньше не использовал угловые маршруты, поэтому, глядя на ваш пример, кажется, что нет нормальных и не нормальных (тип углового маршрута) гиперссылок. так что забудьте мой комментарий ..
Thomas Kremmel

2
$ LocationProvider.html5Mode (true) используется только для того, чтобы ссылки работали в jsFiddle, вам он не понадобится в вашем проекте. Просто помните, что перед ссылками должен стоять # /, чтобы angular мог их захватить (в скрипке их нет). Если вы используете «обычную» ссылку, например / book, angular ее не захватит, и страница будет перенаправлена ​​на этот URL-адрес, это приведет к сбросу вашего приложения, потому что все скрипты будут перезагружены, поэтому он не будет работать .
Бертран

19
Для всех (например, меня), кто не понимает, как это работает: при изменении URL-адреса обычным способом (без использования кнопки назад / вперед), затем $locationChangeSuccessсрабатывает событие после $watch обратного вызова. Но при изменении URL-адреса с помощью кнопки назад / вперед или history.back()api $locationChangeSuccessсобытие запускается перед $watch обратным вызовом. Поэтому, когда срабатывание обратного вызова часов actualLocationуже равно newLocation. Это то, как angular работает внутри, и это решение просто использует это внутреннее поведение.
Руслан Стельмаченко

5
Приведенный выше код не обнаруживает изменения строки запроса URL, например от /#/news/?page=2до /#/news/?page=3. Чтобы поймать их, вы также можете использовать $location.absUrl()вместо $location.path()(в обоих местах, где он используется выше).
Майкл

11

Если вам нужно более точное решение (обнаружение вперед и назад), я расширил решение, предоставленное Бертраном :

$rootScope.$on('$locationChangeSuccess', function() {
    $rootScope.actualLocation = $location.path();
});


$rootScope.$watch(function () {return $location.path()}, function (newLocation, oldLocation) {

    //true only for onPopState
    if($rootScope.actualLocation === newLocation) {

        var back,
            historyState = $window.history.state;

        back = !!(historyState && historyState.position <= $rootScope.stackPosition);

        if (back) {
            //back button
            $rootScope.stackPosition--;
        } else {
            //forward button
            $rootScope.stackPosition++;
        }

    } else {
        //normal-way change of page (via link click)

        if ($route.current) {

            $window.history.replaceState({
                position: $rootScope.stackPosition
            });

            $rootScope.stackPosition++;

        }

    }

 });

8

Для ui-routing я использую приведенный ниже код.

Внутри App.run()используйте

 $rootScope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams) 
 {

    if (toState.name === $rootScope.previousState )
       { 
          // u can any 1 or all of below 3 statements
         event.preventDefault();  // to Disable the event
         $state.go('someDefaultState'); //As some popular banking sites are using 
         alert("Back button Clicked");

       }
 else
      $rootScope.previousState= fromState.name;
   });

Вы можете получить значение 'previousState' в событии '$ stateChangeSuccess' также следующим образом, если хотите.

    $rootScope.$on("$stateChangeSuccess", function (event, toState, toParams, fromState, fromParams) 
   {

        $rootScope.previousStatus = fromState.name;
    });

Это не обнаруживает нажатия кнопки возврата - это просто определяет, переключается ли пользователь между двумя именами состояний - либо с помощью кнопки возврата, либо путем щелчка между двумя страницами. Также это не смотрит на параметры состояния.
Amy B

1

Я знаю, что это старый вопрос. В любом случае :)

Ответ Бемы выглядит великолепно. Однако, если вы хотите, чтобы он работал с `` нормальными '' URL-адресами (я думаю, без хеша), вы можете сравнить абсолютный путь вместо того, который возвращается $location.path():

myApp.run(function($rootScope, $route, $location){
//Bind the `$locationChangeSuccess` event on the rootScope, so that we dont need to 
//bind in induvidual controllers.

    $rootScope.$on('$locationChangeSuccess', function() {
        $rootScope.actualLocation = $location.absUrl();
    });      

    $rootScope.$watch(function () {return $location.absUrl()}, function (newLocation, oldLocation) {
        if($rootScope.actualLocation === newLocation) {
            alert('Why did you use history back?');
        }
    });
});

0

JavaScript имеет собственный объект истории.

window.history

Проверьте MDN для получения дополнительной информации; https://developer.mozilla.org/en-US/docs/DOM/window.history?redirectlocale=en-US&redirectslug=window.history

Не уверен, насколько хороша поддержка нескольких браузеров.

Обновить

Кажется, то, что я назвал выше, предназначено только для браузеров типа Gecko.

Для других браузеров попробуйте использовать history.js; https://github.com/browserstate/history.js


Спасибо за ваш комментарий. Поскольку я предпочитаю использовать angular, я попробую ответить Бертрану.
Thomas Kremmel

0

Итак, я переписал ответ @Bema в TypeScript, и вот как он выглядит:

namespace MyAwesomeApp {
    // ReSharper disable once Class
    function detectBackButton(
        $rootScope: ng.IRootScopeService,
        $location: ng.ILocationService
    ) {
        let actualLocation: string = '';

        $rootScope.$on('$locationChangeSuccess',
            () => {
                actualLocation = $location.path();
            });

        $rootScope.$watch(() => $location.path(),
            (newLocation: string, oldLocation: string) => {
                if (actualLocation === newLocation) {
                    //$rootScope.$broadcast('onBackButtonPressed', null);
                }
            });
    }

    detectBackButton.$inject = [
        '$rootScope',
        '$location'
    ];

    angular
        .module('app')
        .run(detectBackButton);
}

Нам не нужно создавать свойство вне $rootScopeслужбы, мы можем просто закрыть код «при изменении местоположения» и «при изменении местоположения» над локальной actualLocationпеременной. Оттуда вы можете делать все, что захотите, как и в исходном коде. Со своей стороны, я бы подумал о трансляции события, чтобы отдельные контроллеры могли делать все, что они должны, но вы могли бы включить глобальные действия, если бы это было необходимо .

Спасибо за отличный ответ, и я надеюсь, что это поможет другим пользователям машинописного текста.


-2

Мое решение этой проблемы -

app.run(function($rootScope, $location) {
    $rootScope.$on('$locationChangeSuccess', function() {
        if($rootScope.previousLocation == $location.path()) {
            console.log("Back Button Pressed");
        }
        $rootScope.previousLocation = $rootScope.actualLocation;
        $rootScope.actualLocation = $location.path();
    });
});

-7

при нажатии кнопки возврата angular запускает только событие $ routeChangeStart , $ locationChangeStart не запускается.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.