AngularJS - нужна комбинация $ routeChangeStart и $ locationChangeStart

Моя проблема на самом деле очень похожа на найденную здесь:

AngularJs - отменить событие изменения маршрута

Короче говоря, я использую $routeChangeStart и пытаюсь изменить текущий маршрут, используя $location. Когда я это делаю, консоль показывает мне, что исходная страница все еще загружается и быстро перезаписывается новой страницей.

Предоставленное решение состояло в том, чтобы использовать $locationChangeStart вместо $routeChangeStart, что должно работать для предотвращения дополнительного перенаправления. К сожалению, я использую дополнительные данные в $routeprovider, к которым мне нужно получить доступ при изменении маршрута (я использую их для отслеживания ограничений страницы). Вот пример...

$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 для достижения того, что мне нужно?


person ThisLanham    schedule 31.07.2013    source источник


Ответы (4)


Один из подходов, который приходит на ум, заключается в попытке использовать для этого параметр разрешения:

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

Обратите внимание, что я не тестировал приведенный выше код, но делаю подобные вещи в своих проектах.

person Marius Soutier    schedule 31.07.2013
comment
Извините за задержку с ответом. Когда что-то работает для меня, я склонен волноваться и двигаться дальше. Это оказалось идеальным решением для того, что я делал. В функции загрузки я смог добавить такие вещи, как $http и $location для дальнейшей обработки моих запросов. - person ThisLanham; 05.11.2013
comment
Мне пришлось сделать так, чтобы моя функция загрузки больше походила на функцию от Ника stackoverflow.com/a/19482187/1449799 (отклонение не работало, как здесь написано) - person Michael; 07.07.2014
comment
Функция load должна выглядеть примерно так: var deferred = $q.defer(); if (access) { deferred.resolve(); } else { deferred.reject(); $location.url('/login'); } return deferred.promise; - person Clay; 27.01.2015
comment
@marius-soutier Большое спасибо за вариант resolve для when() - person andras.tim; 11.05.2015

Я сам столкнулся с той же ситуацией, и мое решение соответствовало тому, что намеревался сделать ОП.

Я использую событие $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"
person tamakisquare    schedule 06.12.2013
comment
что, если в URL-адресе есть параметр запроса... как определить, какой маршрутизатор вызывается? - person niran; 21.12.2013
comment
@niran - я обновил свой ответ для вас. Посмотри. - person tamakisquare; 22.12.2013
comment
Как это работает для маршрутов с параметрами? как /статья/:статьяId? - person mkhatib; 28.12.2013
comment
@mkhatib - Это было бы немного сложнее. Я заметил, что каждый объект в $route.routes имеет свойство с именем regexp. Как следует из названия, это объект регулярного выражения, который вы можете использовать для сопоставления с компонентом пути, который вы получите при анализе полного URL-адреса (упомянутого выше) при циклическом просмотре $route.routes. Я никогда не пробовал это, но я думаю, что это должно работать. - person tamakisquare; 29.12.2013
comment
Чтобы сопоставить произвольный путь, содержащий параметр, с параметризованным маршрутом RegExp, вам придется свериться с некоторым подмножеством объектов routes, если не со всеми. Например, вы могли бы сузить его до первого компонента пути, если бы он никогда не был параметром ни в одном маршруте. Тем не менее, повторение объектов для проверки на наличие нескольких регулярных выражений кажется неэффективным, особенно для процесса $locationChangeStart. - person nshew13; 16.07.2014

Начиная с версии 1.3.0 вы можете использовать недавно представленный метод preventDefault. При этом вы можете отменить текущее изменение маршрута, а затем применить собственное перенаправление, как показано в этом github-issue:


$rootScope.$on("$routeChangeStart", function (event, next, current) {
    if (next.access) {
      event.preventDefault();
      $rootScope.$evalAsync(function() {
        $location.path('/login');
      });
    }
});

Я реализовал этот метод в своем собственном проекте, и он отлично работает. Надеюсь, это поможет любому, кто наткнется на него.

person snrlx    schedule 23.12.2014

Хороший ответ Мариус, поставил меня на правильный путь. Я делаю что-то подобное для контроля доступа. Однако это работает...

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

При нажатии маршрута /orders обещание отклоняется (со всплывающим окном, может быть модальным диалогом), и маршрут не выполняется.

Надеюсь, это поможет кому-то.

person Nick    schedule 20.10.2013