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



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会在全局范围内检测Knockout,并为所有属性分配"ko.obsobservable()"样式的属性。

我如何才能让这些模块正确地配合使用?我尝试了几个Require.JS技巧,比如添加这个Shim:(来自本文)

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成功执行查询。

然而,结果并没有被正确地转化为可观察到的淘汰赛。相反,Breeze似乎使用了本机ES5可观察属性。虽然这实际上有点酷,但它完全打破了我现有的模块。

需要注意的是,正如Druandal文档所建议的那样,我正在使用他们提供的代码片段用Q覆盖内部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环境中运行得非常好。我使用promise来确保init步骤已经完成。我可以手动运行查询,它们可以工作,减去Breeze具体化实体的方式(同样,作为ES5属性,而不是敲除属性)

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

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

我们最近(1.4.7版)更新了">Todo Require"示例,以演示该方法。我意识到这不是一个杜兰达尔应用程序,但我希望你能找到你需要的方向。

我将把我认为对你有帮助的要点抄在这里。

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的预期建立模块名称

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

这就是为什么您看到Breeze将您的实体属性视为ES5属性。调用"上下文设置"中的define同时加载"ko"one_answers"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属性。

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

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

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

这将Knockout建立为Breeze在创建/实体化实体时应该使用的模型库。从此以后,实体将被创建为具有KO可观察属性。

至关重要的是,在您进行此配置更改之前,将淘汰加载requireJSIoC容器中,并以'ko'的形式访问,否则Breeze将抛出异常。

不要期望Breeze等待requireJS异步加载'ko'。适配器初始化是一个同步过程。'在Breeze寻找ko之前,它一定已经被装载了。

杜兰达尔2.0

据我所知,Durandal v2.0在某种程度上改变了我在Durandal v.1.x中熟悉的设置模式。我相信我的答案仍然是密切相关的。

我还不太熟悉杜兰达尔第二版。我对此感到兴奋,部分原因是它提供了使用ES5属性getters/ssetters而不是可观察性函数的可能性。我非常喜欢!

这个特定功能的成本是必须在兼容ES5的浏览器中运行。。。这意味着你不能在仍然流行的IE8中运行。ES5特性没有polyfill。

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

不幸的是,根据Durandal的架构师的说法,在当前的2.0版本中,ES5属性不适用于Breeze。这两个库为那些getter和setter而斗争。因此,您可以将Durandal v2.0与Breeze一起使用,但现在必须保留可观察的函数属性。

我们预计这个故事将在v.2.1 之前得到改进

合计

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

最新更新