Javascript 使用knockoutjs创建网格

Javascript 使用knockoutjs创建网格,javascript,knockout.js,Javascript,Knockout.js,假设我们有一个N+1元素的可观测数组。我想要实现的是用这些元素的按钮构建3×X网格。比如电话键盘之类的 到目前为止,我所做的是创建一个带有foreach的表,如果在其中: <table> <tbody data-bind="foreach: tableList"> <!-- ko if: isEof($index()) --> <tr> <!-- /ko --> <td>&l

假设我们有一个N+1元素的可观测数组。我想要实现的是用这些元素的按钮构建3×X网格。比如电话键盘之类的

到目前为止,我所做的是创建一个带有foreach的表,如果在其中:

<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>
但是我在这个设置中面临两个问题

1) 启用这些if块后,按钮名称绑定将不起作用。当我移除ko if块时,它将正确渲染

2) ko if语句似乎不能正确工作。它将仅渲染这些线,其中也允许渲染


我已经制作了我的解决方案的JSFIDLE示例:

我将创建一个
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
    }));
}

如果您以前没有使用过,它们会跟踪在它们内部访问的任何观察对象—在本例中是,
this.tableItems
this.columnCount
—并且每当其中一个发生更改时,它们会再次运行并生成新结果

this.columns
获取我们的表项数组

[TableItem, TableItem, TableItem, TableItem, TableItem, TableItem]
并按
this.columnCount
将其分组

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

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

查看模型:

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>
函数表项(数据){
var self=这个;
self.id=ko.可观察(data.id);
self.name=ko.observable(data.name);
self.pickarray=ko.observableArray();
self.isEof=函数(索引,根){
如果(索引==0){
self.pickarray(root.tableList().slice(index,index+3));
返回true;
}否则如果((索引+3)%3==0){
self.pickarray(root.tableList().slice(index,index+3));
返回true;
}否则{
返回false;
}
}
}
var vm=函数(){
this.tableList=ko.observearray();
对于(变量i=1;i<15;i++){
this.tableList.push(新建TableItem)({
id:我,
名称:“名称:”+i
}));
}
}
应用绑定(新vm());
查看:

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
,它为我们完成任务


拉小提琴好的,我已经注意到我的问题了。例如,当我有第三个元素时,我将为同一个元素渲染和,所以其他单元格不在和之间。虽然我还不确定如何解决这个问题。
kpilhus
你可以查看现在发布的答案。干杯
janfoh
我发布了一个答案,但在这里面我可以避免从视图中传递
root
?有没有办法在
isEof
函数中获取父数组?尝试了contextFor&dataFor,因为我在那里没有定义。我喜欢让我的视野更清晰。Thanks@supercool每当你发现自己在范围上玩得太多的时候,很可能你问了错误的对象和错误的问题-一个人本身不知道它是否是表项列表中的第三项。与其像您在这里所做的那样,通过授予它对范围的访问权来解决这个问题,为什么不询问已经有权访问列表并能够找到它的父对象(这里的视图模型)?至于
undefined
,您是否尝试了
dataFor
ko.dataFor
?我尝试了,但是
vm.tableList
给了我未定义的。其中as
ko.dataFor()
也给出了未定义的值。我不太明白为什么dataFor不起作用?它是否仅限于单击我们获取此的位置以作为参数传递。你可以试试我的小提琴看看这个问题。再次感谢
ko。dataFor
需要一个HTML元素作为其参数。您从哪里调用了
ko.dataFor
呢?在
self.isEof
内部,我试图访问它,因为我需要父数组列表。现在,我通过根获取函数的参数。