knockout.js - 通过复选框初始化 knockout foreach 片状,与模板一起使用

标签 knockout.js

已经发布了很多关于在 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);
};

Updated fiddle

关于knockout.js - 通过复选框初始化 knockout foreach 片状,与模板一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12967163/

相关文章:

javascript - $.post(...) 无法识别 JSON 主体

knockout.js - 使用knockout.js动态生成下拉列表并绑定(bind)选项

javascript - Knockout js从元素内容初始化可观察值

javascript - 在 knockout 计算访问器上订阅 arrayChange

javascript - 使用 Knockout : Visible changes not saved 控制网页

javascript - 继承和覆盖 Knockout 计算的可观察值

javascript - 当我们确定对象已创建时如何获取对象的属性

javascript - 使用 javascript knockout 绑定(bind) View

Knockout.js v2.3.0 错误 "You cannot apply bindings multiple times to the same element"

javascript - 在 knockout 和 JavaScript 中使用货币