在foreach绑定中knockoutjs afterRender功能

时间:2012-07-31 21:23:16

标签: javascript knockout.js

我正在尝试使用knockoutjs foreach绑定指定插入元素的入口效果。设置非常简单:

myViewModel.myObservableArray.push({enter:function() { ... });

并在标记中:

foreach:{data:myObservableArray, afterRender:enter}

好像应该有用......对吗?但它没有在项目上找到输入功能。我发现的工作是:

myViewModel.enter = function(something, item) { item.enter(); };

foreach:{data:myObservableArray, afterRender:$root.enter}

将enter函数添加到根视图模型并将afterRender绑定到$ root.enter。然后输入作为第二个参数传递给项目,因此可以依次调用项目的输入功能,但感觉就像是黑客。

有谁能解释这里发生了什么?

感谢。

修改

澄清我已经创建了fiddle

这样做非常简单,并且在animated transitions example中有更深入的介绍。它在根视图模型中为使用foreach绑定插入的每个dom元素运行一个函数。

所以问题是:如果我想要特定于项目的afterRender,afterAdd或beforeRemove函数怎么办?我可以看到这很有用。特别是如果使用模板绑定来动态选择模板(note 4)。这样做有干净的方法吗?现在我在视图模型的根目录中有一个enter函数,它只是调用项目上的enter函数,但就像我上面说的那样,感觉就像是一个黑客。

3 个答案:

答案 0 :(得分:5)

不,这就是it was designed的方式。

来自Documenation

  

注3:使用“afterRender”,“afterAdd”和“beforeRemove”

     

有时您可能希望在模板生成的DOM元素上运行自定义后处理逻辑。例如,如果您正在使用JavaScript小部件库(如jQuery UI),则可能需要拦截模板的输出,以便您可以在其上运行jQuery UI命令,将某些呈现的元素转换为日期选择器,滑块或还要别的吗。

     

通常,对DOM元素执行此类后处理的最佳方法是编写custom binding,但如果您真的只想访问模板发出的原始DOM元素,则可以使用afterRender。 / p>      

传递一个函数引用(函数文字,或者给视图模型上的函数名称),Knockout将在渲染或重新渲染模板后立即调用它。

(强调我的)


正如它所说,自定义绑定是另一种方法,可能会更好,具体取决于enter()函数的作用。

答案 1 :(得分:5)

在这种情况下,

下划线去抖(_.debounce)是一个很好的解决方案。

模板

data-bind=" template: {foreach:myObservableArray, afterRender: runWhenAllRenderDone }
如果在最后100毫秒内未触发afterRender,则会执行去抖功能。

var runWhenAllRenderDone = _.debounce(myFunction, 100);

function myFunction(){
    //do some task but do it for once only
}

不是很棒吗?

答案 2 :(得分:0)

找到了另一种没有timeout的解决方法,该技术基于虚拟元素<!-- ko if: $parent.afterRender($index()) --><!-- /ko -->

function ReservationsViewModel() {
    // Editable data
    this.seats = ko.observableArray([
        { name: "Steve", meal: "Standard (sandwich)", price: 343},
        { name: "Steve", meal: "Premium (lobster)", price: 10},
        { name: "Steve", meal: "Ultimate (whole zebra)", price: 290}
    ]);

    this.afterRender = (i) => {
       // checking element rendered is last
       if (i === this.seats().length - 1) { 
           console.log('rendered');
           // our after rendered logic goes here...
       }
    };
}

它的模板是

<tbody data-bind="foreach: seats">
    <tr>
        <td data-bind="text: name"></td>
        <td data-bind="text: meal"></td>
        <td data-bind="text: price"></td>
    </tr>    
    <!-- ko if: $parent.afterRender($index()) --><!-- /ko -->
</tbody>

这个额外的逻辑i === this.seats().length - 1,将检查是否渲染了最后一行。然后我们可以在内部执行afterRender逻辑。