Est-il possible de détecter qu'un utilisateur a saisi une page en utilisant le bouton d'historique dans son navigateur? De préférence, je veux détecter cette action en utilisant angular.js.
Je ne veux pas utiliser le routage angulaire. Cela devrait également fonctionner si un utilisateur envoie un formulaire et après une soumission réussie au serveur et une redirection, cela devrait également être possible si l'utilisateur revient au formulaire en utilisant le bouton Précédent du navigateur.
Voici une solution avec 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?');
}
});
});
J'utilise un bloc d'exécution pour lancer ceci. D'abord, je stocke l'emplacement actuel dans $ rootScope.actualLocation, puis j'écoute $ locationChangeSuccess et, le cas échéant, je mets à jour actualLocation avec la nouvelle valeur.
Dans $ rootScope, je surveille les changements dans le chemin de l'emplacement et si le nouvel emplacement est égal à previousLocation, c'est que $ locationChangeSuccess n'a pas été déclenché, ce qui signifie que l'utilisateur a utilisé l'historique en arrière.
Si vous souhaitez une solution plus précise (détection en amont et en aval), j'ai étendu la solution fournie par 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++;
}
}
});
Pour le routage, j'utilise le code ci-dessous.
Dans App.run()
, utilisez
$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;
});
Vous pouvez obtenir la valeur 'previousState' dans l'événement '$ stateChangeSuccess' également comme suit si vous le souhaitez.
$rootScope.$on("$stateChangeSuccess", function (event, toState, toParams, fromState, fromParams)
{
$rootScope.previousStatus = fromState.name;
});
Je sais que c'est une vieille question. Quoi qu'il en soit :)
La réponse de Bema est superbe. Cependant, si vous voulez le faire fonctionner avec des URL "normales" (sans hachage je suppose), vous pouvez comparer le chemin absolu au lieu de celui renvoyé par $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?');
}
});
});
Donc, j'ai transcrit la réponse de @Bema en TypeScript, et voici à quoi ça ressemble:
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);
}
Nous n'avons pas besoin de créer une propriété à partir du service $rootScope
, nous pouvons simplement avoir notre code «succès de changement sur site» et «code sur site modifié» fermés tous deux sur la variable locale actualLocation
. À partir de là, vous pouvez faire ce que vous voulez, comme dans le code d'origine. Pour ma part, j’envisagerais de diffuser un événement afin que les contrôleurs individuels puissent faire tout ce qu’ils doivent, mais vous pouvez inclure des actions globales si vous deviez .
Merci pour cette excellente réponse et j'espère que cela aidera d'autres utilisateurs de TypeScript.
JavaScript a un objet d'histoire natif.
window.history
Vérifiez le MDN pour plus d'informations. https://developer.mozilla.org/en-US/docs/DOM/window.history?redirectlocale=en-US&redirectslug=window.history
Vous ne savez pas à quel point il est bon de prendre en charge plusieurs navigateurs.
Mettre à jour
Ce que j'ai appelé ci-dessus ne concerne que les navigateurs de type Gecko.
Pour les autres navigateurs, essayez d'utiliser history.js
; https://github.com/browserstate/history.js