web-dev-qa-db-fra.com

AngularJS window.onbeforeunload dans un contrôleur est déclenché sur un autre contrôleur

Voici mon problème, j'ai deux vues (View1 et View2) et un contrôleur pour chaque vue (Ctrl1 et Ctrl2). Dans View1, j'essaie d'avertir l'utilisateur avant qu'il ne quitte la page accidentellement sans enregistrer les modifications.

J'utilise window.onbeforeunload, ce qui fonctionne très bien, mais mon problème est que même si je n'ai onbeforeunload que sur un contrôleur, l'événement semble également être déclenché dans le deuxième contrôleur! Mais je n'ai même pas eu cet événement là-dedans. Lorsque l'utilisateur quitte une page et qu'il y a des modifications non enregistrées, il est averti par onbeforeunload, si l'utilisateur rejette l'alerte et quitte la page, est-il ramené à la deuxième vue, si l'utilisateur essaie de quitter maintenant cette autre vue, nous serions également prévenus des modifications non enregistrées! Quand ce n'est même pas le cas! Pourquoi cela arrive-t-il?

J'utilise également $ locationChangeStart, qui fonctionne très bien, mais uniquement lorsque l'utilisateur modifie l'itinéraire, pas lorsqu'il actualise le navigateur, appuie sur 'retour' ou essaie de le fermer, c'est pourquoi je suis obligé d'utiliser onbeforeunload ( Si vous avez une meilleure approche, veuillez me le faire savoir). Toute aide ou meilleure approche serait grandement appréciée!

//In this controller I check for window.onbeforeunload
app.controller("Ctrl1", function ($scope, ...)){
  window.onbeforeunload = function (event) {        

    //Check if there was any change, if no changes, then simply let the user leave
    if(!$scope.form.$dirty){
      return;
    }

    var message = 'If you leave this page you are going to lose all unsaved changes, are you sure you want to leave?';
    if (typeof event == 'undefined') {
      event = window.event;
    }
    if (event) {
      event.returnValue = message;
    }
    return message;
  }

  //This works only when user changes routes, not when user refreshes the browsers, goes to previous page or try to close the browser
  $scope.$on('$locationChangeStart', function( event ) {    
    if (!$scope.form.$dirty) return;
    var answer = confirm('If you leave this page you are going to lose all unsaved changes, are you sure you want to leave?')
    if (!answer) {
      event.preventDefault();
    }
  });
}

//This other controller has no window.onbeforeunload, yet the event is triggered here too!
app.controller("Ctrl2", function ($scope, ...)){
  //Other cool stuff ...
}

J'ai essayé de vérifier le chemin actuel avec $ location.path () pour avertir uniquement l'utilisateur lorsqu'il est dans la vue que je veux que l'onbeforeunload soit déclenché, mais cela semble un peu `` hacky '' la solution serait de ne pas exécuter onbeforeunload sur l'autre contrôleur du tout.

J'ai ajouté $ location.path ()! = '/ View1' dans le premier qui semble fonctionner, mais cela ne me semble pas juste, d'ailleurs je n'ai pas seulement deux vues, je avoir plusieurs vues et cela deviendrait difficile avec plus de contrôleurs impliqués:

app.controller("Ctrl1", function ($scope, ...)){
  window.onbeforeunload = function (event) {        

    //Check if there was any change, if no changes, then simply let the user leave
    if($location.path() != '/view1' || !$scope.form.$dirty){
      return;
    }

    var message = 'If you leave this page you are going to lose all unsaved changes, are you sure you want to leave?';
    if (typeof event == 'undefined') {
      event = window.event;
    }
    if (event) {
      event.returnValue = message;
    }
    return message;
  }

  //This works only when user changes routes, not when user refreshes the browsers, goes to previous page or try to close the browser
  $scope.$on('$locationChangeStart', function( event ) {    
    if (!$scope.form.$dirty) return;
    var answer = confirm('If you leave this page you are going to lose all unsaved changes, are you sure you want to leave?')
    if (!answer) {
      event.preventDefault();
    }
  });
}
23
Poncho

Désenregistrez l'événement onbeforeunload lorsque le contrôleur qui l'a défini sort du domaine:

$scope.$on('$destroy', function() {
    delete window.onbeforeunload;
});
24
Jawher

J'ai juste essayé la solution ci-dessus, et cela ne fonctionnait pas pour moi. Même en tapant manuellement delete window.onbeforeunload dans la console ne supprimerait pas la fonction. J'avais plutôt besoin de définir la propriété sur undefined.

$scope.$on('$destroy', function(e){
  $window.onbeforeunload = undefined;
});
20
Nick Fitzpatrick

Pour ceux d'entre vous qui utilisent angular-ui-router vous utiliseriez est $stateChangeStart au lieu de $locationChangeStart, par exemple.

$scope.$on('$stateChangeStart', function (event){
  if (forbit){
    event.preventDefault()
  }
  else{  
    return
  }
})
8
Santiago Angel

En guise de mise à jour, pour toute personne utilisant un routeur ui angulaire, je passerais maintenant $ transitions dans votre contrôleur et l'utiliser;

$transitions.onStart({}, function ($transition)
{
    $transition.abort();
    //return false;
});
3
Shawson