Wie erkennt man das Ereignis "Zurück-Schaltfläche" des Browsers mithilfe des Winkels?

79

Ist es möglich zu erkennen, dass ein Benutzer eine Seite über die Schaltfläche "Zurück" in seinem Browser eingegeben hat? Am besten möchte ich diese Aktion mit angle.js erkennen.

Ich möchte kein Winkelrouting verwenden. Es sollte auch funktionieren, wenn ein Benutzer ein Formular sendet und nach einer erfolgreichen Übermittlung an den Server und einer Umleitung sollte es auch möglich sein, wenn der Benutzer über die Schaltfläche "Zurück" des Browsers zum Formular zurückkehrt.

Thomas Kremmel
quelle

Antworten:

120

Hier ist eine Lösung mit 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?');
        }
    });
});

Ich benutze einen Run-Block, um dies zu starten. Zuerst speichere ich den tatsächlichen Speicherort in $ rootScope.actualLocation, dann höre ich $ locationChangeSuccess ab und aktualisiere in diesem Fall den tatsächlichen Speicherort mit dem neuen Wert.

Im $ rootScope achte ich auf Änderungen im Speicherortpfad. Wenn der neue Speicherort dem vorherigen Standort entspricht, wurde $ locationChangeSuccess nicht ausgelöst, was bedeutet, dass der Benutzer den Verlauf zurück verwendet hat.

Bertrand
quelle
1
Vielen Dank! Sieht großartig aus. Müssen es morgen im Detail überprüfen. Insbesondere muss ich prüfen, ob die $ rootScope.actualLocation auch aktualisiert wird, wenn die Position ohne Verwendung von Winkelrouten, aber "normalen" Hyperlinks geändert wird. Glaubst du, dass es auch mit normalen Hyperlinks funktionieren wird?
Thomas Kremmel
4
Ah ja. Ich habe noch nie zuvor Winkelrouten verwendet. Wenn Sie sich also Ihr Beispiel ansehen, scheint es, dass es keine normalen und nicht normalen Hyperlinks (Typ mit Winkelrouten) gibt.
Also
2
Der $ locationProvider.html5Mode (true) wird nur verwendet, damit Links in jsFiddle funktionieren. Sie werden in Ihrem Projekt nicht benötigt. Denken Sie daran, dass vor den Links # / stehen muss, damit Angular sie erfassen kann (in der Geige sind sie es nicht). Wenn Sie einen "normalen" Link verwenden, z. B. / book, wird Angular nicht erfasst und die Seite wird zu dieser URL umgeleitet. Dadurch wird Ihre App zurückgesetzt, da alle Skripte neu geladen werden. Deshalb funktioniert dies nicht .
Bertrand
19
Für alle (wie mich), die nicht verstehen, wie es funktioniert: Wenn sich die URL auf übliche Weise ändert (ohne Zurück / Vorwärts-Taste), wird nach dem Rückruf ein $locationChangeSuccessEreignis ausgelöst. Wenn sich die URL jedoch über die Zurück / Vorwärts-Taste oder die API ändert , wird das Ereignis vor dem Rückruf ausgelöst. Also, wenn Watch Callback Firing bereits gleich ist . Es ist nur so, wie Winkel intern funktioniert und diese Lösung verwendet nur dieses interne Verhalten. $watchhistory.back()$locationChangeSuccess $watchactualLocationnewLocation
Ruslan Stelmachenko
5
Der obige Code erkennt keine Änderungen an URL-Abfragezeichenfolgen wie von /#/news/?page=2bis /#/news/?page=3. Um auch diese zu fangen, können Sie $location.absUrl()stattdessen $location.path()(an beiden Stellen, an denen es oben verwendet wird) verwenden.
Michael
11

Wenn Sie eine präzisere Lösung wünschen (Hin- und Her erkennen), habe ich die von Bertrand gelieferte Lösung erweitert :

$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
quelle
8

Für das UI-Routing verwende ich den folgenden Code.

Im Inneren App.run()verwenden

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

Sie können den Wert 'previousState' im Ereignis '$ stateChangeSuccess' auch wie folgt abrufen, wenn Sie möchten.

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

        $rootScope.previousStatus = fromState.name;
    });
Harish Polanki
quelle
Dies erkennt keine Klicks auf die Schaltfläche "Zurück" - dies erkennt nur, ob der Benutzer zwischen zwei Statusnamen wechselt - entweder über die Schaltfläche "Zurück" oder durch Klicken zwischen zwei Seiten. Auch hier geht es nicht um die Parameter des Staates.
Amy B
1

Ich weiß, dass es eine alte Frage ist. Sowieso :)

Bemas Antwort sieht gut aus. Wenn Sie jedoch möchten, dass es mit "normalen" URLs funktioniert (ohne Hash, denke ich), können Sie den absoluten Pfad vergleichen, anstatt den von $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
quelle
0

JavaScript hat ein natives Verlaufsobjekt.

window.history

Überprüfen Sie das MDN für weitere Informationen; https://developer.mozilla.org/en-US/docs/DOM/window.history?redirectlocale=de-US&redirectslug=window.history

Ich bin mir nicht sicher, wie gut die Unterstützung für mehrere Browser ist.

Aktualisieren

Das, was ich oben genannt habe, scheint nur für Browser vom Typ Gecko zu gelten.

Versuchen Sie für andere Browser zu verwenden history.js; https://github.com/browserstate/history.js


quelle
Vielen Dank für Ihren Kommentar. Da ich es vorziehe, es in einem Winkel zu tun, werde ich Bertrands Antwort versuchen.
Thomas Kremmel
0

Also habe ich die Antwort von @Bema in TypeScript transkribiert und so sieht es aus:

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

Wir müssen keine Eigenschaft außerhalb des $rootScopeDienstes erstellen, sondern können nur unseren Code "Erfolg beim Ändern vor Ort" und " Vor Ort geändert" über der lokalen actualLocationVariablen schließen. Von dort aus können Sie wie im Originalcode tun, was Sie möchten. Ich für meinen Teil würde in Betracht ziehen, ein Ereignis zu senden, damit einzelne Controller alles tun können, was sie müssen, aber Sie könnten globale Aktionen einschließen, wenn Sie müssten .

Vielen Dank für die großartige Antwort, und ich hoffe, dies hilft anderen Typoskript-Benutzern da draußen.

Andrew Gray
quelle
-2

Mein Solutio zu diesem Problem ist

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
quelle
-7

Beim Drücken der Zurück-Taste löst Angular nur das Ereignis $ routeChangeStart aus , $ locationChangeStart wird nicht ausgelöst.

Bullgare
quelle