Knockout js - beforeRemove动画,同时将项目添加到可观察数组

时间:2013-02-12 08:49:41

标签: javascript knockout.js

我遇到了敲门js的问题,并将一个可观察的数组显示为列表;在beforeRemove动画运行时添加项目时,删除的元素将移动到列表的底部,而不是保持其位置,直到动画完成并删除元素。

这是一个更好地解释问题的方法:http://jsfiddle.net/bPP5Q/8/

任何人都知道如何解决这个问题?

的javascript:

jQuery(function ($) {
    var ViewModel = function (data) {
        var self = this;
        self.data = ko.observableArray(data);
        self.removeLine = function (elem) {
            if (elem.nodeType === 1) $(elem).fadeOut(3000, function () {
                $(elem).remove();
            });
        }

        self.addLine = function (elem) {
            if (elem.nodeType === 1) 
                $(elem).hide().fadeIn(3000);
        }

        self.removeItem = function() {
            self.data.remove(function(item) { return item.test && item.test == 2; });   
        }

        self.addItem = function() {
            self.data.splice(1, 0, { test: 9 }); 
        }

        self.addremove = function () {
            self.removeItem();
            var id = setInterval(function() {
                self.addItem();
                clearInterval(id);
            },1000);
        }
    }

    var vm = new ViewModel([{ test: 9 }, { test: 2 }, { test: 1 }, { test: 1 }, { test: 1 }, { test: 1 }, { test: 1 }]);

    ko.applyBindings(vm);
});

HTML:

<button id="button" data-bind="click: addremove">Click</button>
<table id="grid">
    <tbody data-bind='template: { foreach: data, afterAdd: addLine, beforeRemove: removeLine }'>
        <tr>
            <td data-bind="text: test"></td>
            <td>

            </td>
        </tr>
    </tbody>
</table>

7 个答案:

答案 0 :(得分:1)

您的函数“addItem”在1秒后执行(setInterval 1000 ms),因此“self.data”包含一个新元素,而动画淡出未完成(需要3000毫秒)。这说明你的问题。

要解决此问题,您必须为“addItem”设置与“fadeout”相同的间隔,例如,它为3000。 代码变为:

jQuery(function ($) {
    var ViewModel = function (data) {
        var self = this;
        self.data = ko.observableArray(data);
        self.removeLine = function (elem) {
            if (elem.nodeType === 1) $(elem).fadeOut(3000, function () {
                $(elem).remove();
            });
        }

        self.addLine = function (elem) {
            if (elem.nodeType === 1) 
                $(elem).hide().fadeIn(3000);
        }

        self.removeItem = function() {
            self.data.remove(function(item) { return item.test && item.test == 2; });   
        }

        self.addItem = function() {
            self.data.splice(1, 0, { test: 9 }); 
        }

        self.addremove = function () {
            self.removeItem();
            var id = setInterval(function() {
                self.addItem();
                clearInterval(id);
            },3000); // the same interval as the "fadeout"
        }
    }

    var vm = new ViewModel([{ test: 9 }, { test: 2 }, { test: 1 }, { test: 1 }, { test: 1 }, { test: 1 }, { test: 1 }]);

    ko.applyBindings(vm);
});

答案 1 :(得分:0)

作为一种解决方法,如果可能,您希望在单个操作中对可观察数组进行所有更改。例如,您不想单独添加和/或删除元素,而是希望立即更新整个observable数组,可能使用ko.utils.arrayFilter将observableArray的过滤版本分配回自身。

self.data( ko.utils.arrayFilter(self.data(), function(x) {
    return shouldKeepElement(x);
}) );

如果无法做到这一点(可能就是这种情况),您需要延迟将项目添加到数组中,直到删除动画完成为止。这可能涉及一个精心设计的“杠杆和滑轮”系统,因此产生的代码将是非常不受欢迎的。用户体验也可能受到影响,因为“快速”用户操作需要排队或禁止。

显然,理想的解决方案是在下一个淘汰版本中解决这个问题。在此之前,您可以“降级”到2.1,或者a small patch到现有的淘汰代码库。我不能保证这个补丁,它可能会产生不良后果,但它应该是这个问题的一个良好的止损。

答案 2 :(得分:0)

问题在于addItem的时间 - 你需要在开始添加新项目的过程之前等待动画结束。

工作小提琴:http://jsfiddle.net/bPP5Q/26/

jQuery(function ($) {
// this runs after the window loads
var ViewModel = function (data) {
    var self = this;
    self.data = ko.observableArray(data);
    self.removeLine = function (elem) {
        if (elem.nodeType === 1) $(elem).fadeOut(3000, function () {
            $(elem).remove();
            self.addItem();
        });
    }

    self.addLine = function (elem) {
        if (elem.nodeType === 1) 
            $(elem).hide().fadeIn(3000);
    }

    self.removeItem = function() {
        self.data.remove(function(item) { return item.test && item.test == 2; });   
    }

    self.addItem = function() {
        self.data.splice(1, 0, { test: 9 }); 
    }

    self.addremove = function () {
        self.removeItem();
    }
}


var vm = new ViewModel([{
    test: 9
}, {
    test: 2
}, {
    test: 1
}, {
    test: 1
}, {
    test: 1
}, {
    test: 1
}, {
    test: 1
}]);

ko.applyBindings(vm);

});

答案 3 :(得分:0)

混合&#39;拼接&#39;和淘汰的阵列。删除&#39;可能会导致意外行为,您可以更好地一致地处理它,例如,只使用&#39; splice&#39;如下所示:

                self.addItem = function () {
                    var index = $.map(self.data(), function (item, index) {
                        if (item.test && item.test == 2) {
                            return index;
                        }
                    })[0];
                    self.data.splice(index + 1, 0, { test: 9 });
                    self.data.splice(index, 1);
                }

                self.addremove = function () {
                    //self.removeItem();
                    var id = setInterval(function () {
                        self.addItem();
                        clearInterval(id);
                    }, 1000);
                }

答案 4 :(得分:0)

此处描述的问题已在3.4.0中修复。见https://github.com/knockout/knockout/issues/790

答案 5 :(得分:-1)

您可以使用beforeAdd绑定在显示添加的元素

时删除该项目

**编辑**

这是小提琴:http://jsfiddle.net/janarde/U6Um5/5/

<button id="button" data-bind="click: addremove">Click</button>
<table id="grid">
    <tbody data-bind='template: { foreach: data, afterAdd: addLine, beforeAdd: removeLine }'>
        <tr>
            <td data-bind="text: test"></td>
            <td>

            </td>
        </tr>
    </tbody>
</table>

答案 6 :(得分:-1)

更新了我认为仅按照预期将项目添加到1秒到3的预期示例

self.removeItem();
var id = setInterval(function() {
  self.addItem();
  clearInterval(id);
},3000);

http://jsfiddle.net/bPP5Q/15/