AngularJS - нужна комбинация $routeChangeStart и $locationChangeStart
Моя проблема на самом деле очень похожа на найденную здесь:
AngularJs - отменить событие изменения маршрута
Короче говоря, я использую $routeChangeStart и пытаюсь изменить текущий маршрут, используя $location. Когда я это сделаю, консоль показывает мне, что исходная страница по-прежнему загружается и быстро перезаписывается новой страницей.
Предоставленное решение состояло в том, чтобы использовать $locationChangeStart вместо $routeChangeStart, который должен работать для предотвращения дополнительной перенаправления. К сожалению, я использую дополнительные данные в $rubeprovider, к которому мне нужно получить доступ, изменяя маршрут (я использую его для отслеживания ограничений страницы). Вот пример...
$routeProvider.
when('/login', { controller: 'LoginCtrl', templateUrl: '/app/partial/login.html', access: false}).
when('/home', { controller: 'HomeCtrl', templateUrl: '/app/partial/home.html', access: true}).
otherwise({ redirectTo: '/login' });
$rootScope.$on('$routeChangeStart', function(event, next, current) {
if(next.access){
//Do Stuff
}
else{
$location.path("/login");
//This will load the current route first (ie: '/home'), and then
//redirect the user to the correct 'login' route.
}
});
С $routeChangeStart я могу использовать "следующий" и "текущий" параметры (см. AngularJS - $route) в качестве объектов для извлечения мои значения доступа. С $locationChangeStart эти два параметра возвращают строки url, а не объекты. Таким образом, похоже, что нет способа получить мои значения доступа.
Можно ли каким-либо образом объединить мощность переадресации $locationChangeStart с гибкостью объекта $routeChangeStart для достижения того, что мне нужно?
Ответы
Ответ 1
Один из подходов, который приходит на ум, пытается использовать параметр разрешения для этого:
var resolver = function(access) {
return {
load: function($q) {
if (access) { // fire $routeChangeSuccess
var deferred = $q.defer();
deferred.resolve();
return deferred.promise;
} else { // fire $routeChangeError
return $q.reject("/login");
}
}
}
}
$routeProvider.
when('/login', { controller: 'LoginCtrl', templateUrl: '/app/partial/login.html', resolve: resolver(false)}).
when('/home', { controller: 'HomeCtrl', templateUrl: '/app/partial/home.html', resolve: resolver(true)}).
otherwise({ redirectTo: '/login' });
Обратите внимание, что я не тестировал код выше, но я делаю подобное в своих проектах.
Ответ 2
Я столкнулся с одной и той же ситуацией, и мое решение было согласовано с тем, что предполагал OP.
Я использую событие $locationChangeStart
и службу $route
. Получив доступ к $route.routes
, я получаю все объекты маршрута, определенные с помощью $routeProvider
.
.run(function($rootScope, $route, $location) {
$rootScope.$on('$locationChangeStart', function(ev, next, current) {
// We need the path component of `next`. We can either process `next` and
// spit out its path component, or simply use $location.path(). I go with
// the latter.
var nextPath = $location.path();
var nextRoute = $route.routes[nextPath]
console.log(nextRoute.access); // There you go!
});
})
Чтобы проанализировать компонент пути из абсолютного URL-адреса:
var urlParsingNode = document.createElement('a');
urlParsingNode.href = next; // say, next = 'http://www.abc.com/foo?name=joe
console.log(urlParsingNode.pathname) // returns "/foo"
Ответ 3
Начиная с версии 1.3.0, вы действительно можете использовать новый метод preventDefault. С этим вы можете отменить текущее изменение маршрута, а затем применить свой собственный перенаправление, как показано в этой github-проблеме:
$rootScope.$on("$routeChangeStart", function (event, next, current) {
if (next.access) {
event.preventDefault();
$rootScope.$evalAsync(function() {
$location.path('/login');
});
}
});
Я реализовал этот метод в своем собственном проекте, и он отлично работает. Надеюсь, это поможет кому-то еще, кто наткнулся на него.
Ответ 4
Хороший ответ Мариус, поставь меня на правильный путь. Я делаю что-то вроде этого для контроля доступа. Это работает, однако...
var resolver = function(route, routeEvent) {
return {
load: function($q) {
deferred = $q.defer();
if (routeEvent!=3) { // eventually will be a list of routeEvents that the logged-in user is not allowed to visit read from a db table configured by admin
deferred = $q.defer();
deferred.resolve();
return deferred.promise;
} else { // fire $routeChangeError
alert("You don't have permissions to access this.");
deferred.reject(route);
return deferred.promise;
}
}
}
}
var jsonRoutes = [
{'route' : '/logout', 'templateUrl': 'partials/login.html', 'controller' : 'LoginCtrl', 'routeEvent' : 1 },
{'route' : '/orders', 'templateUrl': 'partials/orders.html', 'controller': 'OrderListCtrl', 'routeEvent' : 2 },
{'route' : '/products', 'templateUrl': 'partials/products.html', 'controller': 'ProductListCtrl', 'routeEvent' : 3 },
...
];
// somewhere on successful login code add in the dynamic routes
angular.forEach(jsonRoutes, function(r) {
$route.routes[r.route] = {templateUrl: r.templateUrl, controller: r.controller, routeEvent: r.routeEvent, resolve: resolver(r.route, r.routeEvent)};
});
// got some static routes too which don't have access control - user has to login right?
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/error', {templateUrl: 'partials/error.html', controller : ErrorCtrl, routeEvent : 1 }).
when('/login', {templateUrl: 'partials/login.html', controller : LoginCtrl, routeEvent : 1 }).
when('/home', {templateUrl: 'partials/home.html', controller : HomeCtrl, routeEvent : 1 }).
when('/logout', {templateUrl: 'partials/login.html', controller : LoginCtrl, routeEvent : 1 }).
otherwise( {redirectTo: '/error'} );
При щелчке по маршруту/порядку обещание отклоняется (с всплывающим окном может быть модальное диалоговое окно), и маршрут не выполняется.
Надеюсь, это поможет кому-то.