AngularJs - ยกเลิกเหตุการณ์การเปลี่ยนเส้นทาง


88

ฉันจะยกเลิกเหตุการณ์การเปลี่ยนเส้นทางใน AngularJs ได้อย่างไร?

รหัสปัจจุบันของฉันคือ

$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  

}
});

ด้วยสิ่งนี้แม้ว่าการตรวจสอบจะล้มเหลว Angular จะดึงเทมเพลตถัดไปและข้อมูลที่เกี่ยวข้องจากนั้นเปลี่ยนกลับไปที่มุมมอง / เส้นทางก่อนหน้าทันที ฉันไม่ต้องการให้เชิงมุมดึงเทมเพลตและข้อมูลถัดไปหากการตรวจสอบล้มเหลวโดยหลักการแล้วไม่ควรมี window.history.back () ฉันเคยลอง event.preventDefault () แต่ไม่มีประโยชน์

คำตอบ:


184

แทนที่จะ$routeChangeStartใช้$locationChangeStart

นี่คือการสนทนาเกี่ยวกับเรื่องนี้จากพวก angularjs: https://github.com/angular/angular.js/issues/2109

แก้ไข 3/6/2018คุณสามารถค้นหาได้ในเอกสาร: https://docs.angularjs.org/api/ng/service/$location#event-$locationChangeStart

ตัวอย่าง:

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

8
ปัญหานี้คือไม่มีวิธีเข้าถึงคอลเล็กชันพารามิเตอร์เส้นทาง หากคุณกำลังพยายามตรวจสอบพารามิเตอร์เส้นทางการแก้ปัญหานี้ไม่ดี
KingOfHypocrites

1
โปรดทราบว่าเมื่อใช้ตัวแปรเป็นเพียงสตริงและมันไม่สามารถมีข้อมูลใด ๆ (เช่นคุณไม่สามารถเข้าถึงที่กำหนดไว้ก่อนหน้านี้ตัวแปร)$routeChangeStartnextauthorizedRoles
MyTitle

@KingOfHypocrites คุณไม่สามารถรับพารามิเตอร์เส้นทางได้ แต่คุณสามารถรับ$location.path()และ$location.search()
ออกแบบโดย Adrian

2
คุณสามารถ / ขอแนะนำให้ทำสิ่งนี้บน rootScope ได้หรือไม่หากคุณต้องการติดตามการเปลี่ยนแปลงเส้นทางทั้งหมด หรือมีทางเลือกอื่นที่ถูกปากกว่านี้?
maxm

38

ตัวอย่างโค้ดที่สมบูรณ์ยิ่งขึ้นโดยใช้ $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); 
      });
    }
  );

ฉันไม่ค่อยพอใจกับการเชื่อมต่อสิ่งนี้ในตัวควบคุมรูทของฉัน (ตัวควบคุมระดับบนสุด) ถ้ามีรูปแบบที่ดีกว่านี้ฉันอยากรู้ ฉันยังใหม่กับ :-) เชิงมุม


วิธีนี้ใช้ได้ผลดีสำหรับฉันแม้ว่าฉันจะไม่ได้พยายามยกเลิกการเปลี่ยนเส้นทางเหมือนโปสเตอร์ต้นฉบับ ขอบคุณ!
Jim Clouse

4
ใช่ปัญหากับ rootScope คือคุณต้องจำไว้ว่าให้เลิกผูกตัวจัดการนั้นเมื่อคอนโทรลเลอร์ของคุณหายไป
lostintranslation

12

วิธีแก้ปัญหาคือการถ่ายทอดเหตุการณ์ที่ 'ไม่ได้รับอนุญาต' และจับมันในขอบเขตหลักเพื่อเปลี่ยนสถานที่ใหม่ ฉันคิดว่ามันไม่ใช่ทางออกที่ดีที่สุด แต่มันใช้ได้ผลสำหรับฉัน:

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

และในตัวควบคุมหลักของฉัน:

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

หมายเหตุ: มีการอภิปรายเกี่ยวกับปัญหานี้ในไซต์เชิงมุมซึ่งยังไม่ได้รับการแก้ไข: https://github.com/angular/angular.js/pull/4192

แก้ไข:

หากต้องการตอบความคิดเห็นนี่คือข้อมูลเพิ่มเติมเกี่ยวกับการใช้งาน LoginService ประกอบด้วย 3 ฟังก์ชั่น:

  1. login () (ชื่อทำให้เข้าใจผิด) ส่งคำขอไปยังเซิร์ฟเวอร์เพื่อรับข้อมูลเกี่ยวกับผู้ใช้ที่เข้าสู่ระบบ (ก่อนหน้านี้) มีหน้าเข้าสู่ระบบอีกหน้าซึ่งเพิ่งเติมข้อมูลสถานะผู้ใช้ปัจจุบันในเซิร์ฟเวอร์ (โดยใช้กรอบงาน SpringSecurity) บริการบนเว็บของฉันไม่ได้เป็นคนไร้สัญชาติ แต่ฉันต้องการให้เฟรมเวิร์กที่มีชื่อเสียงนั้นจัดการความปลอดภัยของฉัน
  2. isAuthenticated () เพียงค้นหาว่าเซสชันไคลเอ็นต์เต็มไปด้วยข้อมูลหรือไม่ซึ่งหมายความว่าได้รับการพิสูจน์ตัวตนก่อนหน้านี้
  3. isAuthorized () จัดการสิทธิ์การเข้าถึง (อยู่นอกขอบเขตสำหรับหัวข้อนี้)

(*) เซสชันของฉันจะถูกเติมเมื่อเส้นทางเปลี่ยนไป ฉันได้แทนที่แล้วเมื่อ () วิธีการเติมเซสชันเมื่อว่างเปล่า

นี่คือรหัส:

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

คุณช่วยบอกได้ไหมว่าผลตอบแทนใดLoginService.isAuthenticated()ในการโหลดหน้าแรก คุณจัดเก็บcurrentUserอย่างไร? จะเกิดอะไรขึ้นหากผู้ใช้รีเฟรชเพจ (ผู้ใช้ต้องป้อนข้อมูลรับรองอีกครั้ง)
MyTitle

ฉันได้เพิ่มข้อมูลเพิ่มเติมเกี่ยวกับบริการเข้าสู่ระบบในคำตอบเดิมของฉัน currentUser จัดเตรียมโดยเซิร์ฟเวอร์และการเปลี่ยนแปลงเส้นทางจัดการรีเฟรชหน้าใด ๆ ไม่จำเป็นต้องให้ผู้ใช้เข้าสู่ระบบอีกครั้ง
Asterius

4

สำหรับใครก็ตามที่สะดุดกับคำถามนี้เป็นคำถามเก่า (อย่างน้อยในเชิงมุม 1.4) คุณสามารถทำได้:

 .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
ฉันสงสัยว่าคุณจะถูกเรียกเก็บเงินเพิ่มเติมสำหรับการใช้เครื่องมือจัดฟันหรือ ";"?
cel sharp

6
@MatthiasJansen แน่นอน. และเหนือสิ่งอื่นใดวงเล็บปีกกาจะนับสองครั้งและอัฒภาคจะนับสามเท่า
Ákos Vandra

1

นี่เป็นวิธีแก้ปัญหาของฉันและใช้ได้กับฉัน แต่ฉันไม่รู้ว่าฉันมาถูกทางหรือเปล่าเพราะฉันยังใหม่กับเทคโนโลยีเว็บ

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
คุณจะตอบคำถามได้อย่างไรหากคุณไม่แน่ใจในคำตอบของคุณ
deW1

ฉันมั่นใจว่าได้ผล ฉันไม่แน่ใจว่านี่เป็นวิธีที่เหมาะสมหรือไม่ หากคุณมีทางออกที่ดีกว่าฉันอยากเห็น
Alexandrakis alexandros

1

ฉันพบว่าสิ่งนี้เกี่ยวข้อง

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

โพสต์ของฉันอาจช่วยได้ในอนาคต


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
แม้ว่าข้อมูลโค้ดนี้จะช่วยแก้ปัญหาได้ แต่การมีคำอธิบายจะช่วยปรับปรุงคุณภาพของโพสต์ของคุณได้มาก โปรดจำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตและบุคคลเหล่านั้นอาจไม่ทราบสาเหตุของการแนะนำโค้ดของคุณ
ธ.ค.

0

ในกรณีที่คุณจำเป็นต้องหยุดการเปลี่ยนเส้นทางใน$routeChangeStartเหตุการณ์ (เช่นคุณต้องการดำเนินการบางอย่างตามเส้นทางถัดไป ) ให้ฉีด$routeและ$routeChangeStartโทรภายใน:

$route.reload()

1
ฉันมีความหวัง แต่ใน Angular 1.2.7 บน Chrome ดูเหมือนว่าจะทำให้เกิด JS loop และหน้าค้าง
Nick Spacek

1
@NickSpacek เงื่อนไขที่คุณเรียก$route.reload()จะต้องแตกต่างออกไปมิฉะนั้นจะเรียกใช้รหัสเดิมอีกครั้ง นี่เทียบเท่ากับการสร้างwhileลูปโดยมีtrueเงื่อนไข
Kevin Beal

0

เพื่อแบ่งปันในกรณีของฉันฉันต้องการชะลอการแก้ปัญหาเส้นทางด้วย $ routeChangeStart ฉันมี SomethingService ที่ต้องโหลดก่อนที่ความละเอียดของเส้นทางจะเริ่มต้น (ใช่แอปพลิเคชันช่างพูด) ดังนั้นฉันจึงมีสัญญาว่าจะรอ บางทีฉันอาจพบการแฮ็ก ... การแก้ไขเส้นทางเกิดข้อผิดพลาดหากการแก้ไขกลับเป็นการปฏิเสธ ฉันใช้การกำหนดค่าการแก้ไขไม่ได้และจะแก้ไขในภายหลัง

    var rejectingResolve = {
        cancel: function ($q){
            // this will cancel $routeChangeStart
            return $q.reject();
        }
    }
    
    $rootScope.$on("$routeChangeStart", function(event, args, otherArgs) {
        var route = args.$$route,
            originalResolve = route.resolve;
    
        if ( ! SomethingService.isLoaded() ){

            SomethingService.load().then(function(){
                // fix previously destroyed route configuration
                route.resolve = originalResolve;
                
                $location.search("ts", new Date().getTime());
                // for redirections
                $location.replace();
            });

            // This doesn't work with $routeChangeStart: 
            // we need the following hack
            event.preventDefault();
            
            // This is an hack! 
            // We destroy route configuration, 
            // we fix it back when SomethingService.isLoaded
            route.resolve = rejectingResolve;
        } 
    });
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.