Ember.js Application.inject循环依赖 [英] Ember.js Application.inject circular dependencies
问题描述
我用ember.js构建应用程序大约需要2周的时间,现在该将我的项目布局整理成最终形状了。为此,我开始研究使用Ember的register / inject机制,而不仅仅是创建全局单例并将它们附加到我的App对象(有关Ember中依赖项注入的详细说明,请参见此处)
Hi, I'm about 2 weeks into building my application with ember.js, and the time has come to pull together my project layout into its final shape. To that end, I started looking into using Ember's register / inject mechanism instead of just creating global singletons and attaching them to my App object (for an excellent description of dependency injection in Ember, see here)
我陷入了标准依赖项注入难题-循环引用。
I'm stuck with standard dependency injection dilemma - circular references.
比方说,我在整个应用程序中需要两个类似经理的类。我们称它们为AuthManager和DataManager。
Let's say, I have two manager-like classes that I need available throughout application. Let's call them AuthManager and DataManager.
App.AuthManager = Ember.Object.extend({
logIn: function (user) {
var promise = this.dataManager.post("/session/new", user);
//...
}
});
App.DataManager = Ember.Object.extend({
getJSON: function (url) {
if (!this.authManager.get("isLoggedIn")) {
return false;
}
//...
}
});
因此,如您所见,dataManager需要访问authManager,反之亦然。
我对解决方案的幼稚尝试是这样的:
So, as you see dataManager needs access to authManager and vice-versa. My naive take at a solution was something like this:
App.initializer({
name: "dataManager",
initialize: function (container, application) {
application.register("my:dataManager", application.DataManager);
application.inject("my:authManager", "dataManager", "my:dataManager");
}
});
App.initializer({
name: "authManager",
initialize: function (container, application) {
application.register("my:authManager", application.AuthManager);
application.inject("my:dataManager", "authManager", "my:authManager");
}
});
可以预料,这将导致死循环。我希望依赖项注入系统会尝试一些巧妙的方法,例如node的 require
会这样做,但是没有。
Predictably, this results in a dead loop. I was hoping dependency injection system would try some crafty juggle, like node's require
does, but no.
尝试过:
- 在
my:authManager
之后将第一个注入项移入第二个初始化器中 - 在前两个注入之后将第一个注入移动到其自己的初始化器中
- 将它们的任意组合放入
Ember.onLoad('Ember.Application',...)
来自链接的文章
- Moving the first inject into the second initializer, after
my:authManager
is registered. - Moving the first inject into its own initializer, after the first two
- Putting any combination of these into the
Ember.onLoad('Ember.Application', ...)
from the linked article
不幸的是,我尝试过的所有操作都以堆栈溢出(双关语:-)结尾。
Unfortunately, everything I tried ended in stack overflow (pun intended :-)).
我错过了什么吗?在这方面的文档很少。
当然,在官方注入之后,我总是可以手动查找实例,但是我希望有一些更优雅的解决方案。
Am I missing something? The documentation is pretty sparse in this area. Of course, I can always manually lookup instance after the 'official' injection, but I was hoping for some more elegant solution.
推荐答案
您肯定有一个循环依赖项,如果您使用其他语言,我会告诉您使用控制模式的反转,但是使用您的问题和容器会有些困难。
You definitely have a circular dependency, and if you were using a different language I'd tell you to use the inversion of control pattern, but it's a little difficult using your problem and the container.
如果可以将它们添加到诸如 manager
之类的命名空间下,这是一个解决方案(它紧密耦合,但是代码已经紧密耦合,几乎可以将它们组合在一起或彼此混合)。
If you're fine adding them under a namespace such as manager
or something like that then here's a solution (it's tightly coupled, but the code is tightly coupled already, almost enough that they could be together or a mixin on the other).
App.Manager = Ember.Object.extend({
init: function(){
// late fake injection
this.authManager.dataManager = this.dataManager;
this.dataManager.authManager = this.authManager;
}
});
App.initializer({
name: "manager",
after:['dataManager', 'authManager'],
initialize: function (container, application) {
application.register("my:manager", application.Manager);
application.inject("my:manager", "dataManager", "my:dataManager");
application.inject("my:manager", "authManager", "my:authManager");
application.inject("controller", "manager", "my:manager");
application.inject("route", "manager", "my:manager");
}
});
App.initializer({
name: "dataManager",
initialize: function (container, application) {
application.register("my:dataManager", application.DataManager);
}
});
App.initializer({
name: "authManager",
initialize: function (container, application) {
application.register("my:authManager", application.AuthManager);
}
});
和一个示例:
http://emberjs.jsbin.com/mopaquko/2/edit
另一方面,这会在每个路由/控制器上创建一个新实例。如果只需要一个实例。您可以这样操作,轻松得多,并且不需要名称空间。
On another note, this creates a new instance on each route/controller. If you only need one instance. You can do it like so, much easier and doesn't need the namespace.
App.initializer({
name: "joinManagers",
after:['dataManager', 'authManager'],
initialize: function (container, application) {
var dataManager = container.lookup('my:dataManager'),
authManager = container.lookup('my:authManager');
authManager.dataManager = dataManager;
dataManager.authManager = authManager;
application.register("my:jointDataManager", dataManager, {instantiate:false});
application.register("my:jointAuthManager", authManager, {instantiate:false});
application.inject("controller", "dataManager", "my:jointDataManager");
application.inject("controller", "authManager", "my:jointAuthManager");
application.inject("route", "dataManager", "my:jointDataManager");
application.inject("route", "authManager", "my:jointAuthManager");
}
});
App.initializer({
name: "dataManager",
initialize: function (container, application) {
application.register("my:dataManager", application.DataManager);
}
});
App.initializer({
name: "authManager",
initialize: function (container, application) {
application.register("my:authManager", application.AuthManager);
}
});
http://emberjs.jsbin.com/mopaquko/3/edit
,默认情况下是Ember容器创建的单例,您可以急切创建副本,然后让余烬仍然根据原始名称空间进行解析。
As was pointed out, Ember's container create's singletons by default, you can eagerly create the copies then allow ember to still resolve based on the original namespace.
App.initializer({
name: "joinManagers",
after:['dataManager', 'authManager'],
initialize: function (container, application) {
var dataManager = container.lookup('my:dataManager'),
authManager = container.lookup('my:authManager');
authManager.dataManager = dataManager;
dataManager.authManager = authManager;
application.inject("controller", "dataManager", "my:dataManager");
application.inject("controller", "authManager", "my:authManager");
application.inject("route", "dataManager", "my:dataManager");
application.inject("route", "authManager", "my:authManager");
}
});
App.initializer({
name: "dataManager",
initialize: function (container, application) {
application.register("my:dataManager", application.DataManager);
}
});
App.initializer({
name: "authManager",
initialize: function (container, application) {
application.register("my:authManager", application.AuthManager);
}
});
http://emberjs.jsbin.com/mopaquko/7/edit
这篇关于Ember.js Application.inject循环依赖的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!