BreezeJS与DurandalJS问题的集成与KnockoutJS的加载方式有关

时间:2014-01-08 21:01:16

标签: knockout.js breeze durandal q

Durandal.JS和Breeze.JS在一起玩有些麻烦。 Durandal基于几个库,其中两个是Require和Knockout。在使用Require引入的模块化模式之前,我的原始项目在Breeze模型上使用了Knockout样式绑定。

在我的旅途中,我发现Breeze可以使用Breeze模型的多个库,例如Backbone,Knockout,Angular和其他框架。当Breeze作为Require模块加载时,Breeze会检查是否存在Knockout模块,别名为“ko”。此模块名称与Durandal别名Knockout的方式相冲突,因为Durandal使用模块名称“knockout”代替。

当Breeze加载时,会进行检查以确定如何呈现Breeze模型的数据属性。在我的原始项目中,Breeze将检测全局范围内的Knockout并分配所有属性“ko.observable()”样式属性。

如何才能让这些模块正常播放?我尝试了几种Require.JS技巧,比如添加这个Shim:(from this post)

breeze: { deps: ['ko', 'jQuery', 'Q'] }

并添加这些虚拟模块定义:

define('ko', ['knockout'], function (ko) { return ko; });
define('Q', ['q'], function (Q) { return Q; });
define('jQuery', ['jquery'], function ($) { return $; });

在我的main.js.这个组合允许Breeze运行。我能够针对后端API成功执行查询。

然而,结果没有正确地变成Knockout observable。相反,Breeze似乎使用本机ES5可观察属性。虽然这实际上有点酷,但它完全打破了我现有的模块。

要注意,正如Druandal文档所建议的那样,我使用他们提供的代码片段来覆盖内部Promise库。

   system.defer = function (action) {
        var deferred = Q.defer();
        action.call(deferred, deferred);
        var promise = deferred.promise;
        deferred.promise = function () {
            return promise;
        };
        return deferred;
    };

这些相同的问题同时出现在Q库和jQuery库中,尽管上面的垫片和虚拟模块纠正了这种行为。我不知道下一步该尝试什么。

编辑:响应“显示您的上下文设置”评论:

define([
'breeze',
'q',
'durandal/system',
'lodash'
],
function (breeze, Q, system, _) {
    return new function () {
        var self = this;

        self.create = create;
        self.init = init;

        var EntityQuery = breeze.EntityQuery;
        var BREEZE_URL = '/breeze/AtlasApi/';
        var masterManager = new breeze.EntityManager(BREEZE_URL);
        self.masterManager = masterManager;

        function init() {

            return masterManager.fetchMetadata()
                    .fail(function (error) {
                        system.error(error);
                    });
        };
        function create() {
            var manager = masterManager.createEmptyCopy();

            return manager;
        };
    };
});

上面的模块我在AppStart加载一次并调用Init方法以确保我有元数据。然后我在其他地方调用.create()来创建一个空的,孤立的副本。这在非Require.js环境中非常有效。我使用promises来确保init步骤已经完成。我可以手动运行查询并且它们可以工作,减去Breeze实现实体的方式(再次,作为ES5属性,而不是淘汰属性)

1 个答案:

答案 0 :(得分:7)

看起来您正在尝试通过 requireJS 加载每个库。我记得,开箱即用的Durandal方法是直接加载第三方脚本(在require之外),并且仅对应用程序脚本使用require。

这简化了事情,但它不是唯一的方法,很多人都想使用require加载他们的所有脚本。

我们最近(v.1.4.7)更新了“ Todo-Require ”示例以演示该方法。我意识到它不是Durandal应用程序,但我希望你能找到你需要的方向。

我将在此复制我认为对您有所帮助的要点。

的index.html

...
<body>
    <div id="applicationHost"></div>

    <!-- Require + main. All scripts retrieved async by requireJS -->
    <script data-main="Scripts/app/main" src="Scripts/require.js"></script>
</body>
...

main.js

(function () {
    requirejs.config({
        paths: {
            'breeze': '../breeze.debug',
            'jquery': '../jquery-1.8.3.min',
            'ko':     '../knockout-2.2.0',
            'Q':      '../q'
        }
    });

    //  Launch the app
    //  Start by requiring the 3rd party libraries that Breeze should find
    define(['require', 'ko', 'jquery', 'logger', 'Q'], function (require, ko, $, logger) {
        logger.info('Breeze Todo is booting');

        // require the 'viewModel' shell 
        // require '../text' which is an html-loader require plugin; 
        //     see http://requirejs.org/docs/api.html#text
        require(['viewModel', '../text!view.html'],

        function (viewModel, viewHtml) {
            var $view = $(viewHtml);
            ko.applyBindings(viewModel, $view.get(0));
            $("#applicationHost").append($view);
        });
    });
})();

注意我们如何使用路径来定位库并获得Breeze预期的模块名称

另请注意,我们强制 requireJS 在加载Breeze本身之前加载这些依赖的第三方库。这非常重要。在Breeze开始寻找它们时,它们必须位于 requireJS IoC容器中;如果他们不在那里,Breeze认为他们永远不会在那里。

这就是为什么你看到Breeze将你的实体属性视为ES5属性。通话“上下文设置”中的define会同时加载“ko”和“breeze” 。这意味着当Breeze在自己的初始化阶段寻找时,无法保证会加载'ko'。

如果在Breeze找到它时没有加载'ko',Breeze假设你没有使用Knockout并且回退到它的本机模型库(“backingStore”)......它将实体构建为ES5属性。这恰好是Angular应用程序的正确选择。这不是KO应用程序的正确选择。

最后,如果Durandal期望一个模块的名称不同(我会接受你的话),使用 requireJS “map”配置来定义同义词,如下例所示:

requirejs.config({
    paths: {
        'breeze': '../breeze.debug',
        'jquery': '../jquery-1.8.3.min',
        'ko':     '../knockout-2.2.0',
        'Q':      '../q'
    },
    map: {
        '*': { 'knockout': 'ko' }
    }
});

现在,当Durandal请求'淘汰'时, requireJS 会将其映射到(已加载的)'ko'模块。

这种“地图”技术代替了同样有效的“虚拟模块”方法:

define('knockout', [ko], function (ko) { return ko; });

在查看示例代码时,您可能想知道此应用何时加载Breeze。答案:viewModel解决后。 viewModel有自己的依赖关系,包括dataservice,它本身依赖于Breeze。依赖注入难道不是很了不起? : - )

替代

您也可以以其他方式解决问题。

根据您的问题,您可以启动并运行Breeze和Durandal,但Breeze模型库似乎是为Breeze的本机“backingStore”配置的,后者将实体属性写为ES5 getter / setter属性。

您可以稍后在启动过程中更改该选择,可能在您首先与Breeze交互的dataservicedatacontext模块中创建EntityManager

在您进行第一次Breeze互动之前,请致电

breeze.config.initializeAdapterInstance("modelLibrary", "ko", true);

这将Knockout建立为Breeze在创建/实现实体时应使用的模型库。此后,将使用KO可观察属性创建实体。

加载到 requireJS IoC容器至关重要,并且在进行此配置更改之前可以作为'ko'访问<或者> Breeze会抛出异常。

不要希望Breeze等到 requireJS 异步加载'ko'。适配器初始化是一个同步过程。

Durandal 2.0

我被告知Durandal v2.0在某种程度上改变了我在Durandal v.1.x中熟悉的设置模式。我相信我的回答仍然存在密切关系。

我还不熟悉Durandal v.2。我很兴奋,因为它提供了使用ES5属性getter / setter而不是可观察性函数的可能性。我喜欢那一吨!

此特定功能的成本(您必须使用)是您必须在兼容ES5的浏览器中运行...这意味着您可以在仍然流行的IE8中运行。 ES5属性没有polyfill。

大多数......但不是全部......单页应用程序可以在此限制范围内运行。

不幸的是,根据Durandal的架构师,在目前的2.0版本中,ES5属性不适用于Breeze。两个图书馆争夺那些吸气者和二传手。所以你可以使用Durandal v2.0和Breeze ,但你现在必须使用可观察的函数属性。

我们希望通过v.2.1

改进这个故事

总之

希望这些想法和变化能让你走上成功的道路。