Ember.js Application.inject 循环依赖
您好,我用 ember.js 构建我的应用程序大约需要 2 周时间,现在是时候将我的项目布局组合成最终形状了.为此,我开始研究使用 Ember 的注册/注入机制,而不是仅仅创建全局单例并将它们附加到我的 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");
}
});
可以预见的是,这会导致死循环.我希望依赖注入系统会尝试一些狡猾的杂耍,就像节点的 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 的容器默认创建单例,您可以急切地创建副本,然后允许 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
相关文章