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.