foreach 在 knockout.js 内完成后触发操作

Firing actions after foreach finishes in knockout.js

我看过 this question and this other 一篇关于该主题的文章,但其中 none 似乎解决了我的问题。

我正在尝试为 table 的每一行中的元素初始化 javascript 插件(BootStrap javascript 工具提示)。

为此,table 必须在 DOM 中呈现。这就是问题所在。 据我所知,knockout.js 没有提供正确的方法来知道 foreach 何时渲染了整个 table.

我试图通过应用我发布的第一个 link 中提供的解决方案来解决它,即创建自定义绑定,但这对我来说不起作用。 我猜是因为数据是通过从 AJAX 调用中调用 API 来显示的。

Reproduction of the issue

ko.bindingHandlers.doSomething = {
    update: function (element) {
        console.log("table loaded???");

        //applying the tooltip
        $('.actions').tooltip({
            title: 'Actions'
        });
    }
};

function ViewModel(){
    var self = this;
    self.myItems= ko.observableArray(['A', 'B', 'C']);

    //emulating the delay of calling an external API 
    self.getdata = function(){
        setTimeout(function(){
            self.myItems.push('D');
            self.myItems.push('E');
            self.myItems.push('F');
        }, 200);       
    };

    self.addItem = function () {
        this.myItems.push('New item');
    };

    //getting the data on loading    
    self.getdata();
}

ko.applyBindings(new ViewModel());

我宁愿不使用 Knockout.js 提供的 afterRender 回调用于 foreach 情况,因为这样插件将必须在每次迭代时初始化,而不是只在结束。

我找到的唯一解决方案是在应用工具提示插件之前应用 setTimeout,但它远非理想:

ko.bindingHandlers.doSomething = {
    update: function (element) {
        console.log("table loaded???");

        setTimeout(function(){
            //applying the tooltip
            $('.actions').tooltip({
                title: 'Actions'
            });
        }, 2000);
    }
};

另一个解决方案是在从 API:

获取数据后调用 tooltip 插件
//emulating the delay of calling an external API 
self.getdata = function(){
    setTimeout(function(){
        //simulating 
        $.get(url, function(data){
            self.orders(data);
            self.loading(false);

            $('.actions').tooltip({
                title: 'Actions'
            });
        });
    }, 200);       
};

有什么更好的方法吗?

这里有一个选项,它以一种可能更可口的不同方式混合了一些您已经知道的内容。

http://jsfiddle.net/xveEP/260/

我首先向名为 tooltipHandle 的视图模型添加了一个变量。此变量将是对为应用工具提示而进行的任何 setTimeout 调用的引用(或指针)。

我为 <li> 元素而不是父 <ul> 元素创建了自定义绑定。此自定义绑定首先清除所有现有 setTimeout 实例以添加工具提示。然后它创建一个新的 setTimeout 实例,它将在 5 毫秒内 运行。此 setTimeout 中的函数会将工具提示应用到具有 .actions class 且尚未应用工具提示的所有元素。

在您的示例中,总共添加了 6 个元素:3 个立即添加,3 200 毫秒后添加。我的绑定将执行 6 次,但是,它只会应用工具提示两次:一次用于前 3 次,一次用于第二次 3 次。因为我在执行 tooltipHandle 之前清除了 setTimeout功能,仅在 2 组中的每组的第 3 项完成绑定后 运行s。我在 setTimeout 函数中添加了一个 console.log 调用,这样您会看到它在您之前的示例中只被调用了 2 次。

此外,绑定将在您单击“添加”按钮时执行,并且只会将工具提示应用于新添加的元素。

<ul data-bind="foreach: myItems">
    <li data-bind="text: $data, doSomething2: true" class="actions"></li>
</ul>

ko.bindingHandlers.doSomething2 = {
    init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        clearTimeout(bindingContext.$parent.tooltipHandle);
        bindingContext.$parent.tooltipHandle = setTimeout(function() {
            $('.actions:not([data-original-title])').tooltip({
                title: 'Actions'
            });
            window.console.log('tooltip applied');
        }, 5);
    }
};

function ViewModel(){
    var self = this;
    self.tooltipHandle = undefined;
    self.myItems= ko.observableArray(['A', 'B', 'C']);

    //emulating the delay of calling an external API 
    self.getdata = function(){
        setTimeout(function(){
            self.myItems.push('D');
            self.myItems.push('E');
            self.myItems.push('F');
        }, 200);       
    };

    self.addItem = function () {
        this.myItems.push('New item');
    };

    //getting the data on loading    
    self.getdata();
}

ko.applyBindings(new ViewModel());