我正在编写一个自定义绑定,其中包括在运行某些显示逻辑之前需要知道它的高度。内容以foreach
绑定呈现。
通常情况下,KO会在渲染任何这些项目之前调用我的绑定init
,这意味着高度不正确。
然后我使用{ controlsDescendantBindings: true }
并在使用高度之前手动绑定子项;这在init
。
问题是,我希望绑定触发update
并在项目更新时重新计算高度,如果我更改You cannot apply bindings multiple times to the same element
,上面的模式会给我init
到update
,这是可以理解的。
这是减少代码:
HTML:
<a href="#" data-bind="click: rerun">Re-run</a>
<hr/>
<div class="container" data-bind="test: true">
<!-- ko foreach: Items -->
<div class="item" data-bind="text: ID"></div>
<!-- /ko -->
</div>
<hr/>
<b id="res"></b>
CSS:
hr {
clear: both;
}
.item {
width: 50px;
height: 50px;
background: red;
margin: 5px;
display: inline-block;
}
CODE:
ko.bindingHandlers.test = {
init:
function(el, f_valueaccessor, allbindings, viewmodel, bindingcontext)
{
var val = ko.unwrap(f_valueaccessor());
if(!!val)
{
ko.applyBindingsToDescendants(bindingcontext, el);
$('#res').html($(el).height());
return { controlsDescendantBindings: true };
}
}
};
function Model(data, mapping)
{
var _this = this;
var _mapping = $.extend({}, {}, mapping);
this.Items = ko.observableArray();
if(data)
ko.mapping.fromJS(data, _mapping, _this);
this.rerun = function(model, e)
{
_this.Items(_this.genItems());
}
this.genItems = function()
{
var a = [];
for(var i = 0; i < 10 + (Math.random() * 20); i++)
a.push({ID:i});
return a;
}
this.rerun();
}
var model = new Model();
ko.applyBindings(model);
小提琴:https://jsfiddle.net/whelkaholism/tzrxbojj/
我可以看到两种方法来解决这个问题:
将对象传递给包含函数引用的绑定,当项目被更改为强制重新计算时,模型可以调用该函数引用(这很好但很笨重)
在afterRender
上使用foreach
(这也可以,但会为模型添加代码)
afterRender
解决方案运行正常,可能会成为推荐的方法;但它确实意味着我必须将此添加到任何需要类似功能的模型,并且自定义绑定看起来像一个非常好的抽象。
使用容器上的单个自定义绑定是否有一种好的(即优于#1)方式让它工作?
答案 0 :(得分:1)
您的解决方案中缺少的主要内容是自定义绑定需要了解项目模型,以便订阅其更改并重新计算元素的高度。我也使高度成为可观察的解决方案,使其更像是Knockout的用途。
我的解决方案也不需要controlsDescendantBindings: true
。
HTML:
<a href="#" data-bind="click: rerun">Re-run</a>
<hr/>
<div class="container" data-bind="test: {heightValue: containerHeight, itemsModel: Items}">
<!-- ko foreach: Items -->
<div class="item" data-bind="text: ID"></div>
<!-- /ko -->
</div>
<hr/>
<b id="res" data-bind="text: containerHeight"></b>
结合:
ko.bindingHandlers.test = {
init:
function(el, f_valueaccessor, allbindings, viewmodel, bindingcontext)
{
var data = f_valueaccessor();
var recalculate = function () {
setTimeout(function () {
data.heightValue($(el).height());
}, 0);
}
data.itemsModel.subscribe(function () {
recalculate();
});
recalculate();
}
};
setTimeout为零有助于在渲染完成后触发高度重新计算。
除了为容器高度添加了observable之外,视图模型大致相同:
this.containerHeight = ko.observable();