是否可以将knockout-sortable与knockout-ui树一起使用,以便可以将项目从一个树拖到另一个树?
我一直在尝试改编RP Niemeyer中的例子,但是我就是不能让它工作。
我可以从两个树中拖动项目,但它不会让我拖放它们。我将connectWith属性作为参数添加到可排序插件中,但它不起作用。
这是我到目前为止所知道的:
JS:
$(function () {
$(".availableItemsContainer .node").sortable({ connectWith: ".groupedItemsContainer" });
$(".groupedItemsContainer .node").sortable({ connectWith: ".availableItemsContainer" });
});
ko.bindingHandlers.flash = {
init: function (element) {
$(element).hide();
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (value) {
$(element).stop().hide().text(value).fadeIn(function () {
clearTimeout($(element).data("timeout"));
$(element).data("timeout", setTimeout(function () {
$(element).fadeOut();
valueAccessor()(null);
}, 3000));
});
}
},
timeout: null
};
ko.bindingHandlers.droppable = {
init: function(element, valueAccessor) {
var dropHandler = valueAccessor() || {};
$(element).droppable({
drop: function(event, ui) {
var item = ko.utils.domData.get(ui.draggable[0], "ko_dragItem");
if (item) {
item = item.clone ? item.clone() : item;
dropHandler.call(this, item, event, ui);
}
}
});
}
};
var DGViewModel = function () {
var self = this;
self.tree1 = {
id: 'groupedItems',
remember: true,
children: [
{
name: "Annabelle",
id: '1',
isOpen: true,
children: [
{ name: "Arnie", cssClass: 'page', id: '5', children: [] },
{ name: "Anders", cssClass: 'page', id: '6', children: [] },
{ name: "Apple", cssClass: 'page', id: '7', children: [] }
]
},
{
name: "Bertie",
id: '2',
children: [
{ name: "Boutros-Boutros", cssClass: 'page', id: '8', children: [] },
{ name: "Brianna", cssClass: 'page', id: '9', children: [] },
{ name: "Barbie", cssClass: 'page', id: '10', children: [] },
{ name: "Bee-bop", id: '4', children: [] }
]
},
{ name: "Charles", id: '3', children: [] }
],
dragHolder: ko.observable(undefined),
handlers: {
addNode: function(parent, type, name, onSuccess) {
nextId = nextId + 1;
onSuccess({ id: nextId, parent: parent, name: name, cssClass: type });
}
},
logTo: '#log1',
defaults: {
'folder': {
name: 'New Folder',
childType: 'page'
},
'page': {
name: 'New Page',
isDraggable: true,
isDropTarget: false,
canAddChildren: false
}
},
contextMenu: {
contextMenus:
[
{
name: 'foldercontext',
width: 190,
items: [
{
text: 'Delete',
run: function(dataItem) {
dataItem.deleteSelf();
}
},
{
text: 'Rename',
run: function(dataItem) {
dataItem.isRenaming(true);
}
},
{ separator: true },
{
text: 'New',
items: [
{
text: 'Page',
iconCssClass: 'page',
run: function(dataItem) {
dataItem.addChild({ type: 'page' });
}
},
{
text: 'Folder',
iconCssClass: 'folder',
run: function(dataItem) {
dataItem.addChild({ type: 'folder' });
}
}
]
}
]
},
{
name: 'pagecontext',
width: 190,
items: [
{
text: 'Delete',
run: function(dataItem) {
dataItem.deleteSelf();
}
},
{
text: 'Rename',
run: function(dataItem) {
dataItem.isRenaming(true);
}
}
]
}
],
build: function(event, dataItem) {
dataItem.selectNode();
var type = dataItem.type();
if (type == 'folder') {
return { name: 'foldercontext' };
} else {
return { name: 'pagecontext' };
}
}
}
};
self.tree2 = {
id: 'availableItems',
remember: true,
children: [
{ name: "Test1", id: '300', cssClass: 'page', children: [] },
{ name: "Test2", id: '301', cssClass: 'page', children: [] },
{ name: "Test3", id: '302', cssClass: 'page', children: [] },
{ name: "Test4", id: '303', cssClass: 'page', children: [] }
],
dragHolder: ko.observable(undefined),
handlers: {
addNode: function(parent, type, name, onSuccess) {
nextId = nextId + 1;
onSuccess({ id: nextId, parent: parent, name: name, cssClass: type });
}
},
logTo: '#log1',
defaults: {
'folder': {
name: 'New Folder',
childType: 'page'
},
'page': {
name: 'New Page',
isDraggable: true,
isDropTarget: false,
canAddChildren: false
}
},
contextMenu: {
contextMenus:
[
{
name: 'foldercontext',
width: 190,
items: [
{
text: 'Delete',
run: function(dataItem) {
dataItem.deleteSelf();
}
},
{
text: 'Rename',
run: function(dataItem) {
dataItem.isRenaming(true);
}
},
{ separator: true },
{
text: 'New',
items: [
{
text: 'Page',
iconCssClass: 'page',
run: function(dataItem) {
dataItem.addChild({ type: 'page' });
}
},
{
text: 'Folder',
iconCssClass: 'folder',
run: function(dataItem) {
dataItem.addChild({ type: 'folder' });
}
}
]
}
]
},
{
name: 'pagecontext',
width: 190,
items: [
{
text: 'Delete',
run: function(dataItem) {
dataItem.deleteSelf();
}
},
{
text: 'Rename',
run: function(dataItem) {
dataItem.isRenaming(true);
}
}
]
}
],
build: function(event, dataItem) {
dataItem.selectNode();
var type = dataItem.type();
if (type == 'folder') {
return { name: 'foldercontext' };
} else {
return { name: 'pagecontext' };
}
}
}
};
};
var vm = new DGViewModel();
var nextId = 20;
vm.treeViewModel1 = new ko.tree.viewModel(vm.tree1);
vm.treeViewModel2 = new ko.tree.viewModel(vm.tree2);
ko.bindingHandlers.sortable.beforeMove = vm.verifyAssignments;
ko.bindingHandlers.sortable.afterMove = vm.updateLastAction;
ko.applyBindings(vm);HTML:
<div id="availableItemsTreeViewWrapper">
<div class="container">
<div class="availableItemsContainer">
<div data-bind="tree : treeViewModel2"></div>
</div>
<div class="availableItemsContextMenuContainer">
<div style="padding: 10px;">
<a href="#" id="add" data-bind="click : function() { treeViewModel2.addNode(); }">Add</a>
<a href="#" id="delete" data-bind="click : treeViewModel2.deleteNode">Delete</a>
<a href="#" id="delete" data-bind="click : treeViewModel2.renameNode">Rename</a>
</div>
<div id="log2" class="logger">
</div>
</div>
</div>
</div>
<div class="mainWrapper">
<div id="groupedItemsTreeViewWrapper">
<div class="container">
<div class="groupedItemsContainer">
<div data-bind="tree : treeViewModel1"></div>
</div>
<div class="groupedItemsContextMenuContainer">
<div style="padding: 10px;">
<a href="#" id="add" data-bind="click : function() { treeViewModel1.addNode(); }">Add</a>
<a href="#" id="delete" data-bind="click : treeViewModel1.deleteNode">Delete</a>
<a href="#" id="delete" data-bind="click : treeViewModel1.renameNode">Rename</a>
</div>
<div id="log1" class="logger">
</div>
</div>
</div>
</div>
</div>我是不是做错了什么?
提前谢谢你!
发布于 2014-04-16 05:35:00
我在没有使用你正在做的解决方案的情况下做到了这一点。我所做的是保持所有ui交互都相同,然后处理事件中的所有内容。所以我有一个可排序的绑定(ko.bindingHandlers.sortable),值访问器保留了我的选项。在可排序绑定中,我会有如下内容:
ko.bindingHandlers.sortable = {
init: function (element, valueAccessor) {
options = valueAccessor();
$(element).sortable({
connectWith: options.connectWith || {},
items: options.items || {},
update: options.update || {}
});
}
};然后在更新中,我处理将它从一个列表添加到另一个列表。因此,您将其放入您的列表中(此时它仅在DOM中),更新将捕获它(或者停止,取决于您需要什么。Update在第一个列表和第二个列表上触发,stop只是第二个列表。)在这里,您将它从第一个列表中删除(显式地或在dom节点上使用ko.contextFor ),然后将其拼接到第二个列表中。此时,您必须从ui中删除该项,这就像ui.item.remove()一样简单。如果您将此信息存储在数据库中(无论采用哪种方式),您都必须在其中保留一个序列,因为无论如何,它们都会以创建时的顺序返回。
这种方式是让它与jquery ui一起工作的一种好方法,但它并没有真正集成。您让jquery ui完成它的工作,然后根据最终结果更新模型。
--
所以在html中,你要做的就是使用我上面提到的绑定。要做到这一点,您将像这样绑定两个列表:
<div id="list1" data-bind="sortable: { connectWith: '#list2', update: updateListFunction, items: '.myItems' }, foreach: list1.items">
<div class="myItems"></div>
</div>
<div id="list2" data-bind="sortable: { connectWith: '#list1', update: updateListFunction, items: '.myItems' }, foreach: list2.items">
<div class="myItems"></div>
</div>然后,updateListFunction将是上下文视图模型中的内容,如下所示:
self.updateListFuntion = function() {
//makes sure it only updates on the list to which the sorting applied, since it fires twice
if (this === ui.item.parent()[0]){
//do your updating here, using the method in the second paragraph
//ko.contextFor(ui.item) will give you the original item to get it out of the list and move it into the next
//a lot of the stuff that's going to happen here is array math and is a whole 'nother can of worms!
}
}https://stackoverflow.com/questions/23093666
复制相似问题