Подтвердить что ты не робот

Нокаут js - beforeRemove анимация при добавлении элементов в наблюдаемый массив

У меня проблема с нокаутом js и отображением наблюдаемого массива в виде списка; При добавлении элемента во время анимации beforeRemove удаленный элемент перемещается в нижнюю часть списка вместо того, чтобы оставаться в его положении, пока анимация не закончится, и элемент будет удален.

Вот jsfiddle, чтобы объяснить проблему немного лучше: 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>
4b9b3361

Ответ 1

Ваша функция "addItem" выполняется через 1 секунду (setInterval 1000 мс), поэтому "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);
});

Ответ 2

В качестве обходного пути, если это возможно, вы хотите сделать все свои изменения в наблюдаемом массиве за одну операцию. Например, вместо добавления и/или удаления элементов по отдельности вы хотите обновить весь наблюдаемый массив сразу, возможно, используя ko.utils.arrayFilter, чтобы вернуть отфильтрованную версию наблюдаемого массива обратно.

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

Если это невозможно (как это, вероятно, для вас), вам нужно отложить добавление элемента в массив до завершения анимации удаления. Это, вероятно, будет включать сложную систему "рычагов и шкивов", поэтому полученный код будет довольно нежелательным. Возможно, пострадает и пользовательский интерфейс, так как "быстрые" действия пользователя должны быть поставлены в очередь или запрещены.

Очевидно, идеальным решением является то, что это рассматривается в следующем выпуске нокаута. До тех пор вы можете либо "понизить" до 2,1, либо сделать небольшой патч существующей кодовой базе нокаутов. Я не могу ручаться за этот патч, и он может иметь нежелательные последствия, но он должен быть хорошим стоп-лоском для этой проблемы.

Ответ 3

Проблема связана с временем добавления: вам нужно дождаться окончания анимации, прежде чем начинать процесс добавления нового элемента.

Рабочая скрипка: 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);

});

Ответ 4

Смешивание "сращивания" и нокаута "array.remove" может привести к неожиданному поведению, вам лучше обращаться с ним последовательно, например, используйте только "сращивание", как показано ниже:

                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);
                }

Ответ 6

Вы можете использовать привязку 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>

Ответ 7

обновил ваш пример, который, как мне кажется, является ожидаемым только временем aumeti, чтобы добавить элемент в 1 секунду до 3

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

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