使用Knockout.js foreach创建组

时间:2013-07-11 10:38:24

标签: mvvm knockout.js

我有一个html section元素,它有一个Knockout foreach绑定到我的viewmodel上的一组项目。这可以很好地将集合中的每个项目渲染到垂直向下的div上。我现在希望项目根据窗口大小按行分组,因此项目在桌面浏览器上显示为4行,但在移动设备上每行只显示1行。

我实际上是通过在viewmodel中创建组并让我的view元素与foregroup属性绑定来实现的。这种方法的问题在于我的viewmodel现在具有我认为的一堆视图逻辑并直接引用window对象。哪个不对,我不认为。

我已经有一个单独的js文件,其中包含视图特定的逻辑,即“slideVisible”之类的自定义Knockout绑定。如何将分组逻辑从我的viewmodel移到此文件中?我猜我如果在viewmodel中没有进行分组,我将无法使用Knockout的foreach绑定?

1 个答案:

答案 0 :(得分:4)

如果你需要在KO中动态地执行此操作,那么这里是一个绑定普通foreach绑定的绑定示例,并动态创建一个计算结果,该计算结果返回一个带有行/列的结构,基于“算“可观察。

ko.bindingHandlers.foreachGroups = {
    init: function(element, valueAccessor) {
         var groupedItems,
             options = valueAccessor();

        //create our own computed that transforms the flat array into rows/columns
        groupedItems = ko.computed({
            read: function() {
                var index, length, group,
                    result = [],
                    count = +ko.utils.unwrapObservable(options.count) || 1,
                    items = ko.utils.unwrapObservable(options.data);

                //create an array of arrays (rows/columns)
                for (index = 0, length = items.length; index < length; index++) {
                    if (index % count === 0) {
                       group = [];
                       result.push(group);
                    }

                    group.push(items[index]);
                }

                return result;
            },
            disposeWhenNodeIsRemoved: element
        });  

        //use the normal foreach binding with our new computed
        ko.applyBindingsToNode(element, { foreach: groupedItems });

        //make sure that the children of this element are not bound
        return { controlsDescendantBindings: true };
    }
};

您可以使用它:

<div data-bind="foreachGroups: { data: items, count: count }">
    <ul data-bind="foreach: $data">
        <li data-bind="text: $data"></li>
    </ul>
</div>

以下是一个示例:http://jsfiddle.net/rniemeyer/F48XU/

对于你的具体情况,我可能会:

  • 删除count选项,然后传入商品
  • count函数中创建自己的init observable。
  • 添加一个运行逻辑的resize事件处理程序,并相应地更新count observable。

它可能看起来像(填写你特定的调整大小逻辑):

ko.bindingHandlers.foreachGroups = {
    init: function(element, valueAccessor) {
         var groupedItems,
             data = valueAccessor(),
             count = ko.observable(1);

        ko.utils.registerEventHandler(window, "resize", function() {
           //run your calculation logic here and update the "count" observable with a new value
        });

        //create our own computed that transforms the flat array into rows/columns
        groupedItems = ko.computed({
            read: function() {
                var index, length, group,
                    result = [],
                    itemsPerRow = +ko.utils.unwrapObservable(count) || 1,
                    items = ko.utils.unwrapObservable(data);

                //create an array of arrays (rows/columns)
                for (index = 0, length = items.length; index < length; index++) {
                    if (index % itemsPerRow === 0) {
                       group = [];
                       result.push(group);
                    }

                    group.push(items[index]);
                }

                return result;
            },
            disposeWhenNodeIsRemoved: element
        });  

        //use the normal foreach binding with our new computed
        ko.applyBindingsToNode(element, { foreach: groupedItems });

        //make sure that the children of this element are not bound
        return { controlsDescendantBindings: true };
    }
};