Jquery 可排序-将项目从一个列表拖放到另一个列表(复制)
我希望能够选择多个项目,并通过使用拖放功能将它们从一个列表复制到另一个列表(双向),并且只有当列表不存在时才使用拖放功能 我们非常感谢您的帮助 编辑:关于Charlie的帖子和小提琴,我如何才能选择多个项目拖放到其他列表?现在,它只允许一个项目同时进行 HTML:Jquery 可排序-将项目从一个列表拖放到另一个列表(复制),jquery,knockout.js,Jquery,Knockout.js,我希望能够选择多个项目,并通过使用拖放功能将它们从一个列表复制到另一个列表(双向),并且只有当列表不存在时才使用拖放功能 我们非常感谢您的帮助 编辑:关于Charlie的帖子和小提琴,我如何才能选择多个项目拖放到其他列表?现在,它只允许一个项目同时进行 HTML: <div class="list"> <h2>Stored procedures In DB 1</h2> <ul class="list" data-bind="sortab
<div class="list">
<h2>Stored procedures In DB 1</h2>
<ul class="list" data-bind="sortable: { data: storedProceduresInDB1, beforeMove: checkAndCopy }">
<li class="item" data-bind="text: Name"></li>
</ul>
</div><br>
<div class="list">
<h2>Stored procedures In DB 2</h2>
<ul class="list" data-bind="sortable: { data: storedProceduresInDB2, beforeMove: checkAndCopy }">
<li class="item" data-bind="text: Name"></li>
</ul>
</div>
var ViewModel = function () {
var self = this;
self.storedProceduresInDB1 = ko.observableArray([
{ Name: 'SP1', Id: 1 },
{ Name: 'SP2', Id: 2 },
{ Name: 'SP3', Id: 3 }
]);
self.storedProceduresInDB2 = ko.observableArray([
{ Name: 'SP3', Id: 3 },
{ Name: 'SP4', Id: 4 },
{ Name: 'SP5', Id: 5 }
]);
self.selectedStoredProcedureInDB1 = ko.observable();
self.selectedStoredProcedureInDB2 = ko.observable();
self.selectStoredProcedureInDB1 = function (sp) {
self.selectedStoredProcedureInDB1(sp);
};
self.selectStoredProcedureInDB2 = function (sp) {
self.selectedStoredProcedureInDB2(sp);
};
self.checkAndCopy = function(event) {
var targetHasItem = ko.utils.arrayFilter(event.targetParent(), function(item) {
return item.Id == event.item.Id;
}).length;
if(!targetHasItem) {
event.targetParent.splice(event.targetIndex, 0, event.item);
}
if(event.targetParent != event.sourceParent) {
event.cancelDrop = true;
}
};
};
ko.applyBindings(new ViewModel());
注意:这个答案一次只允许对一个项目进行排序。有关一次对多个项目进行排序的信息,请参阅。 我建议使用Ryan Niemeyer的
knockout-sortable
binding-handler(),它将处理knockout和jQuery的sortable的许多怪癖放在一起
如果使用knockout sortable
,您可以使用beforeMove
回调取消删除,并根据您的条件将副本添加到第二个列表中
,以下是相关部分:
HTML
请注意可排序绑定,其中我们传入一个对象,指定用于列表的数据,以及用于beforeMove
回调的方法
<div class="list">
<h2>Stored procedures In DB 1</h2>
<ul class="list" data-bind="sortable: { data: storedProceduresInDB1, beforeMove: checkAndCopy }">
<li class="item" data-bind="text: Name"></li>
</ul>
</div>
<div class="list">
<h2>Stored procedures In DB 2</h2>
<ul class="list" data-bind="sortable: { data: storedProceduresInDB2, beforeMove: checkAndCopy }">
<li class="item" data-bind="text: Name"></li>
</ul>
</div>
这是一个允许一次对多个项目进行排序的解决方案。基于但修改为使用
knockout.js
和knockout sortable
JSFIDLE(在Chrome中测试):
HTML
向排序表和列表项添加ID,以便jQuery排序代码可以确定选择了哪些项
添加了一个options
属性,该属性被传递给底层jQuery可排序插件
为每个列表项添加了一个用于选择和多重选择的单击处理程序
<div class="list">
<h2>Stored procedures In DB 1</h2>
<ul class="list" id="sortableForDB_1" data-bind="sortable: {
data: storedProceduresInDB1,
beforeMove: checkAndCopy,
options: multiSortableOptions }">
<li class="item" data-bind="attr: { id: 'sp_'+Id }, text: Name,
click: $root.selectProcedure.bind($data, $parent.storedProceduresInDB1()),
css: { selected: Selected }">
</li>
</ul>
</div>
<div class="list">
<h2>Stored procedures In DB 2</h2>
<ul class="list" id="sortableForDB_2" data-bind="sortable: {
data: storedProceduresInDB2,
beforeMove: checkAndCopy,
options: multiSortableOptions }">
<li class="item" data-bind="attr: { id: 'sp_'+Id }, text: Name,
click: $root.selectProcedure.bind($data, $parent.storedProceduresInDB2()),
css: { selected: Selected }">
</li>
</ul>
</div>
视图模型。在每个存储过程中添加了一个选定的
可观察属性。添加了一个selectProcedure
方法,以基于单击和ctrl+clicks更新新的Selected
属性
还对checkAndCopy
方法进行了大量修改。我认为内联注释解释了它是如何工作得很好的
var ViewModel = function () {
var self = this;
self.storedProceduresInDB1 = ko.observableArray([
{ Name: 'SP1', Id: 1, Selected: ko.observable(false) },
{ Name: 'SP2', Id: 2, Selected: ko.observable(false) },
{ Name: 'SP3', Id: 3, Selected: ko.observable(false) }
]);
self.storedProceduresInDB2 = ko.observableArray([
{ Name: 'SP3', Id: 3, Selected: ko.observable(false) },
{ Name: 'SP4', Id: 4, Selected: ko.observable(false) },
{ Name: 'SP5', Id: 5, Selected: ko.observable(false) }
]);
self.checkAndCopy = function(event) {
var items;
if(event.targetParent !== event.sourceParent) {
// Get all items that don't yet exist in the target
items = ko.utils.arrayFilter(event.sourceParent(), function(item) {
return item.Selected() && !ko.utils.arrayFirst(event.targetParent(), function(targetItem) {
return targetItem.Id == item.Id;
});
});
// Clone the items (otherwise the Selected observable is shared by the item in both lists)
items = ko.utils.arrayMap(items, function(item) {
var clone = ko.toJS(item);
clone.Selected = ko.observable(true);
// While we're here, let's deselect the source items so it looks cleaner
item.Selected(false);
return clone;
});
// Deselect everything in the target list now so when we splice only the new items are selected
ko.utils.arrayForEach(event.targetParent(), function(item) {
item.Selected(false);
});
} else {
// Moving items within list, grab all selected items from list
items = event.sourceParent.remove(function(item) {
return item.Selected();
});
}
// splice items in at target index
items = items.reverse();
for(var i=0; i<items.length; i++) {
event.targetParent.splice(event.targetIndex, 0, items[i]);
}
// always cancel drop now, since we're manually rearranging everything
event.cancelDrop = true;
};
self.selectProcedure = function(array, $data, event) {
if(!event.ctrlKey && !event.metaKey) {
$data.Selected(true);
ko.utils.arrayForEach(array, function(item) {
if(item !== $data) {
item.Selected(false);
}
});
} else {
$data.Selected(!$data.Selected());
}
};
};
谢谢你的意见。有没有可能选择多个项目同时复制淘汰排序?我已经开始悬赏,希望你能回答。嘿@Henrik,我现在没有时间写一个拖动多个项目的答案,但我确实找到了这个相关的答案,可能会让你朝着正确的方向前进:好的,谢谢。我曾尝试使用jQueryUI和sortable实现拖动多个项目,但没有成功。如果本周晚些时候有时间,我会看看是否能想出一些办法。
var multiSortableOptions = {
delay: 150,
revert: 0,
stop: function(event, ui) {
// Force lists to refresh all items
var db1 = myViewModel.storedProceduresInDB1,
db2 = myViewModel.storedProceduresInDB2,
temp1 = db1(),
temp2 = db2();
ui.item.remove();
db1([]);
db1(temp1);
db2([]);
db2(temp2);
},
helper: function(event, $item) {
// probably a better way to pass these around than in id attributes, but it works
var dbId = $item.parent().attr('id').split('_')[1],
itemId = $item.attr('id').split('_')[1],
db = myViewModel['storedProceduresInDB'+dbId];
// If you grab an unhighlighted item to drag, then deselect (unhighlight) everything else
if(!$item.hasClass('selected')) {
ko.utils.arrayForEach(db(), function(item) {
if(item.Id == itemId) {
item.Selected(true);
} else {
item.Selected(false);
}
});
}
// Create a helper object with all currently selected items
var $selected = $item.parent().find('.selected');
var $helper = $('<li class="helper"/>');
$helper.append($selected.clone());
$selected.not($item).remove();
return $helper;
}
};
var ViewModel = function () {
var self = this;
self.storedProceduresInDB1 = ko.observableArray([
{ Name: 'SP1', Id: 1, Selected: ko.observable(false) },
{ Name: 'SP2', Id: 2, Selected: ko.observable(false) },
{ Name: 'SP3', Id: 3, Selected: ko.observable(false) }
]);
self.storedProceduresInDB2 = ko.observableArray([
{ Name: 'SP3', Id: 3, Selected: ko.observable(false) },
{ Name: 'SP4', Id: 4, Selected: ko.observable(false) },
{ Name: 'SP5', Id: 5, Selected: ko.observable(false) }
]);
self.checkAndCopy = function(event) {
var items;
if(event.targetParent !== event.sourceParent) {
// Get all items that don't yet exist in the target
items = ko.utils.arrayFilter(event.sourceParent(), function(item) {
return item.Selected() && !ko.utils.arrayFirst(event.targetParent(), function(targetItem) {
return targetItem.Id == item.Id;
});
});
// Clone the items (otherwise the Selected observable is shared by the item in both lists)
items = ko.utils.arrayMap(items, function(item) {
var clone = ko.toJS(item);
clone.Selected = ko.observable(true);
// While we're here, let's deselect the source items so it looks cleaner
item.Selected(false);
return clone;
});
// Deselect everything in the target list now so when we splice only the new items are selected
ko.utils.arrayForEach(event.targetParent(), function(item) {
item.Selected(false);
});
} else {
// Moving items within list, grab all selected items from list
items = event.sourceParent.remove(function(item) {
return item.Selected();
});
}
// splice items in at target index
items = items.reverse();
for(var i=0; i<items.length; i++) {
event.targetParent.splice(event.targetIndex, 0, items[i]);
}
// always cancel drop now, since we're manually rearranging everything
event.cancelDrop = true;
};
self.selectProcedure = function(array, $data, event) {
if(!event.ctrlKey && !event.metaKey) {
$data.Selected(true);
ko.utils.arrayForEach(array, function(item) {
if(item !== $data) {
item.Selected(false);
}
});
} else {
$data.Selected(!$data.Selected());
}
};
};
myViewModel = new ViewModel();
ko.applyBindings(myViewModel);