承诺角度ui-router中的依赖关系解析顺序

时间:2014-02-24 09:20:02

标签: javascript angularjs dependency-injection promise angular-ui-router

我已经设置了一个顶级控制器,只有在成功解析了一个promise(由Config工厂返回)时才会实例化。该承诺基本上下载了Web应用程序配置,使用RESTful端点等等。

$stateProvider
  .state('app', {
    url: '/',
    templateUrl: 'views/_index.html',
    controller: 'MainCtrl',
    resolve: {
      config: 'Config'
    }
  });

这个设置允许我在任何下级控制器有机会使用之前断言配置已正确加载。

现在我需要在更深层次的嵌套控制器中注入使用factory的另一个Config,并且仅在解析后才能工作(看起来像需要一些$resource包装器Web服务URL)。如果我这样做:

$stateProvider
  .state('app.bottom.page', {
    url: '/bottom/page',
    templateUrl: 'views/_a_view.html',
    controller: 'BottomLevelCtrl',
    resolve: {
      TheResource: 'MyConfigDependingResource'
    }
  });

看起来resolve评估顺序不是从上到下跟随控制器层次结构,而是从下到上,因此:

  1. app.bottom.page已输入
  2. ui-router尝试解析MyConfigDependingResource,但注入失败, 因为Config从未初始化
  3. ui-router分辨率由于错误而停止(甚至没有抛出Error,但这是另一个问题),并且Config永远不会被顶级控制器初始化
  4. 为什么ui-router以相反的顺序解析依赖关系?如何在最高级别TheResource已解决MainCtrl之后轻松解析我的Config对象(当然,不依赖$inject)?< / p>

    更新:从this plnkr's log您可以看到,只有在嵌套控制器启动了自己的解析过程后,才会尝试使用顶级resolve

4 个答案:

答案 0 :(得分:60)

与@Kasper Lewau的答案类似,可以指定对具有单个状态的解析的依赖性。如果其中一个解析依赖于同一解析块中的一个或多个解析属性。在我的情况下,checkS依赖于另外两个结果

.state('stateofstate', {
    url: "/anrapasd",
    templateUrl: "views/anrapasd.html",
    controller: 'SteofsteCtrl',
    resolve: {
        currU: function(gamMag) {
            return gamMag.checkWifi("jabadabadu")
        },
        userC: function(gamUser, $stateParams) {
            return gamUser.getBipi("oink")
        },
        checkS: ['currU', 'userC', 'gamMag', function(currU, userC, gamMag) {
            return gamMag.check(currU, userC);
        }]
    }
})

** PS:**检查&#34;解决&#34; following document的一部分,了解resolve的内部工作原理的详细信息。

答案 1 :(得分:15)

解析并行运行的对象,因此不遵循某种层次结构。 但是,通过将更高阶的解析对象定义为对辅助解析的依赖关系,可以实现嵌套解析。

$stateProvider
  .state('topState', {
    url: '/',
    resolve: {
      mainResolveObj: ['$someService', function ($someService) {
        return 'I am needed elsewhere!';
      }]
    }
  })
  .state('topState.someOtherState', {
    url: '/some-other-place',
    resolve: {
      someOtherResolveObj: ['mainResolveObj', function (mainResolveObj) {
        console.log(mainResolveObj); //-> 'I am needed elsewhere!'. 
      }]
    }
  });

有点不好的例子,但我认为你得到了它的要点。将更高级别的解析对象的名称定义为对较低级别解析的依赖关系,它将等待它首先解析。

这是我们解决在低阶解析对象之前预加载某些数据以及身份验证要求(以及其他方面)的方法。

祝你好运。

答案 2 :(得分:1)

我同意你的意见,结果应该链接,我在这个领域遇到了很多问题。

但是,他们没有,所以我提出的解决方案是使用我自己的承诺存储在您在数据完成时解决的服务中。我尽力编辑你的plunkr工作但无济于事。我不再遇到你的错误,所以希望你可以这样做:http://plnkr.co/edit/Yi65ciQPu7lxjkFMBKLn?p=preview

它正在做什么:

将配置存储在新承诺对象旁边的状态

myapp.service('state', function($q) {
  this.load = $q.defer();
  this.config = {}
})

在您第一次解决时,将您的配置存储在此服务中,并在准备好后解决我们在上面创建的新承诺。

myapp.factory('Config', function($http, $log, state) {
  return $http.get('example.json')
    .then(function (data) {
      angular.extend(state.config, data.data);
      state.load.resolve();
    });
});

最后也是最重要的一步是在我们的上述承诺得到解决之前,不要将我们的第二个内容称为子解析函数:

myapp.factory('MyConfigDependingResource', function($log, state) {
  return state.load.promise.then(function() {
    if (!state.config.text) {
      $log.error('Config is uninitialized!');
    }
    else {
      // Do wonderful things
    }
    return
  });
});

您需要注意的主要事情是配置现在存储在一个状态中。应该有办法解决这个问题,但这就是我以前做过的事情。

答案 3 :(得分:1)

虽然它们无法链接,但您可以互相呼叫:

var resolve2 = ['$stateParams',function($stateParams){
  // do resolve 2 stuff
  ...
  return true;
}];

var resolve1 = ['$stateParams',function($stateParams){
  // do resolve 1 stuff
  ...
  // now do resolve2 stuff
  return $injector.invoke(resolve2,this,{$stateParams:$stateParams});
}];


$stateProvider.state("myState", {
  resolve: {
    resolve1: resolve1
  }
});