4

设想

我正在使用 RequireJS 和 KnockoutJS 构建一个小部件。ko.applyBindings(widgetViewModel, thisWidget)小部件在实例化时调用。无论站点是否使用 Knockout 本身,该小部件都应该能够在站点中使用。

问题

当我将小部件放入使用 Knockout 的站点时,如果该站点ko.applyBindings(siteViewModel)在设置小部件后调用,则会将错误的视图模型应用于小部件。小部件得到siteViewModel而不是期望的widgetViewModel.

到目前为止我尝试过的

  1. 重新排序调用以ko.applyBindings(siteViewModel)在小部件设置之前发生。 这可行但并不理想,因为它限制了我的小部件的调用方式。

  2. 为我的小部件添加一个自定义绑定,该绑定应用正确的绑定上下文(即widgetViewModel到我的小部件并返回{controlsDescendantBindings: true}; 不幸的是,该站点使用的 Knockout 实例与我的小部件使用的实例不同(因为需要)所以我需要访问站点的实例,可能通过查看全局命名空间。

4

3 回答 3

1

我认为您对第二个解决方案很接近。您可以做的是有一个自定义绑定,它只是停止站点的绑定,就像本页http://knockoutjs.com/documentation/custom-bindings-controlling-descendant-bindings.html上提供的示例一样

但是正如你所说,如果他们已经在网站上使用了淘汰赛,你需要这个绑定在那个版本的淘汰赛上,所以你应该把它包装在这样的 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 };
        }
    };
}

然后因为您使用的是 requirejs,您可以将您的淘汰赛版本映射到 ko 以外的其他内容,例如 ko2 或其他。

define(["knockout", "jquery"], function (ko2, $) {

    var myViewModel = {
        SomeComponentLevelBinding: ko2.observable()
    };

    ko2.applyBindings(myViewModel, document.getElementById('someElementId'));
}

那么你的html可能看起来像这样......

<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 内的任何内容。然后,因为我们确实使用组件的敲除版本从我们的组件将绑定应用到其中的 div,所以我们应该在同一页面上有 2 个敲除版本一起玩得很好。

如果他们在页面上没有敲除,这也应该没问题,因为如果他们没有,我们不会添加 allowBindings 绑定,并且因为我们只将绑定应用到其中的 div,所以allowBindings:false在那种情况下会忽略 data-bind 属性。

于 2015-05-21T08:38:52.007 回答
0

如何为您的小部件编写自定义绑定,如下所示:

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

然后你可以像这样使用你的小部件:

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);
});
于 2014-02-17T16:34:20.373 回答
0

我建议以导出应用程序方法的方式编写小部件,或者可以将其作为 ViewModel 属性包含在内。

演示

exports.widget = function(title){
    var self = this;

    self.title = ko.observable(title);

    self.apply = function(element){
        ko.applyBindings(self, element);
    };

    return self;
};

您的小部件的 HTML 如下所示:

<h1 data-bind="text: title">Default</h1>

现在,我们需要为两个用户提供一种方法,将其包含在他们的文档中,而不会干扰他们的工作流程。

非 KO 用户可以简单地将其绑定到h1元素的容器(假设您的模块称为 MyWidgets)。

MyWidgets.widget("Not In KO").apply(document.getElementById('parentOfWidget'));

Knockout 用户可以将其包含在他们的 ViewModel 中,并使用with绑定为其提供上下文。

var ViewModel = function(){
    var self = this;
    self.widgetInstance = new MyWidgets.widget("In KO");
    self.thing = "Other Thing";
}

ko.applyBindings(new ViewModel);

他们的 HTML 将如下所示:

<div id="ko" data-bind="with: widgetInstance">
     <h1 data-bind="text: title">Default</h1>
</div>
<h2 data-bind="text: thing"></h2>

请注意,他们仍然可以创建其他属性,这些属性不会与您的小部件上的任何属性冲突。

(ps某人,请想一个比我的帖子更好的名字apply并编辑我的帖子)

于 2013-07-08T02:08:42.140 回答