AngularJs: annulla l'evento di cambio percorso


88

Come si annulla l'evento di cambio percorso in AngularJs?

Il mio codice attuale è

$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  

}
});

con questo anche se la convalida fallisce Angular estrae il modello successivo e i dati associati e quindi torna immediatamente alla vista / percorso precedente. Non voglio che angular estragga il modello e i dati successivi se la convalida fallisce, idealmente non dovrebbe esserci window.history.back (). Ho anche provato event.preventDefault () ma inutile.

Risposte:


184

Invece di $routeChangeStartusare$locationChangeStart

Ecco la discussione a riguardo dai ragazzi di angularjs: https://github.com/angular/angular.js/issues/2109

Modifica 3/6/2018 Puoi trovarlo nella documentazione: https://docs.angularjs.org/api/ng/service/$location#event-$locationChangeStart

Esempio:

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

8
Il problema con questo è che non c'è modo di accedere alla raccolta dei parametri del percorso. Se stai cercando di convalidare i parametri del percorso, questa soluzione non è buona.
KingOfHypocrites

1
Nota, quando si utilizza $routeChangeStartla nextvariabile è solo una stringa e non può contenere dati (ad esempio non è possibile accedere alla authorizedRolesvariabile precedente definita )
MyTitle

@KingOfHypocrites non puoi ottenere i parametri del percorso, ma puoi ottenere $location.path()e$location.search()
Design by Adrian

2
Puoi / è consigliabile farlo su rootScope se vuoi tenere traccia di tutti i cambiamenti di percorso? O esiste un'alternativa più appetibile?
maxm

38

Un esempio di codice più completo, utilizzando $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); 
      });
    }
  );

Non sono completamente soddisfatto di collegarlo al mio controller di root (controller di livello superiore). Se esiste uno schema migliore, mi piacerebbe saperlo. Sono nuovo in angolare :-)


Questo ha funzionato benissimo per me, anche se non sto cercando di annullare il mio cambio di percorso come il poster orig. Grazie!
Jim Clouse

4
Sì, il problema con rootScope è che devi ricordarti di svincolare quel gestore quando il tuo controller va via.
perdita di traduzione

12

Una soluzione è trasmettere un evento "notAuthorized" e catturarlo nell'ambito principale per modificare nuovamente la posizione. Penso che non sia la soluzione migliore, ma per me ha funzionato:

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');
                }
            }
        });
    }
]);

e nel mio controller principale:

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

Nota: c'è qualche discussione su questo problema sul sito angular, non ancora risolto: https://github.com/angular/angular.js/pull/4192

MODIFICARE:

Per rispondere al commento, ecco ulteriori informazioni sul funzionamento del LoginService. Contiene 3 funzioni:

  1. login () (il nome è fuorviante) fa una richiesta al server per ottenere informazioni sull'utente (precedentemente) registrato. C'è un'altra pagina di accesso che popola solo lo stato utente corrente nel server (utilizzando il framework SpringSecurity). I miei servizi Web non sono veramente apolidi, ma ho preferito lasciare che quel famoso framework gestisse la mia sicurezza.
  2. isAuthenticated () cerca solo se la sessione client è piena di dati, il che significa che è stata autenticata prima (*)
  3. isAuthorized () ha gestito i diritti di accesso (fuori dall'ambito di questo argomento).

(*) La mia sessione viene popolata quando il percorso cambia. Ho quindi sovrascritto il metodo when () per popolare la sessione quando è vuota.

Ecco il codice:

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'
    });
}]);

Puoi per favore dire cosa ritorna LoginService.isAuthenticated()al caricamento della prima pagina? Come immagazzini currentUser? Cosa succede se l'utente aggiorna la pagina (l'utente deve reinserire nuovamente le credenziali)?
MyTitle

Ho aggiunto ulteriori informazioni su LoginService nella mia risposta originale. CurrentUser è fornito dal server e il cambio di percorso gestisce qualsiasi aggiornamento della pagina, non è necessario che l'utente acceda di nuovo.
Asterius

4

Per chiunque si imbattesse in questa è una vecchia domanda, (almeno in angolare 1.4) puoi farlo:

 .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
Mi chiedo, ti viene addebitato un extra per l'utilizzo di parentesi graffe o ";"?
cel sharp

6
@ MatthiasJansen ovviamente. E per finire, le parentesi graffe contano il doppio e il punto e virgola conta il triplo.
Ákos Vandra,

1

Questa è la mia soluzione e funziona per me, ma non so se sono sulla strada giusta perché sono nuovo alle tecnologie 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
Come puoi rispondere a una domanda se non sei sicuro della tua risposta?
deW1

Sono sicuro che funziona. Non sono sicuro che questo sia il modo corretto. Se hai una soluzione migliore, mi piacerebbe vederla.
Alexandrakis alexandros

1

ho trovato questo pertinente

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

il mio post potrebbe aiutare qualcuno in futuro.


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
Sebbene questo snippet di codice possa risolvere la domanda, includere una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che stai rispondendo alla domanda per i lettori in futuro e quelle persone potrebbero non conoscere i motivi del tuo suggerimento sul codice.
dpr

0

Nel caso in cui sia necessario interrompere la modifica del percorso $routeChangeStartnell'evento (ovvero si desidera eseguire alcune operazioni in base al percorso successivo ), inject $routee inside $routeChangeStartcall:

$route.reload()

1
Speravo, ma in Angular 1.2.7 su Chrome questo sembra causare un loop JS e la pagina si blocca.
Nick Spacek

1
@NickSpacek La condizione in cui chiami $route.reload()deve essere diversa, altrimenti viene eseguito di nuovo lo stesso codice. Questo è l'equivalente della creazione di un whileciclo con truecome condizione.
Kevin Beal
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.