Knockout.js 当我的Knockout/RequireJS小部件在同样使用Knockout的站点中使用时,它如何绑定到正确的上下文? 脚本
我正在使用RequireJS和KnockoutJS构建一个小部件。小部件在实例化时调用Knockout.js 当我的Knockout/RequireJS小部件在同样使用Knockout的站点中使用时,它如何绑定到正确的上下文? 脚本,knockout.js,requirejs,Knockout.js,Requirejs,我正在使用RequireJS和KnockoutJS构建一个小部件。小部件在实例化时调用ko.applyBindings(widgetViewModel,thisWidget)。这个小部件应该能够在一个站点中使用,而不管站点本身是否使用了Knockout 问题 当我将我的小部件放入使用Knockout的站点时,如果该站点在小部件设置后调用ko.applyBindings(siteViewModel),则会将错误的视图模型应用于该小部件。小部件将获取siteViewModel,而不是所需的widge
ko.applyBindings(widgetViewModel,thisWidget)
。这个小部件应该能够在一个站点中使用,而不管站点本身是否使用了Knockout
问题
当我将我的小部件放入使用Knockout的站点时,如果该站点在小部件设置后调用ko.applyBindings(siteViewModel)
,则会将错误的视图模型应用于该小部件。小部件将获取siteViewModel
,而不是所需的widgetViewModel
到目前为止我都试过了
ko.applyBindings(siteViewModel)
的调用,使其在小部件设置之前发生。这可以工作,但并不理想,因为它限制了小部件的调用方式widgetViewModel
应用于我的小部件,并返回{ControlsDescentBindings:true};
不幸的是,站点使用的淘汰实例与我的小部件使用的实例不同(因为需要)因此,我需要访问站点的实例,可能需要查看全局名称空间我建议以导出应用程序方法的方式编写小部件,或者将其作为ViewModel属性包含 小部件的HTML如下所示:
<h1 data-bind="text: title">Default</h1>
<div id="ko" data-bind="with: widgetInstance">
<h1 data-bind="text: title">Default</h1>
</div>
<h2 data-bind="text: thing"></h2>
require(["mywidget", "jquery"], function (widget, $) {
widget($('.widgetStyle')[0]);
});
require(["mywidget", "jquery", "knockout"], function (widget, $, ko) {
var viewModel = { title: "My App" };
widget($('.widgetStyle')[0]);
ko.applyBindings(viewModel);
});
淘汰用户可以将其包含在他们的ViewModel中,并使用带有绑定的为其提供上下文
var ViewModel = function(){
var self = this;
self.widgetInstance = new MyWidgets.widget("In KO");
self.thing = "Other Thing";
}
ko.applyBindings(new ViewModel);
他们的HTML将如下所示:
<h1 data-bind="text: title">Default</h1>
<div id="ko" data-bind="with: widgetInstance">
<h1 data-bind="text: title">Default</h1>
</div>
<h2 data-bind="text: thing"></h2>
require(["mywidget", "jquery"], function (widget, $) {
widget($('.widgetStyle')[0]);
});
require(["mywidget", "jquery", "knockout"], function (widget, $, ko) {
var viewModel = { title: "My App" };
widget($('.widgetStyle')[0]);
ko.applyBindings(viewModel);
});
违约
请注意,它们仍然可以创建其他属性,这些属性与小部件上的任何属性都不冲突
(旁白:有人,请想出一个比“应用”
更好的名字,然后编辑我的帖子)为你的小部件编写一个自定义绑定怎么样,如下所示:
define(["knockout", "jquery"], function (ko, $) {
var widgetViewModel = {
title: "My Widget Titel"
};
var widgetWrapperHtml = "<div data-bind=\"mywidget: true\"></div>";
var widgetHtml = $("<div data-bind=\"text: $data.title\"</div>");
ko.virtualElements.allowedBindings.mywidget = true;
ko.bindingHandlers.mywidget = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
// Create the widget context:
var widgetBindingContext = widgetViewModel;
// Or uncomment this line to created a child context:
// var widgetBindingContext = bindingContext.createChildContext(widgetBindingContext);
ko.virtualElements.emptyNode(element);
ko.virtualElements.prepend(element, widgetHtml[0]);
ko.applyBindingsToDescendants(innerContext, element);
}
};
var applyWidgetToElement = function (element) {
$(element).html(widgetWrapperHtml);
ko.applyBindings({}, element);
};
return applyWidgetToElement;
})
但也像这样:
<h1 data-bind="text: title">Default</h1>
<div id="ko" data-bind="with: widgetInstance">
<h1 data-bind="text: title">Default</h1>
</div>
<h2 data-bind="text: thing"></h2>
require(["mywidget", "jquery"], function (widget, $) {
widget($('.widgetStyle')[0]);
});
require(["mywidget", "jquery", "knockout"], function (widget, $, ko) {
var viewModel = { title: "My App" };
widget($('.widgetStyle')[0]);
ko.applyBindings(viewModel);
});
我想您已经接近第二个解决方案了。您可以做的是使用一个自定义绑定来停止站点的绑定,如本页提供的示例所示 但是正如你所说,如果他们已经在网站上使用了knockout,那么你需要在该版本的knockout上使用这个绑定,所以你应该像这样用if(ko)来包装它
if(ko){
ko.bindingHandlers.allowBindings = {
init: function(elem, valueAccessor) {
// Let bindings proceed as normal *only if* my value is false
var shouldAllowBindings = ko.unwrap(valueAccessor());
return { controlsDescendantBindings: !shouldAllowBindings };
}
};
}
<body>
<div>
<span data-bind="text: SomeSiteLevelBinding"></span>
</div>
<div data-bind="allowBindings: false">
<div id="someElementId">
<span data-bind="text:SomeComponentLevelBinding"></span>
</div>
</div>
</body>
然后,因为您使用的是requirejs,所以您可以将您的击倒版本映射到ko以外的其他版本,如ko2或其他任何版本
define(["knockout", "jquery"], function (ko2, $) {
var myViewModel = {
SomeComponentLevelBinding: ko2.observable()
};
ko2.applyBindings(myViewModel, document.getElementById('someElementId'));
}
那么你的html可能看起来像这样
if(ko){
ko.bindingHandlers.allowBindings = {
init: function(elem, valueAccessor) {
// Let bindings proceed as normal *only if* my value is false
var shouldAllowBindings = ko.unwrap(valueAccessor());
return { controlsDescendantBindings: !shouldAllowBindings };
}
};
}
<body>
<div>
<span data-bind="text: SomeSiteLevelBinding"></span>
</div>
<div data-bind="allowBindings: false">
<div id="someElementId">
<span data-bind="text:SomeComponentLevelBinding"></span>
</div>
</div>
</body>
因为站点已经使用了knockout,并且我们将allowBindings绑定添加到该实例中,所以我们阻止站点版本的knockout控制该div中的任何内容。然后,因为我们确实使用组件版本的knockout将绑定仅应用于组件中的div,所以在s上应该有两个版本的knockout一起玩得很好
如果他们在页面上没有敲除,这也没关系,因为如果他们没有,我们就不会添加allowBindings绑定,而且因为我们只对其中的div应用绑定,在这种情况下,带有
allowBindings:false
的数据绑定属性被忽略。这是一个挑战。我现在的想法是将您的小部件位于一个容器中,该容器具有如我所述的绑定。然后,当您的小部件脚本加载时,将自定义绑定添加到您的KO实例中,查看window.KO是否存在,并将其添加到该实例中。唯一剩下的问题是,如果您通过require.js使用多个KO版本,或者当KO.applyBi时是否存在计时问题ndings
被调用,当你的模块被加载时。如果我们添加一个停止绑定
,比如绑定到KO-core,那会很好。是的,这就是我所采取的方法,但我并不100%满意必须预测外部容器的KO实例的位置。是的,目前我想不出一个更好的解决方案缺点。例如,您可以使用不使用数据绑定
的,但这可能会弄乱现有的KO实例。它可以同时支持这两个属性,因此,如果您的页面和小部件使用相同的KO实例,您仍然可以。仍然不太好,只是另一种想法。