KO中的foreach绑定

本文深入探讨了Ko.js中foreach绑定的使用方法,包括生成表格、添加/删除操作、参数说明及注意事项。通过实例展示了如何在不同场景下灵活运用foreach绑定,以及数组改变时UI的高效更新机制。同时介绍了如何处理数组项的销毁、DOM元素后期处理和动画,以及如何避免不必要的DOM操作以提高性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、目的

foreach绑定为数组中的每个元素复制一系列标记,并将标记绑定到对应的数组元素上。这对于绘制list和table非常有用。如果数组是一个observable数组,当添加,删除,或者重排序数组元素时,foreach绑定可以非常高效的更新UI来匹配---通过插入或删除标记,或者重排序DOM元素来实现,而且不会影响到其他DOM元素。这比数组改变后重新生成所有标记要快得多。
当然,你可以在其它控制流绑定如if绑定,with绑定中随意嵌套foreach绑定。

二、例子

例子1、在数组中进行迭代

这个例子使用foreach绑定生成一个只读的table,每一行是数组中的一项。

<table>
    <thead>
        <tr><th>First name</th><th>Last name</th></tr>
    </thead>
    <tbody data-bind="foreach: people">
        <tr>
            <td data-bind="text: firstName"></td>
            <td data-bind="text: lastName"></td>
        </tr>
    </tbody>
</table>

<script type="text/javascript">
    ko.applyBindings({
        people: [
            { firstName: 'Bert', lastName: 'Bertington' },
            { firstName: 'Charles', lastName: 'Charlesforth' },
            { firstName: 'Denise', lastName: 'Dentiste' }
        ]
    });
</script>

运行效果:



例子2、add/remove操作
这个例子说明,如果数组是observable的,UI会与数组的改变保持同步。

<h4>People</h4>
<ul data-bind="foreach: people">
    <li>
        Name at position <span data-bind="text: $index"> </span>:
        <span data-bind="text: name"> </span>
        <a href="#" data-bind="click: $parent.removePerson">Remove</a>
    </li>
</ul>
<button data-bind="click: addPerson">Add</button>

function AppViewModel() {
    var self = this;

    self.people = ko.observableArray([
        { name: 'Bert' },
        { name: 'Charles' },
        { name: 'Denise' }
    ]);

    self.addPerson = function() {
        self.people.push({ name: "New at " + new Date() });
    };

    self.removePerson = function() {
        self.people.remove(this);
    }
}

ko.applyBindings(new AppViewModel());


foreach参数说明:

主要参数:
参数是你想要迭代的数组。foreach绑定将为每个数组项生成html标记。当然也可以传递一个JavaScript object作为参数,包含你想要迭代的数据,这个object可能还包含其它属性,例如:afterAdd,includeDestroyed。另外,如果数组是observable的,foreach绑定将根据数组内容的改变,增加或删除相应的html标记。

一些注意事项:

1、使用$data访问每个数组项

上面的例子中,在foreach绑定里,可以直接引用数组项的元素,例如firstName和lastName。如果想引用数组项本身,而不是它的某个属性,可以使用$data参数,表示当前数组项。
<ul data-bind="foreach: months">
    <li>
        The current item is: <b data-bind="text: $data"></b>
    </li>
</ul>

<script type="text/javascript">
    ko.applyBindings({
        months: [ 'Jan', 'Feb', 'Mar', 'etc' ]
    });
</script>

可以使用$data作为属性的前缀访问数组项的各个属性。例如:
<td data-bind="text: $data.firstName"></td>

2、使用 $index, $parent等其它环境属性

可以使用$index引用从0开始的某个数组项。$index是observable的,当增加或删除数组项时,index会自动改变。同样的,可以使用$parent引用foreach之外的数据。例如:
<h1 data-bind="text: blogPostTitle"></h1>
<ul data-bind="foreach: likes">
    <li>
        <b data-bind="text: name"></b> likes the blog post <b data-bind="text: $parent.blogPostTitle"></b>
    </li>
</ul>
查看绑定环境一文,了解更多$index等环境属性的知识。

3、用'as'给foreach项取别名。

正如1所述,你可以通过$data引用数组项。在别的一些情形中,可能需要给当前项取一个容易理解的别名。
<ul data-bind="foreach: { data: people, as: 'person' }"></ul>
现在在foreach循环中,可以使用person引用当前数组项。当在嵌套foreach,在内层循环需要引用上层循环数据时,这个做法非常有用。
<ul data-bind="foreach: { data: categories, as: 'category' }">
    <li>
        <ul data-bind="foreach: { data: items, as: 'item' }">
            <li>
                <span data-bind="text: category.name"></span>:
                <span data-bind="text: item"></span>
            </li>
        </ul>
    </li>
</ul>

<script>
    var viewModel = {
        categories: ko.observableArray([
            { name: 'Fruit', items: [ 'Apple', 'Orange', 'Banana' ] },
            { name: 'Vegetables', items: [ 'Celery', 'Corn', 'Spinach' ] }
        ])
    };
    ko.applyBindings(viewModel);
</script>

4、无容器使用foreach绑定

在一些情形下,你可能没有容器元素来进行foreach绑定,例如:
<ul>
    <li class="header">Header item</li>
    <!-- The following are generated dynamically from an array -->
    <li>Item A</li>
    <li>Item B</li>
    <li>Item C</li>
</ul>
在这个例子中,没有地方可以供你插入foreach绑定。你可以使用无容器控制流语法,它基于注释标签:
<ul>
    <li class="header">Header item</li>
    <!-- ko foreach: myItems -->
        <li>Item <span data-bind="text: $data"></span></li>
    <!-- /ko -->
</ul>

<script type="text/javascript">
    ko.applyBindings({
        myItems: [ 'A', 'B', 'C' ]
    });
</script>

 <!-- ko --> 和<!-- /ko -->注释起着开始和结束标记的作用,定义了一个虚拟的元素,html标记放在它里面。Ko能够理解这种虚拟元素语法并进行绑定,就象真的有一个容器一样。

5、 数组改变如何检测和处理

当修改数组内容时(添加,移动或者删除项),foreach绑定使用一个高效的算法计算出哪些发生了改变,然后更新相应的UI:

当添加数组项时,foreach会绘制模板并将它们插入到存在的DOM中去。
当删除数组项时,foreach简单地删除相应的DOM元素。
当重排序数组项时,foreach仅仅移动相应的DOM元素。

注意,重排序检测并不能百分百保证仅仅移动相应的DOM元素。为了保证算法快速完成,重排序对检测少数数组项的简单移动进行了优化。如果算法检测到大量重排序和不相关的插入,删除,为了加快速度,它可能会选择删除后重新添加,而不是仅仅移动DOM元素。


6、 被销毁的数组项默认隐藏

有时需要标记一些数组项是已删除的,但不是真正的删除它们,这被称为非破坏性删除。如果想要了解如何做到的,请参阅observableArray的销毁函数。
默认情况下,foreach绑定会隐藏被标记为销毁的数组项。如果想要显示这些项,使用includeDestroyed参数。例如:
<div data-bind='foreach: { data: myArray, includeDestroyed: true }'>
    ...
</div>

7、对生成的DOM元素进行后期处理和动画

如果需要对生成的DOM元素再运行一些自定义的逻辑,可一使用一些回调函数,例如:afterRender/afterAdd/beforeRemove/beforeMove/afterMove
注意:这些回调函数仅仅用于触发和list相关的一些动画。如果你的目标是为一个新添加的DOM元素附加其他行为--例如事件处理,或者激活第三方UI控件,那么去实现一个自定义的绑定会相对来说更容易一些,因为你可以在任何地方使用它们而不是仅仅在foreach绑定中。

举一个简单的例子,使用afterAdd实现一个经典的yellow fade效果(这需要JQuery动画插件)。

<ul data-bind="foreach: { data: myItems, afterAdd: yellowFadeIn }">
    <li data-bind="text: $data"></li>
</ul>
<button data-bind="click: addItem">Add</button>

<script type="text/javascript">
    ko.applyBindings({
        myItems: ko.observableArray([ 'A', 'B', 'C' ]),
        yellowFadeIn: function(element, index, data) {
            $(element).filter("li")
                      .animate({ backgroundColor: 'yellow' }, 200)
                      .animate({ backgroundColor: 'white' }, 800);
        },
        addItem: function() { this.myItems.push('New item'); }
    });
</script>

回调函数简介:


afterRender -----当forech块被复制且插入到文档,foreach第一次初始化和新的数组项添加到数组时,都会被触发。参数有:

被插入的DOM元素数组
绑定的数据项


afterAdd----- 类似于afterRender,但仅当新数组项被添加到数组时才被触发。一个通常用法是在afterAdd里调用类似于jQuery’s $(domNode).fadeIn(),提供一个添加的动画效果。参数:

被添加的DOM节点
被添加的数组项Index
被添加的数组项

beforeRemove--- -在数组项被删除后,对应的DOM节点删除前触发。如果指定了该回调,必须手动删除节点。该回调通常可以调用jQuery’s $(domNode).fadeOut()来为删除提供动画效果,然后再手动删除该节点。参数:

待删除的DOM节点
删除的数组项index
删除的数组项

beforeMove----- 在数组项改变了在数组中的位置后,在相应的节点移动前触发。需要注意的是,该回调会应用到序号发生改变的所有的数组项,所以当你在数组开头插入一个项,那么所有的元素都会触发这个回调。你可以在该回调里,存储受影响的元素的原始屏幕坐标,然后在afterMove回调里提供动画效果。参数:

待删除的DOM节点
被删除的数组项序号
被删除的数组元素


afterMove----- 在数组里的数组项改变的位置,且DOM元素已经更新后触发。需要注意的是,该回调会应用到所有序号发生改变的数组项,所以当你在数组开头插入一个项,那么所有的元素都会触发这个回调。参数:

被删除的DOM节点
被删除的数组项序号
被删除的数组元素
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值