How do I swap two items in an observableArray?

2019-01-11 10:24发布

I have a button that moves an item one position left in an observableArray. I am doing it the following way. However, the drawback is that categories()[index] gets removed from the array, thus discarding whatever DOM manipulation (by jQuery validation in my case) on that node.

Is there a way to swap two items without using a temporary variable so as to preserve the DOM node?

    moveUp: function (category) {
        var categories = viewModel.categories;
        var length = categories().length;
        var index = categories.indexOf(category);
        var insertIndex = (index + length - 1) % length;

        categories.splice(index, 1);
        categories.splice(insertIndex, 0, category);
        $categories.trigger("create");
    }

标签: knockout.js
4条回答
神经病院院长
2楼-- · 2019-01-11 10:49

I had a similar problem as I wanted jQuery drag & drop on my items. My solution became to use knockoutjs templates to bind the beforeRemove and afterAdd events to the model. The Person Class/function is also a simple knockout view model.

In the below example I use .draggable(), but you could easily use validation. Add your own code for manipulating the observableArray and you should be good to go.

HTML:

<div data-bind="template: {foreach:attendeesToShow, beforeRemove:hideAttendee, afterAdd:showAttendee}">
    <div class="person">
        <img src="person.jpg" alt="" />
        <div  data-bind="text: firstName" ></div>
        <div class="deleteimg" data-bind="click:$parent.removeAttendee" title="Remove"></div>
    </div>
</div>

ViewModel:

var ViewModel = function () {
    var self = this;
    var at = [new Person('First', 'Person', 'first@example.com'),
                    Person('Second', 'Person', 'second@example.com')
                ];
    self.attendees = ko.observableArray(at);

    self.removeAttendee = function (attendee) {
        self.attendees.remove(attendee);
    };

    this.showAttendee = function (elem) {
        if (elem.nodeType === 1) {
    $(elem).hide().show("slow").draggable();//Add jQuery functionality 
        }
    };
    this.hideAttendee = function (elem) {
        if (elem.nodeType === 1) {
            $(elem).hide(function () {
                $(elem).remove();
            });
        }
    };
};

ko.applyBindings(new ViewModel());
查看更多
做自己的国王
3楼-- · 2019-01-11 10:53

I know this answer comes a bit late, but I thought it might be useful to others who want a more general swap solution. You can add a swap function to your observableArrays like so:

ko.observableArray.fn.swap = function(index1, index2) {
    this.valueWillMutate();

    var temp = this()[index1];
    this()[index1] = this()[index2];
    this()[index2] = temp;

    this.valueHasMutated();
}

You can then use this function to swap two elements in an array given their indices:

myArray.swap(index1, index2);

For a moveUp function, you could then do something like this:

moveUp: function(category) {
    var i = categories.indexOf(category);
    if (i > 0) {
        categories.swap(i, i+1);
    }
}
查看更多
Deceive 欺骗
4楼-- · 2019-01-11 10:56

Here's my version of moveUp that does the swap in one step:

moveUp: function(category) {
    var i = categories.indexOf(category);
    if (i >= 1) {
        var array = categories();
        categories.splice(i-1, 2, array[i], array[i-1]);
    }
}

That still doesn't solve the problem, though, because Knockout will still see the swap as a delete and add action. There's an open issue for Knockout to support moving items, though. Update: As of version 2.2.0, Knockout does recognize moved items and the foreach binding won't re-render them.

查看更多
混吃等死
5楼-- · 2019-01-11 10:59

thanks to Michael Best for his version of moveup

my version of moveDown

moveDown: function(category) {
    var array = categories();
    var i = categories.indexOf(category);
    if (i < arr.length) {
        categories.splice(i, 2, array[i + 1], array[i]);
    }
}
查看更多
登录 后发表回答