在foreach knockout绑定中使用前一项

时间:2014-05-22 02:21:00

标签: knockout.js

我有以下问题:我有一个可观察的数组(从服务器填充),如下所示:

this.elems = ko.observableArray([
    {n: 1, t : 'hello'},
    {n: 1, t : 'why'},
    {n: 1, t : 'are'},
    {n: 2, t : 'some'},
    {n: 2, t : 'ducks'},
    {n: 3, t : 'here'},
    {n: 8, t : '?'}
]);

我希望以下列方式展示它:

      1
Hello
why
are
      2
some
ducks
      3
here
      8
?

如您所见,仅当前一个数字与当前数字不同时才显示数字。 使用foreach绑定(这是我的jsFiddle)非常容易。

<div data-bind="foreach: elems">
    <div class="t1">
        <span data-bind="text: n" class="c1"></span>
        <span data-bind="text: t" class="c2"></span>
    </div>
</div>

只有我能够访问先前的元素。 我知道我可以修改我的observalbe数组并实现我想要的,但有没有办法在不改变它的情况下做到这一点?

5 个答案:

答案 0 :(得分:7)

您可以访问$ index和$ parent值。试试这个:

<div data-bind="foreach: elems">
    <div class="t1">
        <!-- ko if: $index() === 0 || $parent.elems()[$index() - 1].n !== n -->
        <span data-bind="text: n" class="c1"></span>
        <!-- /ko -->
        <span data-bind="text: t" class="c2"></span>
    </div>
</div>

使用可见绑定的替代方法:

<div data-bind="foreach: elems">
    <div class="t1">
        <span data-bind="text: n, visible: $index() === 0 || $parent.elems()[$index() - 1].n !== n" class="c1"></span>
        <span data-bind="text: t" class="c2"></span>
    </div>
</div>

答案 1 :(得分:2)

如果elems属性是动态加载的,我建议您使用computed observable对数据进行分组,并返回包含这些组的数组。

因此,给定以下简单的分组功能:

function groupArray(array, keyRetrieverFunc){
    var groupedArray = array.reduce(function(previous, item){
        var key = keyRetrieverFunc(item);
        if (previous[key]){
            previous[key].items.push(item);
        } else {
            previous[key] = { key: key, items: [
                item
            ]};
        }
        return previous;
    }, {});
    return groupedArray
}

我们可以创建一个如下所示的viewmodel:

function ViewModel(){
    var self = this;

    self.elems = ko.observableArray([
        {n: 1, t : 'hello'},
        {n: 1, t : 'why'},
        {n: 1, t : 'are'},
        {n: 2, t : 'some'},
        {n: 2, t : 'ducks'},
        {n: 3, t : 'here'},
        {n: 8, t : '?'}
    ]);
    self.groupedElements = ko.computed(function(){
        var groups = {};
        var elements = self.elems();
        var groups = groupArray(elements, function(item){return item.n;});
        var results = [];
        for(var property in groups){
            results.push(groups[property]);
        }
        return results;
    });
}

观察groupsElements computed可观察对象。这是我们将DOM元素绑定到的可观察对象。

<div data-bind="foreach: groupedElements">
    Group name: <span data-bind="text: key"></span>
    <div data-bind="foreach: items">
        <div data-bind="text: t"></div>
    </div>
</div>

当新数据进入并替换观察到的self.elems数组时,groupedElements将自动更新,因为computed observable已订阅self.elems数组中的更改

我在小提琴http://jsfiddle.net/J6k54/处设置了他的设置。

答案 2 :(得分:1)

一个。您必须更改viewmodel的结构 喜欢:

function AppViewModel() {
this.elems = ko.observable([
    {n: 1, t : ['hello','why']},
    {n: 2, t : ['why','not']},
]);

}

ko.applyBindings(new AppViewModel());

B中。你的html bindins将是:

<div data-bind="foreach: elems">
<div class="t1" >
    <span data-bind="text: n" class="c1"></span>
    <span data-bind="foreach: t">
        <br/>
        <span data-bind="text: $data" class="c2"></span>
    </span>
</div>

答案 3 :(得分:1)

我认为你正在寻找这个:

<div data-bind="foreach: elems">
    <div class="t1">
        <!-- ko if: ($index() === 0 
           || ($parent.elems()[$index()-1].n != $parent.elems()[$index()].n)) -->
        <span data-bind="text: n" class="c1"></span>
        <!--/ko-->
        <span data-bind="text: t" class="c2"></span>
    </div>
</div>

参见 fiddle

答案 4 :(得分:0)

实现此目的的最简单方法是使用underscore.js转换数据结构

看到这个小提琴 http://jsfiddle.net/62Fz8/9/

underscore.js中的转换函数非常简单:


  _.chain(self.elems())
   .groupBy(function (o) { return o.n; })
   .map(function (o, key) { return { n: key,t: o };}).value();

将此函数包装在ko.computed函数中并直接绑定到它。 这会将您的数组转换为一个简单的分组,然后您需要的是以下数据绑定:

<h1> What I you want to achieve </h1>
<div data-bind="foreach: groupedItems">
  <div class="t1"> 
        <span data-bind="text: n" class="c1"></span>
       <!--ko foreach:t -->
       <span data-bind="text: t" class="c2"></span>
       <!-- /ko -->
</div>
</div>