Можно ли использовать нокаут-сортировку с деревьями-нокаутами, чтобы можно было перетаскивать предметы из одного дерева в другое?
Я пытался адаптировать пример от 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>
Есть ли что-то, что я делаю неправильно?
Заранее большое спасибо!
Я сделал это, не используя решение, которое вы делаете. То, что я делал, поддерживало все взаимодействия ui одинаково, а затем обрабатывало все в событиях. Таким образом, у меня было упорядоченное привязку (ko.bindingHandlers.sortable) с атрибутом value, поддерживающим мои параметры. В сортируемом привязке у меня будет что-то вроде:
ko.bindingHandlers.sortable = {
init: function (element, valueAccessor) {
options = valueAccessor();
$(element).sortable({
connectWith: options.connectWith || {},
items: options.items || {},
update: options.update || {}
});
}
};
а затем в обновлении я обрабатываю добавление его из одного списка в другой. Таким образом, вы заносите его в свой список (на данный момент это только в DOM), и обновление ловит его (или останавливается, в зависимости от того, что вам нужно). Обновление пожаров в первом списке И второй список, остановка - это только второй список.) Здесь вы удаляете его из первого списка (либо явно, либо используя ko.contextFor для узла dom), а затем вставляете его во второй список. На этом этапе вам придется удалить элемент из 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 going to happen here is array math and is a whole 'nother can of worms!
}
}