AngularJs - Routenänderungsereignis abbrechen


88

Wie kann ich ein Routenänderungsereignis in AngularJs abbrechen?

Mein aktueller Code ist

$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  

}
});

Selbst wenn die Validierung fehlschlägt, zieht Angular die nächste Vorlage und die zugehörigen Daten und wechselt dann sofort zur vorherigen Ansicht / Route zurück. Ich möchte nicht, dass Angular die nächste Vorlage und die nächsten Daten abruft, wenn die Validierung fehlschlägt. Idealerweise sollte kein window.history.back () vorhanden sein. Ich habe sogar event.preventDefault () ausprobiert, aber keine Verwendung.

Antworten:


184

Anstelle von $routeChangeStartGebrauch$locationChangeStart

Hier ist die Diskussion der Angularjs: https://github.com/angular/angular.js/issues/2109

Bearbeiten 3/6/2018 Sie finden es in den Dokumenten: https://docs.angularjs.org/api/ng/service/$location#event-$locationChangeStart

Beispiel:

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

8
Das Problem dabei ist, dass es keine Möglichkeit gibt, auf die Sammlung der Routenparameter zuzugreifen. Wenn Sie versuchen, Routenparameter zu validieren, ist diese Lösung nicht gut.
KingOfHypocrites

1
Bitte beachten Sie, wenn $routeChangeStartdie nextVariable nur eine Zeichenfolge ist und keine Daten enthalten kann (zum Beispiel können Sie nicht auf definierte frühere authorizedRolesVariablen zugreifen )
MyTitle

@KingOfHypocrites Sie können keine Routenparameter erhalten, aber Sie können $location.path()und$location.search()
Design von Adrian

2
Können / ist es ratsam, dies auf dem rootScope zu tun, wenn Sie alle Routenänderungen verfolgen möchten? Oder gibt es eine schmackhaftere Alternative?
Maxm

38

Ein vollständigeres Codebeispiel mit $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); 
      });
    }
  );

Ich bin nicht ganz zufrieden damit, dies in meinem Root-Controller (Top-Level-Controller) anzuschließen. Wenn es ein besseres Muster gibt, würde ich gerne wissen. Ich bin neu in Angular :-)


Das hat bei mir sehr gut funktioniert, obwohl ich nicht versuche, meine Routenänderung wie das Original-Poster abzubrechen. Vielen Dank!
Jim Clouse

4
Ja, das Problem mit rootScope ist, dass Sie daran denken müssen, diesen Handler zu lösen, wenn Ihr Controller ausfällt.
Verlorene Übersetzung

12

Eine Lösung besteht darin, ein 'notAuthorized'-Ereignis zu senden und es im Hauptbereich abzufangen, um den Speicherort erneut zu ändern. Ich denke, es ist nicht die beste Lösung, aber es hat bei mir funktioniert:

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

und in meinem Hauptcontroller:

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

Hinweis: Es gibt einige Diskussionen zu diesem Problem auf einer eckigen Website, die noch nicht gelöst ist: https://github.com/angular/angular.js/pull/4192

BEARBEITEN:

Um den Kommentar zu beantworten, finden Sie hier weitere Informationen zu den Funktionen von LoginService. Es enthält 3 Funktionen:

  1. login () (Name ist irreführend) fordert den Server auf, Informationen über den (zuvor) angemeldeten Benutzer abzurufen. Es gibt eine weitere Anmeldeseite, auf der nur der aktuelle Benutzerstatus auf dem Server ausgefüllt wird (mithilfe des SpringSecurity-Frameworks). Meine Webdienste sind nicht wirklich zustandslos, aber ich habe es vorgezogen, dass dieses berühmte Framework meine Sicherheit übernimmt.
  2. isAuthenticated () sucht nur, ob die Client-Sitzung mit Daten gefüllt ist, was bedeutet, dass sie vor (*) authentifiziert wurde.
  3. isAuthorized () behandelte Zugriffsrechte (außerhalb des Bereichs für dieses Thema).

(*) Meine Sitzung wird ausgefüllt, wenn sich die Route ändert. Ich habe dann die when () -Methode überschrieben, um die Sitzung zu füllen, wenn sie leer ist.

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

Können Sie bitte sagen, was LoginService.isAuthenticated()beim Laden der ersten Seite zurückkommt ? Wie lagern Sie currentUser? Was passiert, wenn der Benutzer die Seite aktualisiert (der Benutzer muss die Anmeldeinformationen erneut eingeben)?
MyTitle

Ich habe in meiner ursprünglichen Antwort weitere Informationen zum LoginService hinzugefügt. Der aktuelle Benutzer wird vom Server bereitgestellt, und die Routenänderung behandelt alle Seitenaktualisierungen. Der Benutzer muss sich nicht erneut anmelden.
Asterius

4

Für jeden, der darüber stolpert, ist eine alte Frage (zumindest in Winkel 1.4): Sie können dies tun:

 .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
Ich frage mich, ob für die Verwendung von Zahnspangen oder ";" eine zusätzliche Gebühr erhoben wird.
Cel Sharp

6
@MatthiasJansen natürlich. Und um das Ganze abzurunden, zählen geschweifte Klammern doppelt und Semikolons dreifach.
Ákos Vandra

1

Dies ist meine Lösung und funktioniert für mich, aber ich weiß nicht, ob ich auf dem richtigen Weg bin, weil ich neu in Webtechnologien bin.

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
Wie können Sie eine Frage beantworten, wenn Sie sich bei Ihrer Antwort nicht sicher sind?
DeW1

Ich bin sicher, dass es funktioniert. Ich bin mir nicht sicher, ob dies der richtige Weg ist. Wenn Sie eine bessere Lösung haben, würde ich es gerne sehen.
Alexandrakis Alexandros

1

Ich fand dieses relevant

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

Mein Beitrag könnte in Zukunft jemandem helfen.


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
Während dieses Code-Snippet die Frage lösen kann, hilft eine Erklärung wirklich dabei, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage für Leser in Zukunft beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen.
dpr

0

Falls Sie verhindern müssen, dass sich die Route im $routeChangeStartEreignis ändert (dh Sie möchten eine Operation basierend auf der nächsten Route ausführen ), injizieren Sie $routeund $routeChangeStartrufen Sie an:

$route.reload()

1
Ich war hoffnungsvoll, aber in Angular 1.2.7 auf Chrome scheint dies eine JS-Schleife zu verursachen und die Seite friert ein.
Nick Spacek

1
@NickSpacek Die Bedingung, in der Sie aufrufen $route.reload(), muss anders sein, sonst wird wieder derselbe Code ausgeführt. Dies entspricht dem Erstellen einer whileSchleife mit trueder Bedingung.
Kevin Beal
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.