javascript - Ember.js Application.inject 循环依赖

标签 javascript ember.js dependency-injection frontend single-page-application

嗨,我使用 ember.js 构建应用程序大约需要 2 周时间,现在是时候将我的项目布局整理成最终形状了。为此,我开始考虑使用 Ember 的注册/注入(inject)机制,而不仅仅是创建全局单例并将它们附加到我的 App 对象(有关 Ember 中依赖注入(inject)的精彩描述,请参阅 here )

我陷入了标准依赖注入(inject)困境 - 循环引用。

比方说,我有两个类似管理器的类,我需要在整个应用程序中使用它们。我们将它们称为 AuthManager 和 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,反之亦然。 我天真的解决方案是这样的:

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");
    }
});

不出所料,这会导致死循环。我希望依赖注入(inject)系统会尝试一些巧妙的技巧,就像节点的 require 所做的那样,但没有。

我已经尝试过:

  • 注册 my:authManager 后,将第一个注入(inject)移至第二个初始值设定项。
  • 将第一个注入(inject)移到其自己的初始化程序中,位于前两个注入(inject)之后
  • 将这些内容的任意组合放入链接文章中的 Ember.onLoad('Ember.Application', ...)

不幸的是,我尝试的一切都以堆栈溢出结束(双关语:-))。

我错过了什么吗?该领域的文档非常稀疏。 当然,我总是可以在“官方”注入(inject)后手动查找实例,但我希望有一些更优雅的解决方案。

最佳答案

你肯定有循环依赖,如果你使用不同的语言,我会告诉你使用控制反转模式,但是使用你的问题和容器有点困难。

解决方案1

如果您可以将它们添加到诸如 manager 或类似名称的命名空间下,那么这里有一个解决方案(它是紧密耦合的,但代码已经紧密耦合,几乎足以使它们可以一起或另一个混合)。

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

解决方案2

另一方面,这会在每个路由/ Controller 上创建一个新实例。如果您只需要一个实例。您可以这样做,更容易,并且不需要命名空间。

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

解决方案3

正如所指出的,Ember 的容器默认创建单例,您可以急切地创建副本,然后允许 ember 仍然基于原始命名空间进行解析。

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

关于javascript - Ember.js Application.inject 循环依赖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22736022/

相关文章:

javascript - javascript 中的这种编程风格怎么样?

javascript - 如何获取对象元素内的文本?

javascript - 在 ember-cli 中对数组模型进行排序

java - @Autowired 尽管@configuration创建空对象

authentication - TestBenchTestCase 中的依赖注入(inject)

javascript - 如何获取从匹配条件返回的 jQuery 元素的数量?

javascript - SignalR 异常记录?

javascript - Handlebars 助手返回 [object HTMLSpanElement]

Ember.js 绑定(bind)数组元素来查看

.net - 命名空间 Microsoft.Extensions 中不存在缓存