简体   繁体   English

如何使用angular检测浏览器后退按钮单击事件?

[英]How to detect browser back button click event using angular?

Is it possible to detect that a user entered a page through using the history back button in his browser? 是否可以通过浏览器中的历史记录返回按钮检测用户是否输入了页面? Preferably I want to detect this action using angular.js. 我最好想用angular.js检测这个动作。

I do not want to use angular routing. 我不想使用角度路由。 It should also work if a user submits a form and after a successful submit to the server and a re-direct, it should also be possible if the user goes back to the form using the back button of the browser. 如果用户提交表单并在成功提交到服务器并重新定向之后,它也应该有效,如果用户使用浏览器的后退按钮返回到表单,也应该可以。

Here is a solution with Angular. 这是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?');
        }
    });
});

I am using a run block to kickstart this. 我正在使用一个运行块来启动它。 First I store the actual location in $rootScope.actualLocation, then I listen to $locationChangeSuccess and when it happens I update actualLocation with the new value. 首先,我将实际位置存储在$ rootScope.actualLocation中,然后我监听$ locationChangeSuccess,当它发生时,我用新值更新actualLocation。

In the $rootScope I watch for changes in the location path and if the new location is equal to previousLocation is because the $locationChangeSuccess was not fired, meaning the user has used the history back. 在$ rootScope中我监视位置路径的变化,如果新位置等于previousLocation是因为没有触发$ locationChangeSuccess,这意味着用户已经使用了历史记录。

If you want more precise solution (detecting back and forward) I extended solution delivered by Bertrand : 如果您想要更精确的解决方案(检测后退和前进),我将通过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++;

        }

    }

 });

For ui-routing, I'm using the below code. 对于ui-routing,我使用下面的代码。

Inside App.run() , use 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;
   });

You can get the 'previousState' value in '$stateChangeSuccess' event also like as follows if `you want. 如果你想要的话,你可以在'$ stateChangeSuccess'事件中得到'previousState'值,如下所示。

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

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

I know it is an old question. 我知道这是一个老问题。 Anyways :) 无论如何 :)

Bema's answer looks great. Bema的答案看起来很棒。 However, if you want to make it work with 'normal' urls (without hash I guess), you could compare absolute path, instead of the one returned by $location.path() : 但是,如果你想让它与'普通'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?');
        }
    });
});

JavaScript has a native history object. JavaScript具有本机历史对象。

window.history

Check the MDN for more info; 检查MDN以获取更多信息; https://developer.mozilla.org/en-US/docs/DOM/window.history?redirectlocale=en-US&redirectslug=window.history https://developer.mozilla.org/en-US/docs/DOM/window.history?redirectlocale=en-US&redirectslug=window.history

Not sure how good it's on multi-browser support tho. 不确定它对多浏览器支持有多好。

Update 更新

Seems what I've called above is only for Gecko-type browsers. 似乎我上面所说的仅适用于Gecko类型的浏览器。

For other browsers try to use history.js ; 对于其他浏览器,尝试使用history.js ; https://github.com/browserstate/history.js https://github.com/browserstate/history.js

So, I've transcribed the answer by @Bema into TypeScript, and this is what it looks like: 所以,我已经将@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);
}

We don't have to create a property off of the $rootScope service, we can just have our 'on location change success' and 'on location changed' code both close over the local actualLocation variable. 我们不必从$rootScope服务创建属性,我们可以让我们的'on location change success'和'on changed changed'代码都靠近本地的actualLocation变量。 From there, you can do whatever you like, just as in the original code. 从那里,你可以做任何你喜欢的事情,就像在原始代码中一样。 For my part, I'd consider broadcasting an event so that individual controllers can do whatever they must, but you could include global actions if you had to . 就我而言,我会考虑在播放事件使个别控制器可以为所欲为必须的,但如果你要 ,你可以包括全球行动。

Thanks for the great answer, and I hope this helps other typescript users out there. 感谢您的回答,我希望这有助于其他打字用户。

My Solutio to this problem is 我对这个问题的解决方案是

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

当按下后退按钮时,角度仅触发$ routeChangeStart事件, $ locationChangeStart未触发。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM