已经发布了很多关于在 Knockout 的 foreach 循环中使用复选框的问题,但我还没有看到为什么初始化选中的绑定(bind)在某些情况下有效而在其他情况下无效的答案。
使用这个简单的 View 模型:
viewModel = function (obj) {
this.AllPossibleThings = ko.observableArray(
[
{ ID: "1", Value: 'Thing1' },
{ ID: "2", Value: 'Thing2' },
{ ID: "3", Value: 'Thing3' },
{ ID: "4", Value: 'Thing4' }
]);
this.selectedThings = ko.observableArray(["2","3"]);
};
$(ko.applyBindings(new viewModel()));
将它们放入 foreach 模板中可以按照您的预期工作。它显示 4 个复选框,其中 2 个和 3 个已预先选中:
<table>
<tbody data-bind="template: { name: 'ThingTmpl', foreach: AllPossibleThings }">
</tbody>
</table>
<script id="ThingTmpl" type="text/html">
<tr>
<td><input type="checkbox"
data-bind="attr: {value: ID}, checked: $root.selectedThings" /></td>
<td><span data-bind="text: Value"></span></td>
</tr>
</script>
在没有模板的情况下做应该等效的事情是不一样的:
<table>
<tbody data-bind="foreach: AllPossibleThings">
<tr>
<td><input type="checkbox"
data-bind="checked: $root.selectedThings,
attr: {value: ID}" /></td>
<td><span data-bind="text: Value"></span></td>
</tr>
</tbody>
</table>
非模板显示 4 个复选框,但没有预先选中。但是,当您单击第一个复选框时,第 2 个和 3 个复选框就会被选中。就好像它在创建“selectedThings”数组之前就被绑定(bind)了,并且从未观察到更改。
用 fiddle 进行演示:http://jsfiddle.net/jturnage/j763w/
有人知道为什么模板 foreach 在这里可以工作,但常规的 foreach 绑定(bind)却不能吗?
最佳答案
问题在于您的绑定(bind)和浏览器的顺序。
据我了解the order in which you iterate over the property names of an object is not well-defined并且不能被依赖。它可能会也可能不会按照您声明它们的顺序进行迭代。 Knockout 在内部将此绑定(bind)转换为对象并迭代该对象的属性。在本例中,它恰好按照声明的顺序排列。
模板中复选框的绑定(bind)如下:
data-bind="attr: {value: ID}, checked: $root.selectedThings"
虽然模板中没有绑定(bind),但如下所示:
data-bind="checked: $root.selectedThings, attr: {value: ID}"
绑定(bind)不起作用的原因是 attr
绑定(bind)尚未应用。因此,复选框的值没有设置为相应的 ID
。当 checked
绑定(bind)更新时,它会在 selectedThings
中搜索不存在的值。如果您更改绑定(bind)的顺序,那么您会发现它会起作用,因此首先应用 attr
。
处理这个 IMO 的更好、更安全的方法是测试自己是否应该检查它,而不是依赖于这种不稳定的行为。添加一个函数来测试是否选择了特定项目。那么这些绑定(bind)的触发顺序就不再重要了。
viewModel = function (obj) {
// ...
this.isSelected = function (thing) {
return this.selectedThings.indexOf(thing.ID) !== -1;
};
};
<td><input type="checkbox"
data-bind="checked: $root.isSelected($data), attr: {value: ID}" />
</td>
当然,如果您想保持 selectedThings
同步,则必须进行调整。您可以通过巧妙地使用计算的可观察量来实现这一点。
this.isSelected = function (thing) {
return ko.computed({
read: function () {
return this.selectedThings.indexOf(thing.ID) !== -1;
},
write: function (newValue) {
var index = this.selectedThings.indexOf(thing.ID);
if (newValue) {
// checked
if (index === -1)
this.selectedThings.push(thing.ID);
} else {
// unchecked
if (index !== -1)
this.selectedThings.remove(thing.ID);
}
}
}, this);
};
关于knockout.js - 通过复选框初始化 knockout foreach 片状,与模板一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12967163/