Jak wykryć zdarzenie kliknięcia przycisku Wstecz w przeglądarce za pomocą kątowej?

79

Czy można wykryć, że użytkownik wszedł na stronę, korzystając z przycisku historii w przeglądarce? Najlepiej chcę wykryć tę akcję za pomocą angular.js.

Nie chcę używać frezowania kątowego. Powinno również zadziałać, jeśli użytkownik prześle formularz i po udanym przesłaniu na serwer i przekierowaniu, powinno być również możliwe, jeśli użytkownik wróci do formularza za pomocą przycisku wstecz w przeglądarce.

Thomas Kremmel
źródło

Odpowiedzi:

120

Oto rozwiązanie z 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?');
        }
    });
});

Aby to rozpocząć, używam bloku uruchamiania. Najpierw zapisuję aktualną lokalizację w $ rootScope.actualLocation, następnie słucham $ locationChangeSuccess i kiedy to się dzieje, aktualizuję realLocation o nową wartość.

W $ rootScope obserwuję zmiany w ścieżce lokalizacji i jeśli nowa lokalizacja jest równa previousLocation, wynika to z tego, że $ locationChangeSuccess nie został uruchomiony, co oznacza, że ​​użytkownik wykorzystał historię z powrotem.

Bertrand
źródło
1
Dzięki! Wygląda świetnie. Muszę to jutro szczegółowo sprawdzić. Szczególnie muszę sprawdzić, czy $ rootScope.actualLocation jest również aktualizowana, jeśli lokalizacja jest zmieniana bez użycia tras kątowych, ale "normalnych" hiperłączy. Czy uważasz, że będzie działać również przy użyciu zwykłych hiperłączy?
Thomas Kremmel
4
o tak. nigdy wcześniej nie korzystałem z tras kątowych, więc patrząc na twój przykład wydaje się, że nie ma normalnych i nie normalnych (typ trasy kątowej) hiperłączy. więc zapomnij o moim komentarzu ...
Thomas Kremmel
2
$ LocationProvider.html5Mode (true) jest używany tylko do tego, aby linki działały w jsFiddle, nie będziesz go potrzebować w swoim projekcie. Pamiętaj tylko, że linki muszą być poprzedzone znakiem # /, aby kątowe mogły je uchwycić (w skrzypcach nie są). Jeśli użyjesz „normalnego” linku, coś takiego jak / book, angular go nie przechwyci, a strona zostanie przekierowana do tego adresu URL, spowoduje to zresetowanie aplikacji, ponieważ wszystkie skrypty zostaną ponownie załadowane, dlatego nie będzie działać .
Bertrand,
19
Dla każdego (takiego jak ja), który nie rozumie, jak to działa: Kiedy adres URL zmienia się w zwykły sposób (bez użycia przycisku Wstecz / Dalej), $locationChangeSuccesszdarzenie jest uruchamiane po $watch wywołaniu zwrotnym. Ale gdy zmieniasz adres URL za pomocą przycisku Wstecz / history.back()Dalej lub interfejsu API, $locationChangeSuccesszdarzenie jest uruchamiane przed $watch wywołaniem zwrotnym. Kiedy więc odpalanie wywołania zwrotnego zegarka actualLocationjest już równe newLocation. Po prostu działa wewnętrznie angular, a to rozwiązanie po prostu wykorzystuje to wewnętrzne zachowanie.
Ruslan Stelmachenko
5
Powyższy kod nie wykrywa zmian ciągu zapytania URL, na przykład od /#/news/?page=2do /#/news/?page=3. Aby je też złapać, możesz użyć $location.absUrl()zamiast $location.path()(w obu miejscach, w których jest używany powyżej).
Michael
11

Jeśli chcesz bardziej precyzyjnego rozwiązania (wykrywanie wstecz i do przodu), rozszerzyłem rozwiązanie dostarczone przez firmę Bertrand :

$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++;

        }

    }

 });
Mariusz
źródło
8

W przypadku routingu ui używam poniższego kodu.

Wewnątrz App.run()użyj

 $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;
   });

Możesz uzyskać wartość 'previousState' w zdarzeniu '$ stateChangeSuccess' również w następujący sposób, jeśli chcesz.

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

        $rootScope.previousStatus = fromState.name;
    });
harish polanki
źródło
Nie wykrywa to kliknięć przycisku Wstecz - wykrywa tylko, czy użytkownik przełącza się między dwoma nazwami stanów - albo za pomocą przycisku Wstecz, albo klikając między dwiema stronami. Również to nie patrzy na parametry stanu.
Amy B,
1

Wiem, że to stare pytanie. Tak czy inaczej :)

Odpowiedź Bemy wygląda świetnie. Jeśli jednak chcesz, aby działało z `` normalnymi '' adresami URL (chyba bez hasha), możesz porównać ścieżkę bezwzględną, zamiast tej zwróconej przez $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?');
        }
    });
});
Jahongir Rahmonov
źródło
0

JavaScript ma natywny obiekt historii.

window.history

Sprawdź MDN, aby uzyskać więcej informacji; https://developer.mozilla.org/en-US/docs/DOM/window.history?redirectlocale=en-US&redirectslug=window.history

Nie jestem pewien, jak dobrze jest obsługiwany przez wiele przeglądarek.

Aktualizacja

Wygląda na to, że to, co nazwałem powyżej, dotyczy tylko przeglądarek typu Gecko.

W przypadku innych przeglądarek spróbuj użyć history.js; https://github.com/browserstate/history.js


źródło
Dzięki za komentarz. Ponieważ wolę sposób, aby zrobić to pod kątem, spróbuję odpowiedzieć Bertrands.
Thomas Kremmel
0

Tak więc przepisałem odpowiedź @Bema na TypeScript i tak to wygląda:

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);
}

Nie musimy tworzyć właściwości poza $rootScopeusługą, możemy po prostu zamknąć kod „przy zmianie lokalizacji” i „przy zmianie lokalizacji” na actualLocationzmiennej lokalnej . Stamtąd możesz robić, co chcesz, tak jak w oryginalnym kodzie. Ze swej strony Pomyślę nadawania zdarzenia tak, że poszczególne kontrolery mogą robić, co im wolno, ale można obejmować działania globalne , jeśli trzeba było .

Dziękuję za świetną odpowiedź i mam nadzieję, że pomoże to innym użytkownikom maszynopisu.

Andrew Gray
źródło
-2

Moim rozwiązaniem tego problemu jest

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();
    });
});
syraz37
źródło
-7

po naciśnięciu przycisku Wstecz kątowy odpala tylko zdarzenie $ routeChangeStart , $ locationChangeStart nie jest wyzwalany.

bzdury
źródło