我正在创建一个自定义绑定来创建HTML元素。我的VM看起来像:
function VM() {
this.data = ko.observableArray(['one', 'two']);
}
HTML:
<div data-bind="myBind: data"></div>
<script id="my-template" type="text/html">
<div data-bind="text: $data"></div>
</script
我希望能够为 data 数组中的每个项创建一个新的DOM元素。问题是DOM元素需要与模板进行数据绑定。类似的东西:
ko.bindingHandlers.myBind = {
update: function(element, valueAccessor) {
ko.unwrap(valueAccessor()).forEach(function (x) {
$(element).append("<div data-bind=\"template: { name: 'my-template', data:" + x + " }\"></div>")
}
}
}
所需的HTML输出:
<div data-bind="myBind: data">
<div data-bind="text: $data">one</div>
<div data-bind="text: $data">two</div>
</div>
<script id="my-template" type="text/html">
<div data-bind="text: $data"></div>
</script
我理解标准的foreach绑定很容易实现,但是在尝试显示数千个条目时,绑定存在问题。表演很糟糕,并且花了很长时间才能做到这一点。
此外,我发现有效的解决方案是:
ko.bindingHandlers.myBind = {
update: function(element, valueAccessor) {
var html = "";
ko.unwrap(valueAccessor()).forEach(function (x) {
html += '<div>' + x + "</div>";
}
element.innerHTML = html;
}
}
这个解决方案的问题在于,实际上我的模板要复杂得多,在js中编写十几行html似乎是不好的做法,难以维护,难以阅读。
我进行了搜索和搜索,我发现的所有解决方案只让我80%。这是一个相对简单的解决方案,对吗?
答案 0 :(得分:2)
如果我们要帮助您编写内部使用模板的自定义绑定,我们基本上会以foreach
绑定结束。淘汰的foreach绑定唯一的事情是包装template
绑定:
ko.bindingHandlers['foreach'] = {
/* ... */
'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
return ko.bindingHandlers['template']['init']( /* ... */ );
},
'update': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
return ko.bindingHandlers['template']['update']( /* ... */ );
}
};
From the knockout source,为简洁起见,部分内容已经注释
如果您想优化性能,您可能需要找到另一种方法。我在使用visible
绑定而不是将元素移入和移出DOM时取得了一些成功,但它确实取决于您的具体情况。
以下是您可以尝试使用此方法的示例。它的速度是否更快取决于模板和浏览器的复杂程度。我的轶事结果可能会指导您研究自己的特定用例,如下所示。 不要认为这些是理所当然的,它只是一台机器上的一个例子!。
const filterValue = ko.observable("odd");
const pred = ko.pureComputed(() =>
filterValue() === "odd"
? item => item.nr % 2 === 1
: item => item.nr % 2 === 0
);
const items = Array.from(
{ length: 10000 },
(_, i) => ({ title: `Item ${i}`, nr: i })
);
const App = function() {
const vm = this;
vm.filterValue = filterValue;
vm.items = items;
vm.filteredItems = ko.pureComputed(() => items.filter(pred()));
vm.isVisible = item => pred()(item);
vm.visibleMode = ko.observable(false);
}
ko.applyBindings(new App());
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<label>
<input type="radio" value="odd" data-bind="checked: filterValue"> odd
</label>
<label>
<input type="radio" value="even" data-bind="checked: filterValue"> even
</label>
<label>
<input type="checkbox" data-bind="checked: visibleMode"> use visible instead of foreach
</label>
<div>
<!-- ko if: visibleMode -->
<ul data-bind="foreach: items">
<li data-bind="text: title, visible: $parent.isVisible($data)"></li>
</ul>
<!-- /ko -->
<!-- ko ifnot: visibleMode -->
<ul data-bind="foreach: filteredItems">
<li data-bind="text: title"></li>
</ul>
<!-- /ko -->
</div>
&#13;