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 Arun
la source

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();
    }
});
Mathew Berg
la source
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 :-)

Shyam Habarakada
la source
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'
    });
}]);
Astérius
la source
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()
            }
        })
      })
Ákos Vandra
la source
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
});
Alexandrakis alexandros
la source
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.

Monojit Sarkar
la source
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();
        }
    })
});
Studio Macle
la source
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()
mrt
la source
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