Як виявити подію клацання кнопки браузера за допомогою 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, а коли це відбувається, я оновлюю фактичне розташування новим значенням.

У $ rootScope я спостерігаю за змінами в шляху розташування, і якщо нове розташування дорівнює попередньому розташуванню, це тому, що $ locationChangeSuccess не було запущено, тобто користувач використав історію назад.


1
Дякую! Виглядає здорово. Доводиться це детально перевіряти завтра. Особливо мені доводиться перевіряти, чи оновлюється $ rootScope.actualLocation також, якщо місце розташування змінюється без використання кутових маршрутів, але "звичайних" гіперпосилань. Ви вважаєте, що це буде працювати і за допомогою звичайних гіперпосилань?
Thomas Kremmel,

4
ах так. ніколи раніше не використовував кутові маршрути, тому, дивлячись на ваш приклад, здається, що немає нормальних і не нормальних (тип кутового маршруту) гіперпосилань. так що просто забудьте мій коментар ..
Томас Креммель

2
$ LocationProvider.html5Mode (true) використовується лише для того, щоб посилання працювали в jsFiddle, він вам не знадобиться у вашому проекті. Просто пам’ятайте, що посиланням потрібно передувати # /, щоб кутові могли їх захопити (у скрипці вони не є). Якщо ви використовуєте "звичайне" посилання, щось на зразок / 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

Для користувальницької інтерфейсу я використовую наведений нижче код.

Всередині 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


Дякуємо за ваш коментар. Оскільки я віддаю перевагу способу зробити це в кутовому режимі, я спробую відповісти Бертранду.
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

при натисканні кнопки назад кутові пожежі запускають лише подію $ routeChangeStart , $ locationChangeStart не запускається .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.