Knockout sortable drag and drop from outside

2019-05-30 08:40发布

问题:

maybe someone can help me.
I got a large view in a webapp. There is a list of products on the left and a list of categories on the right side. The products should be dragged inside the categories.
The list of products was an observableArray. Now we got performance problems (especially on IE) when the list has more than 1000 entries. So cause the products itself does not change, we took them out of the knockout binding, concat them in a string and append only one node. Now of course, the knockout sortable binding does not work anymore...

This is the template for the categories:

<div data-bind="sortable: {data: Products, beforeMove: $root.verifyProducts}, attr: { 'data-max': MaxProducts }">
<div class="menuEditTab2CatDragItem clearfix" data-bind="attr: { 'data-prodid': Id }">
    <div class="menuEditTab3ProdsNameText" data-bind="text: Name"></div>
    <div class="pull-right" style="margin-left:8px;"><i style="margin-top:-4px;" class="icon-remove" data-bind="click: $parent.removeProduct"></i>

    </div>
    <div class="pull-left menuEditTab3ProdsIdText">ID: <span data-bind="text: Id"></span>
    </div>
</div>

And here is the js code for the products list on the left side:

function fillAllProductsTab() {
        var parts = '';
        allProducts.forEach(function (item) {
            parts += '<div id="f_all_' + item.Id + '" >' +  // draggable="{data: $data, options:{containment: \'\#menuEditTab3Ce\', revert: \'invalid\'}}"
                '<div class="menuEditTab2CatDragItem">' +
                '<div>' + item.Name + '</div><div class="clearfix menuEditTab3ProdsIdText">' +
                '<div class="pull-left">ID: <span>' + item.Id + '</span></div><div class="pull-right">' +
                '<span>' + item.Price + '</span> €</div></div></div></div>';
        });
        $("#allp").append(parts);
    };

Now my thought was I can manually init the draggable plugin on the products list after appended them:

    $('#allp > div').draggable({
        //connectToSortable: '#sortable',
        helper: 'clone',
        revert: 'invalid',
        cursor: 'move'
    });

So, my problem is, that I cant get them work together. The left list is draggable, and the sortable on the right is also working but the connection does not work. There is no drop event which is fireing... Is this possible in general? If anyone has an Idea to get this work?

回答1:

When the sortable plugin receives a dragged item, it expects the element to have some meta-data attached to point to the actual data item that should be dropped. The meta-data is attached via KO's ko.utils.domData.set function. It is called like:

ko.utils.domData.set(element, key, value);

So, before an item is dropped into the sortable, you would want to attach this meta-data. Something like:

$(".drag-item").draggable({
    connectToSortable: ".container",
    helper: "clone",
    start: function(event, ui) {
        ko.utils.domData.set(event.target, "ko_dragItem", true);
    }
});

When a draggable item is dropped, the plugin tries two ways to create a copy to actually place into the sortable's observableArray. First, it looks for a clone function on the item. Second it runs a dragged function that is configured in the sortable binding and uses the return value as the item.

So, a sortable binding might look like:

<div class="container" data-bind="sortable: { data: tasks, dragged: handleDraggedItem }">
    <div class="item" data-bind="text: name">
    </div>
</div>

With handleDraggedItem like:

this.handleDraggedItem = function(item, event, ui) {
    return new Task(ui.item.text());
};

Here is a sample in jsFiddle: http://jsfiddle.net/rniemeyer/aewLbnrm/

So, you would want to decide how/where that you want to construct your item. You could do it in the draggable start method and simply return the item from your dragged function or do it like in my sample in the dragged function. Hope that all makes sense!