AngularJs - hủy sự kiện thay đổi tuyến đường


88

Làm cách nào để hủy sự kiện thay đổi tuyến đường trong AngularJs?

Mã hiện tại của tôi là

$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  

}
});

với điều này ngay cả khi xác thực không thành công Angular sẽ kéo mẫu tiếp theo và dữ liệu liên quan và sau đó ngay lập tức chuyển về chế độ xem / tuyến đường trước đó. Tôi không muốn có góc để kéo mẫu & dữ liệu tiếp theo nếu xác thực không thành công, lý tưởng là không có window.history.back (). Tôi thậm chí đã thử event.preventDefault () nhưng không sử dụng được.

Câu trả lời:


184

Thay vì $routeChangeStartsử dụng$locationChangeStart

Đây là cuộc thảo luận về nó từ các anh chàng anglejs: https://github.com/angular/angular.js/issues/2109

Chỉnh sửa 3/6/2018 Bạn có thể tìm thấy nó trong tài liệu: https://docs.angularjs.org/api/ng/service/$location#event-$locationChangeStart

Thí dụ:

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

8
Vấn đề với điều này là không có cách nào để truy cập vào bộ sưu tập thông số tuyến đường. Nếu bạn đang cố gắng xác nhận các thông số tuyến đường, giải pháp này không tốt.
KingOfHypocrites

1
Hãy lưu ý, khi sử dụng $routeChangeStartcác nextbiến chỉ là một chuỗi và nó không thể chứa bất kỳ dữ liệu (ví dụ, bạn không thể truy cập đến trước đó được xác định authorizedRolesbiến)
MyTitle

@KingOfHypocrites, bạn không thể nhận thông số tuyến đường, nhưng bạn có thể lấy $location.path()$location.search()
Design by Adrian

2
Bạn có thể / có nên làm điều này trên rootScope không nếu bạn muốn theo dõi tất cả các thay đổi của tuyến đường? Hoặc là có một thay thế ngon miệng hơn?
tối đa

38

Một mẫu mã hoàn chỉnh hơn, sử dụng $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); 
      });
    }
  );

Tôi không hoàn toàn hài lòng với việc kết nối điều này trong bộ điều khiển gốc của mình (bộ điều khiển cấp cao nhất). Nếu có một mô hình tốt hơn, tôi rất muốn biết. Tôi mới làm quen với góc cạnh :-)


Điều này rất hiệu quả đối với tôi, mặc dù tôi không cố gắng hủy thay đổi tuyến đường của mình như áp phích ban đầu. Cảm ơn!
Jim Clouse

4
Vâng, vấn đề với rootScope là bạn phải nhớ hủy liên kết trình xử lý đó khi bộ điều khiển của bạn biến mất.
lostintranslation,

12

Một giải pháp là phát sự kiện 'notAuthorized' và nắm bắt nó trong phạm vi chính để thay đổi lại vị trí. Tôi nghĩ đó không phải là giải pháp tốt nhất, nhưng nó đã hiệu quả với tôi:

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

và trong Bộ điều khiển chính của tôi:

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

Lưu ý: có một số thảo luận về vấn đề này trên trang web góc cạnh, chưa được giải quyết: https://github.com/angular/angular.js/pull/4192

BIÊN TẬP:

Để trả lời nhận xét, đây là thông tin thêm về các hoạt động của LoginService. Nó chứa 3 chức năng:

  1. login () (tên bị nhầm lẫn) thực hiện một yêu cầu tới máy chủ để lấy thông tin về người dùng đã đăng nhập (trước đó). Có một trang đăng nhập khác chỉ điền trạng thái người dùng hiện tại trong máy chủ (sử dụng SpringSecurity framework). Các Dịch vụ Web của tôi không thực sự không có trạng thái, nhưng tôi muốn để khuôn khổ nổi tiếng đó xử lý bảo mật của tôi.
  2. isAuthenticated () chỉ cần tìm kiếm nếu Phiên khách chứa đầy dữ liệu, có nghĩa là nó đã được xác thực trước đó (*)
  3. quyền truy cập được xử lý isAuthorized () (ngoài phạm vi của chủ đề này).

(*) Phiên của tôi được điền khi tuyến đường thay đổi. Tôi đã ghi đè phương thức when () để điền phiên khi trống.

Đây là mã:

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

Bạn có thể vui lòng nói những gì trả lại LoginService.isAuthenticated()khi tải trang đầu tiên? Bạn lưu trữ currentUsernhư thế nào? Điều gì sẽ xảy ra nếu người dùng làm mới trang (người dùng cần nhập lại thông tin đăng nhập)?
MyTitle

Tôi đã thêm thông tin khác về LoginService trong câu trả lời ban đầu của mình. Người dùng hiện tại được cung cấp bởi máy chủ và thay đổi tuyến xử lý bất kỳ quá trình làm mới trang nào, người dùng không cần phải đăng nhập lại.
Asterius

4

Đối với bất kỳ ai tình cờ gặp đây là một câu hỏi cũ, (ít nhất là trong góc 1.4), bạn có thể làm điều này:

 .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
Tôi tự hỏi, bạn có bị tính thêm phí khi sử dụng mắc cài hay dấu ";"?
cel sharp

6
@MatthiasJansen tất nhiên. Và trên hết, dấu ngoặc nhọn tính gấp đôi và dấu chấm phẩy tính gấp ba.
Ákos Vandra

1

Đây là giải pháp của tôi và nó phù hợp với tôi nhưng tôi không biết liệu mình có đang đi đúng hướng hay không vì tôi chưa quen với công nghệ 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
Làm thế nào bạn có thể trả lời một câu hỏi nếu bạn không chắc chắn về câu trả lời của mình?
deW1

Tôi chắc chắn rằng nó hoạt động. Tôi không chắc liệu đây có phải là cách thích hợp hay không. Nếu bạn có một giải pháp tốt hơn, tôi muốn xem nó.
Alexandrakis alexandros

1

tôi thấy cái này có liên quan

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

bài đăng của tôi có thể giúp một số người trong tương lai.


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
Mặc dù đoạn mã này có thể giải quyết câu hỏi, bao gồm một lời giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho người đọc trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn.
dpr

0

Trong trường hợp bạn cần ngăn tuyến đường thay đổi trong trường $routeChangeStarthợp này (tức là bạn muốn thực hiện một số thao tác dựa trên tuyến đường tiếp theo ), hãy tiêm $route$routeChangeStartgọi nội bộ:

$route.reload()

1
Tôi đã hy vọng, nhưng trong Angular 1.2.7 trên Chrome, điều này dường như gây ra vòng lặp JS và trang bị đóng băng.
Nick Spacek

1
@NickSpacek Điều kiện mà bạn gọi $route.reload()cần phải khác nếu không nó đang chạy lại cùng một mã. Điều này tương đương với việc tạo một whilevòng lặp với trueas điều kiện.
Kevin Beal
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.