javascript angular ui-router 中的承诺依赖解析顺序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21983452/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Promise dependency resolution order in angular ui-router
提问by frapontillo
I have set up a top-level controller that is instantiated only when a promise (returned by a Config
factory) is successfully resolved. That promise basically downloads the Web app configuration, with RESTful endpoints and so on.
我已经设置了一个顶级控制器,只有在Config
成功解决承诺(由工厂返回)时才会实例化。该承诺基本上下载 Web 应用程序配置,以及 RESTful 端点等。
$stateProvider
.state('app', {
url: '/',
templateUrl: 'views/_index.html',
controller: 'MainCtrl',
resolve: {
config: 'Config'
}
});
This setup allows me to kind-of assert that the configuration is properly loaded before any lower controller gets a chance to use it.
这个设置允许我在任何较低的控制器有机会使用它之前断言配置已正确加载。
Now I need to inject, in a deeper nested controller, another factory
that uses Config
and only works when it is resolved (look at it like a $resource
wrapper that needs some Web service URLs). If I do:
现在我需要在更深的嵌套控制器中注入另一个factory
使用Config
并且仅在解析时才起作用的控制器(将其$resource
视为需要一些 Web 服务 URL的包装器)。如果我做:
$stateProvider
.state('app.bottom.page', {
url: '/bottom/page',
templateUrl: 'views/_a_view.html',
controller: 'BottomLevelCtrl',
resolve: {
TheResource: 'MyConfigDependingResource'
}
});
it looks like the resolve
evaluation order does not follow the controller hierarchy from top to bottom, but from bottom to top, therefore:
看起来resolve
评估顺序不是从上到下遵循控制器层次结构,而是从下到上,因此:
app.bottom.page
is enteredui-router
attempts to resolveMyConfigDependingResource
, but the injection fails, becauseConfig
has never been initialized- The
ui-router
resolution stops because of an error (without even throwingError
s, but that's another issue), andConfig
is never initialized by the top level controller
app.bottom.page
被输入ui-router
尝试解析MyConfigDependingResource
,但注入失败,因为Config
从未被初始化- 该
ui-router
决议停止,因为一个错误的(甚至没有扔Error
S,但这是另一个问题),并且Config
永远不会被顶层控制器初始化
Why is ui-router
resolving dependencies in a reverse order? How can I easily resolve my TheResource
object afterthe top level MainCtrl
has resolved Config
(without relying on $inject
, of course)?
为什么ui-router
以相反的顺序解析依赖项?我怎么能轻易解决我的TheResource
对象后的顶层MainCtrl
已经解决了Config
(不依靠$inject
,当然)?
UPDATE: from this plnkr's logyou can see that the top level resolve
is attempted only after the nested controller has started its own resolving process.
更新:从这个 plnkr 的日志中,您可以看到resolve
只有在嵌套控制器开始自己的解析过程后才尝试顶级。
回答by Nikolay Melnikov
Similarly to @Kasper Lewau's answer, one may specify a dependency on resolves withing a single state. If one of your resolves depends on one or more resolve properties from the same resolve block. In my case checkS
relies on two other resolves
与@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: **Check the "Resolves" section of the following documentfor more details about the inner-workings of resolve
.
**PS:**检查以下文档的“解决”部分,了解有关resolve
.
回答by Kasper Lewau
Resolve objects run in parallel, and as such doesn't follow a certain hierarchy. However, nested resolves are possible by defining a higher order resolve object as a dependency to your secondary resolves.
Resolve 对象并行运行,因此不遵循特定的层次结构。但是,通过将高阶解析对象定义为辅助解析的依赖项,可以实现嵌套解析。
$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!'.
}]
}
});
Kind of a bad example, but I take it you get the gist of it. Define the name of a higher level resolve object as a dependency to your lower level resolve and it'll wait for it to resolve first.
一个不好的例子,但我认为你明白了它的要点。将更高级别的解析对象的名称定义为对较低级别的解析的依赖,它会先等待它解析。
This is how we've solved preloading certain data before lower order resolve objects, aswell as authentication requirements (among other things).
这就是我们如何在低阶解析对象以及身份验证要求(除其他外)之前解决预加载某些数据的方法。
Good luck.
祝你好运。
回答by Josh
I agree with you that the resolves should chain, and I've hit many problems around this area.
我同意你的观点,解决方案应该是连锁的,我在这方面遇到了很多问题。
However, they don't, so the solution I have come up with is to use my own promise stored in a service which you resolve when the data is complete. I have tried my best to edit your plunkr to work but to no avail. I am no longer hitting your errors though, so hopefully you can work from this: http://plnkr.co/edit/Yi65ciQPu7lxjkFMBKLn?p=preview
但是,他们没有,所以我想出的解决方案是使用我自己存储在服务中的承诺,您在数据完成时解析该服务。我已尽力编辑您的 plunkr 以使其正常工作,但无济于事。不过,我不再遇到您的错误,所以希望您可以从这里开始工作:http: //plnkr.co/edit/Yi65ciQPu7lxjkFMBKLn?p=preview
What it's doing:
它在做什么:
Store the config in a state alongside a new promise object
将配置存储在一个新的承诺对象旁边的状态中
myapp.service('state', function($q) {
this.load = $q.defer();
this.config = {}
})
On your first resolve, store your config in this service and once ready resolve the new promise we created above.
在您第一次解决时,将您的配置存储在此服务中,一旦准备好解决我们上面创建的新承诺。
myapp.factory('Config', function($http, $log, state) {
return $http.get('example.json')
.then(function (data) {
angular.extend(state.config, data.data);
state.load.resolve();
});
});
The final and most important step is to not call our second the content of the childs resolve function until after our promise above is resolved:
最后也是最重要的一步是在上面的 promise 被解决之前,不要调用我们的第二个孩子的 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
});
});
The main thing you will need to be aware of is that the config is now stored in a state. There should be ways around this but this is how I've done it before.
您需要注意的主要事情是配置现在存储在状态中。应该有办法解决这个问题,但我以前就是这样做的。
回答by Remento
While they cannot be chained, you can call one from another:
虽然它们不能被链接,但您可以从另一个调用它们:
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
}
});