AngularJs - Annuler l'événement de changement d'itinéraire


88

Comment annuler un événement de changement d'itinéraire dans AngularJs?

Mon code actuel est

$rootScope.$on("$routeChangeStart", function (event, next, current) {

// do some validation checks
if(validation checks fails){

    console.log("validation failed");

    window.history.back(); // Cancel Route Change and stay on current page  

}
});

avec ceci même si la validation échoue Angular extrait le modèle suivant et les données associées, puis revient immédiatement à la vue / route précédente. Je ne veux pas qu'angular tire le prochain modèle et les données si la validation échoue, idéalement, il ne devrait pas y avoir de window.history.back (). J'ai même essayé event.preventDefault () mais cela ne sert à rien.

Réponses:


184

Au lieu d' $routeChangeStartutiliser$locationChangeStart

Voici la discussion à ce sujet des gars angularjs: https://github.com/angular/angular.js/issues/2109

Modifier le 06/03/2018 Vous pouvez le trouver dans la documentation: https://docs.angularjs.org/api/ng/service/$location#event-$locationChangeStart

Exemple:

$scope.$on('$locationChangeStart', function(event, next, current) {
    if ($scope.form.$invalid) {
       event.preventDefault();
    }
});

8
Le problème avec ceci est qu'il n'y a aucun moyen d'accéder à la collection de paramètres d'itinéraire. Si vous essayez de valider les paramètres d'itinéraire, cette solution n'est pas bonne.
KingOfHypocrites

1
Veuillez noter que lorsque vous utilisez $routeChangeStartla nextvariable, il ne s'agit que d'une chaîne et qu'elle ne peut contenir aucune donnée (par exemple, vous ne pouvez pas accéder à la authorizedRolesvariable précédente définie )
MyTitle

@KingOfHypocrites, vous ne pouvez pas obtenir de paramètres d'itinéraire, mais vous pouvez obtenir $location.path()et$location.search()
Design by Adrian

2
Pouvez-vous / est-il conseillé de le faire sur le rootScope si vous souhaitez suivre tous les changements d'itinéraire? Ou existe-t-il une alternative plus appétissante?
maxm

38

Un exemple de code plus complet, utilisant $locationChangeStart

// assuming you have a module called app, with a 
angular.module('app')
  .controller(
    'MyRootController',
    function($scope, $location, $rootScope, $log) {
      // your controller initialization here ...
      $rootScope.$on("$locationChangeStart", function(event, next, current) { 
        $log.info("location changing to:" + next); 
      });
    }
  );

Je ne suis pas complètement content de le connecter à mon contrôleur racine (contrôleur de niveau supérieur). S'il y a un meilleur modèle, j'aimerais le savoir. Je suis nouveau dans angular :-)


Cela a très bien fonctionné pour moi, même si je n'essaye pas d'annuler mon changement d'itinéraire comme l'affiche d'origine. Merci!
Jim Clouse

4
Oui, le problème avec rootScope est que vous devez vous rappeler de dissocier ce gestionnaire lorsque votre contrôleur disparaît.
lostintranslation

12

Une solution consiste à diffuser un événement «notAuthorized» et à l'attraper dans la portée principale pour modifier l'emplacement. Je pense que ce n'est pas la meilleure solution, mais cela a fonctionné pour moi:

myApp.run(['$rootScope', 'LoginService',
    function ($rootScope, LoginService) {
        $rootScope.$on('$routeChangeStart', function (event, next, current) {
            var authorizedRoles = next.data ? next.data.authorizedRoles : null;
            if (LoginService.isAuthenticated()) {
                if (!LoginService.isAuthorized(authorizedRoles)) {
                    $rootScope.$broadcast('notAuthorized');
                }
            }
        });
    }
]);

et dans mon contrôleur principal:

    $scope.$on('notAuthorized', function(){
        $location.path('/forbidden');
    });

Remarque: il y a une discussion sur ce problème sur le site angular, pas encore résolu: https://github.com/angular/angular.js/pull/4192

ÉDITER:

Pour répondre au commentaire, voici plus d'informations sur les travaux de LoginService. Il contient 3 fonctions:

  1. login () (le nom est trompeur) fait une requête au serveur pour obtenir des informations sur l'utilisateur (précédemment) connecté. Il existe une autre page de connexion qui remplit simplement l'état actuel de l'utilisateur sur le serveur (à l'aide du framework SpringSecurity). Mes services Web ne sont pas vraiment sans état, mais j'ai préféré laisser ce fameux framework gérer ma sécurité.
  2. isAuthenticated () recherche simplement si la session client est remplie de données, ce qui signifie qu'elle a été authentifiée avant (*)
  3. isAuthorized () gère les droits d'accès (hors de portée de cette rubrique).

(*) Ma session est remplie lorsque l'itinéraire change. J'ai remplacé la méthode when () pour peupler la session lorsqu'elle est vide.

Voici le code:

services.factory('LoginService', ['$http', 'Session', '$q',
function($http, Session, $q){
    return {
        login: function () {
            var defer = $q.defer();
            $http({method: 'GET', url: restBaseUrl + '/currentUser'})
                .success(function (data) {
                    defer.resolve(data);
                });
            return defer.promise;
        },
        isAuthenticated: function () {
            return !!Session.userLogin;
        },
        isAuthorized: function (authorizedRoles) {
            if (!angular.isArray(authorizedRoles)) {
                authorizedRoles = [authorizedRoles];
            }

            return (this.isAuthenticated() &&  authorizedRoles.indexOf(Session.userRole) !== -1);
        }
    };
}]);

myApp.service('Session', ['$rootScope',
    this.create = function (userId,userLogin, userRole, userMail, userName, userLastName, userLanguage) {
        //User info
        this.userId = userId;
        this.userLogin = userLogin;
        this.userRole = userRole;
        this.userMail = userMail;
        this.userName = userName;
        this.userLastName = userLastName;
        this.userLanguage = userLanguage;
    };

    this.destroy = function () {
        this.userId = null;
        this.userLogin = null;
        this.userRole = null;
        this.userMail = null;
        this.userName = null;
        this.userLastName = null;
        this.userLanguage = null;
        sessionStorage.clear();
    };

    return this;
}]);

myApp.config(['$routeProvider', 'USER_ROLES', function ($routeProvider, USER_ROLES) {
    $routeProvider.accessWhen = function (path, route) {
        if (route.resolve == null) {
            route.resolve = {
                user: ['LoginService','Session',function (LoginService, Session) {
                    if (!LoginService.isAuthenticated())
                        return LoginService.login().then(function (data) {
                            Session.create(data.id, data.login, data.role, data.email, data.firstName, data.lastName, data.language);
                            return data;
                        });
                }]
            }
        } else {
            for (key in route.resolve) {
                var func = route.resolve[key];
                route.resolve[key] = ['LoginService','Session','$injector',function (LoginService, Session, $injector) {
                    if (!LoginService.isAuthenticated())
                        return LoginService.login().then(function (data) {
                            Session.create(data.id, data.login, data.role, data.email, data.firstName, data.lastName, data.language);
                            return func(Session, $injector);
                        });
                    else
                        return func(Session, $injector);
                }];
            }
        }
    return $routeProvider.when(path, route);
    };

    //use accessWhen instead of when
    $routeProvider.
        accessWhen('/home', {
            templateUrl: 'partials/dashboard.html',
            controller: 'DashboardCtrl',
            data: {authorizedRoles: [USER_ROLES.superAdmin, USER_ROLES.admin, USER_ROLES.system, USER_ROLES.user]},
            resolve: {nextEvents: function (Session, $injector) {
                $http = $injector.get('$http');
                return $http.get(actionBaseUrl + '/devices/nextEvents', {
                    params: {
                        userId: Session.userId, batch: {rows: 5, page: 1}
                    },
                    isArray: true}).then(function success(response) {
                    return response.data;
                });
            }
        }
    })
    ...
    .otherwise({
        redirectTo: '/home'
    });
}]);

Pouvez-vous s'il vous plaît dire quel retour LoginService.isAuthenticated()au chargement de la première page? Comment stockez-vous currentUser? Que se passe-t-il si l'utilisateur actualise la page (l'utilisateur doit à nouveau saisir les informations d'identification)?
MyTitle

J'ai ajouté plus d'informations sur LoginService dans ma réponse d'origine. Le currentUser est fourni par le serveur et le changement d'itinéraire gère toute actualisation de page, il n'est pas nécessaire que l'utilisateur se connecte à nouveau.
Asterius

4

Pour quiconque tombe sur cette question est une vieille question, (au moins dans angulaire 1.4), vous pouvez le faire:

 .run(function($rootScope, authenticationService) {
        $rootScope.$on('$routeChangeStart', function (event, next) {
            if (next.require == undefined) return

            var require = next.require
            var authorized = authenticationService.satisfy(require);

            if (!authorized) {
                $rootScope.error = "Not authorized!"
                event.preventDefault()
            }
        })
      })

6
Je me demande, est-ce que vous êtes facturé en supplément pour l'utilisation d'accolades ou ";"?
cel sharp du

6
@MatthiasJansen bien sûr. Et pour couronner le tout, les accolades comptent double et les points-virgules comptent triple.
Ákos Vandra le

1

C'est ma solution et cela fonctionne pour moi, mais je ne sais pas si je suis sur la bonne voie car je suis novice dans les technologies Web.

var app = angular.module("app", ['ngRoute', 'ngCookies']);
app.run(function($rootScope, $location, $cookieStore){
$rootScope.$on('$routeChangeStart', function(event, route){
    if (route.mustBeLoggedOn && angular.isUndefined($cookieStore.get("user"))) {
        // reload the login route
        jError(
             'You must be logged on to visit this page',
             {
               autoHide : true,
               TimeShown : 3000,
               HorizontalPosition : 'right',
               VerticalPosition : 'top',
               onCompleted : function(){ 
               window.location = '#/signIn';
                 window.setTimeout(function(){

                 }, 3000)
             }
        });
    }
  });
});

app.config(function($routeProvider){
$routeProvider
    .when("/signIn",{
        controller: "SignInController",
        templateUrl: "partials/signIn.html",
        mustBeLoggedOn: false
});

2
Comment pouvez-vous répondre à une question si vous n'êtes pas sûr de votre réponse?
deW1

Je suis sûr que ça marche. Je ne sais pas si c'est la bonne manière. Si vous avez une meilleure solution, j'aimerais la voir.
Alexandrakis alexandros

1

j'ai trouvé celui-ci pertinent

var myApp = angular.module('myApp', []);

myApp.run(function($rootScope) {
    $rootScope.$on("$locationChangeStart", function(event, next, current) { 
        // handle route changes  
$rootScope.error = "Not authorized!"
                event.preventDefault()   
    });
});

mon message peut aider quelqu'un à l'avenir.


1
var app=angular
    .module('myapp', [])
    .controller('myctrl', function($rootScope) {
        $rootScope.$on("locationChangeStart", function(event, next, current) {
        if (!confirm("location changing to:" + next)) { 
            event.preventDefault();
        }
    })
});

2
Bien que cet extrait de code puisse résoudre la question, inclure une explication contribue vraiment à améliorer la qualité de votre message. N'oubliez pas que vous répondez à la question aux lecteurs à l'avenir, et que ces personnes pourraient ne pas connaître les raisons de votre suggestion de code.
dpr le

0

Dans le cas où vous devez empêcher la route de changer dans l' $routeChangeStartévénement (c'est-à-dire que vous voulez effectuer une opération basée sur la route suivante ), injectez $routeet $routeChangeStartappelez à l' intérieur :

$route.reload()

1
J'avais bon espoir, mais dans Angular 1.2.7 sur Chrome, cela semble provoquer une boucle JS et la page se fige.
Nick Spacek

1
@NickSpacek La condition dans laquelle vous appelez $route.reload()doit être différente, sinon il exécute à nouveau le même code. C'est l'équivalent de créer une whileboucle avec truecomme condition.
Kevin Beal
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.