如何从$ stateChangeStart访问UI-Router根状态的'resolve'属性?

时间:2013-10-02 16:17:27

标签: angularjs angular-ui-router

我正在尝试在我的应用中实施用户身份验证和访问检查系统,但是我一直在遇到障碍。我想这次我有正确的方法,但我有最后一个障碍。

一点背景:我尝试将所有代码放入$ rootScope.on($ startChangeStart)并且它工作得非常糟糕......但它确实有效。路线始终被重定向,但由于后端的验证检查,它显示第一个请求页面1/2秒,然后每次都显示重定向页面。因此,我尝试通过在$ startChangeStart函数的开头调用evt.preventDefault()来“暂停”页面加载,这有效,但是尝试将用户放回到原始路由之后导致路由器中的无限循环。

因此,经过更多的研究和阅读大量的帖子后,我确信'resolve:'是进行身份验证以确保页面在发生时不加载的正确位置,然后在需要时重定向用户来自$ startChangeStart。 ($ state和event总是在我尝试将它们注入解析函数时未定义)这似乎是获胜的组合。

我的问题:我对我的应用中的root状态有了决心:'main'

这是为了避免代码冗余,但我无法确定如何从$ stateChangeStart函数访问根状态的属性,从而确定解析结果。 toState是子状态,而fromState是先前的状态或具有'^'路由的抽象状态......

我是否必须将解决方案放在每个子状态才能使用,或者是否有办法从此处访问根状态?

基本应用设置:

angular.module('App', ['ui.router', 'ui.bootstrap', 'ui.event', 'AngularGM', 'ngResource'])
.config(['$urlRouterProvider', '$stateProvider', function($urlRouterProvider, $stateProvider){
    $urlRouterProvider
        .when('/home', '/')
        .when('', '/')
        .when('/sign-up/joe', '/sign-up')
        .otherwise('/');

    $stateProvider
        .state('main', {
            url: '',
            abstract: true,
            templateUrl: 'views/main.html',
            controller: 'MainCtrl',
            resolve: {
                checkAccess: ['accountService', function(accountService) {
                    accountService.checkAuth(function(){
                        accountService.checkAccess(function (access){
                            return access;
                        });
                    });
                }]
            }
        })
        .state('main.home', {
            url: '',
            abstract: true,
            templateUrl: 'views/home.html',
            controller: 'HomeCtrl'
        })
        .state('main.home.index', {
            url: '/',
            templateUrl: 'views/home/index.html'
        });


.run(['$rootScope', '$state', '$stateParams', 'accountService', function ($rootScope, $state, $stateParams) {
    $rootScope.$state = $state;
    $rootScope.$stateParams = $stateParams;
    $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
        console.dir(toState);
        console.dir(toParams);
        console.dir(fromState);
        console.dir(fromParams);
        if (!toState.checkAccess.allowed) {
            event.preventDefault();
            $state.transitionTo(toState.checkAccess.newState);
        }
    });
}]);

这是对两个状态对象的console.dir()调用的输出:

Object
 name: "main.home.index"
 templateUrl: "views/home/index.html"
 url: "/"
 __proto__: Object

Object
 controller: "PlacesCtrl"
 name: "main.places.search"
 templateUrl: "views/places.html"
 url: "/places"
 __proto__: Object

更新

哎呀,忘了提一下AngularJS版本是v1.2.0-rc.2

$ state.current console.dir()

Object
 abstract: true
 name: ""
 url: "^"
 views: null
 __proto__: Object

5 个答案:

答案 0 :(得分:8)

是的,我相信您可以从$stateChangeStart功能访问root状态。

使用纯AngularJS时,我通常使用current.$$route

例如,使用以下路线

.when('/home', {
  title:'Home',
  bodyClass: 'meetings',
  controler: 'HomeCtrl'
})

我可以像这样访问root状态

  $rootScope.$on('$routeChangeSuccess', function (event, current, previous) {

   if (current.$$route) {

     $rootScope.title      = current.$$route.title;

     $rootScope.bodyClass  = current.$$route.bodyClass;
   }

 });

使用ui-router它只是有点不同,因为它被称为$state.current。并且您可以访问与您命中的任何路径相关联的所有属性(例如:$ state.current.url)

所以在你的代码上你可以有这样的东西

  .run(['$rootScope', '$state', '$stateParams', function ($rootScope, $state, $stateParams) {

      $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
          console.log($state.current.url);
      });
  }]);

答案 1 :(得分:1)

您无需使用resolve。看看我的解决方案:

app.run ($rootScope, $state, Auth, ngDialog) ->
  $rootScope.$on '$stateChangeStart', (e, to) ->
    if to.authRequired and not Auth.isAuthenticated()
      e.preventDefault()
      Auth.currentUser().then(
        (user) ->
          $state.go(to)
        (failure) ->
          $rootScope.storedState = to
          $state.go('main')
          ngDialog.closeAll()
          ngDialog.open
            template: 'modals/login.html'
            controller: 'loginCtrl'
            className: 'ngdialog-theme-default'
      )

我使用angular_devisengDialog但它们是可选的,您可以使用自己的用户服务来实现它。

答案 2 :(得分:0)

是否可以在accountService内进行重定向?如果检测到用户未通过checkAuth或checkAccess功能,则可以阻止回调执行并将用户重定向到错误(或登录)页面。

如果您想将某人重定向到登录页面以刷新其授权/身份验证然后返回到先前的状态,则需要考虑的其他事项是实现某种状态的变量/队列。

答案 3 :(得分:0)

如果您使用默认的空对象初始化您的状态,则您可以在$stateChangeStart内操作它。

$stateProvider
  .state 'home',
    url: "/"
    resolve: {}

...

  $rootScope.$on '$stateChangeStart', (e, toState, toParams, fromState, fromParams) ->
    toState.resolve.x = ->
      $timeout ->
        alert "done"
      , 3000

请参阅https://github.com/angular-ui/ui-router/issues/1165

答案 4 :(得分:0)

这个答案很晚,但它很有用。

解决状态和$stateChangeStart事件同时执行。当您尝试访问$stateChangeStart中已解析的数据时,它将无法使用,但在$stateChangeSuccess事件触发时它将可用。

如果您使用$stateChangeStart,那么您需要从两个地点checkAuth事件和主要解决方案$stateChangeStart进行操作。由于它们具有并行执行功能,因此至少会向服务器发送2个网络请求以获取相同的数据。

而是使用$stateChangeSuccess。使用此选项将确保您的结算已解决,然后您可以检查访问权限。此外,不是访问已解析的属性,而是使用角度服务访问已解析的数据。