使用 Knockoutjs 创建网格

Create grid with knockoutjs

本文关键字:网格 创建 Knockoutjs 使用      更新时间:2023-09-26

假设我们有一个可观察的 N+1 个元素数组。我想实现的是使用这些元素的按钮构建 3 x X 网格。比如手机键盘或类似的东西。

到目前为止,我所做的是创建一个带有foreach和if的表:

<table>
    <tbody data-bind="foreach: tableList">
<!-- ko if: isEof($index())  -->
        <tr>
<!-- /ko -->
            <td><button data-bind="text: name"></button></td>
<!-- ko if: isEof($index())  -->
        </tr>
<!-- /ko -->
    </tbody>
</table>

isEof() 函数应该通过列表索引来确定我们已经渲染了 3 个元素。如果是,那么它将呈现标签。此外,如果索引为 0,则它也呈现元素。这是函数的代码:

function TableItem(data){
    this.id=ko.observable(data.id);
    this.name=ko.observable(data.name);
    this.isEof = function(index){
        if(index ==0){
            return true;
        }else{
            if((index+3) % 3 === 0){
                return true;
            }else{
                return false;
            }
        }
    }
}

但是我在此设置中面临两个问题。

1) 启用这些如果块,按钮名称绑定不起作用。当我删除 ko if 块时,它将正确呈现。

2) KO if 语句似乎无法正常工作。它将仅渲染那些行,其中也允许渲染。

我已经制作了我的解决方案的 JSFiddle 示例:http://jsfiddle.net/kpihus/3Lw7xjae/2/

我会创建一个ko.computed,将表项目列表转换为数组数组:

var TableItem = function TableItem(data) {
    this.id   = ko.observable(data.id);
    this.name = ko.observable(data.name);
};
var Vm = function Vm() {
    this.tableItems  = ko.observableArray([]);
    this.columnCount = ko.observable(3);
    this.columns = ko.computed(function() {
        var columns     = [],
            itemCount   = this.tableItems().length,
            begin       = 0;
        // we use begin + 1 to compare to length, because slice 
        // uses zero-based index parameters
        while (begin + 1 < itemCount) {
            columns.push( this.tableItems.slice(begin, begin + this.columnCount()) );
            begin += this.columnCount();
        }
        return columns;
    // don't forget to set `this` inside the computed to our Vm
    }, this);
};
vm = new Vm();
ko.applyBindings(vm);
for (var i = 1; i < 15; i++) {
    vm.tableItems.push(new TableItem({
        id: i,
        name: "name: " + i
    }));
}

这样,您可以通过嵌套两个foreach绑定来显示表:

<table>
    <tbody data-bind="foreach: { data: columns, as: 'column' }">
        <tr data-bind="foreach: column">
            <td>
                <button data-bind="text: name">A</button>
            </td>
        </tr>
    </tbody>
</table>

JSFiddle

如果你以前没有使用过ko.computed,他们会跟踪它们内部访问的任何可观察量——在本例中是this.tableItemsthis.columnCount——每当其中一个发生变化时,它们就会再次运行并产生新的结果。

this.columns采用我们的表项数组

[TableItem, TableItem, TableItem, TableItem, TableItem, TableItem]

并按this.columnCount将它们分组为

[[TableItem, TableItem, TableItem], [TableItem, TableItem, TableItem]]

我们可以通过简单地传递$root来实现这一点,这将具有tableList并以简单的方式进行循环。

查看模型:

function TableItem(data) {
    var self = this;
    self.id = ko.observable(data.id);
    self.name = ko.observable(data.name);
    self.pickarray = ko.observableArray();
    self.isEof = function (index, root) {
        if (index === 0) {
            self.pickarray(root.tableList().slice(index, index + 3));
            return true;
        } else if ((index + 3) % 3 === 0) {
            self.pickarray(root.tableList().slice(index, index + 3));
            return true;
        } else {
            return false;
        }
    }
}
var vm = function () {
    this.tableList = ko.observableArray();
    for (var i = 1; i < 15; i++) {
        this.tableList.push(new TableItem({
            id: i,
            name: "name: " + i
        }));
    }
}
ko.applyBindings(new vm());

视图:

<table data-bind="foreach:tableList">
    <tr data-bind="if:isEof($index(),$root)">
        <!-- ko foreach: pickarray -->
        <td>
            <button data-bind="text: id"></button>
        </td>
        <!-- /ko -->
    </tr>
</table>

这里的技巧非常简单,我们只需要有条件地填充pickarray就可以为我们完成工作。

在这里工作小提琴