Нокаут вложенных сортируемых списков

Мне нужно создать веб-страницу с двумя списками, которые можно менять местами. В списках есть элементы, которые также можно менять местами или перемещать из одного списка в другой.

Я использовал Knockoutjs и Knockoutjs-sortable, чтобы реализовать это.

HTML

<ul class="Tasks" data-bind="sortable: TaskLists">
    <li class="taskList"> 
        <span data-bind="text: Title" style='background: lightgray;'></span>
        <ul data-bind="sortable: Tasks">
           <li class="item"> 
              <span class="taskName" href="#" data-bind="text: name"></span>
            </li>
        </ul>
    </li>
</ul>

JS

var Task = function (name) {
    this.name = ko.observable(name);
}

var ViewModel = function () {
    var self = this;
    self.tasks1 = ko.observableArray([]);
    self.tasks2 = ko.observableArray([]);
    for (var i = 0; i < 5; i++) {
        self.tasks1.push(new Task("This task belongs to list one"));
        self.tasks2.push(new Task("This task belongs to list two"));
    }
    self.TaskList1 = {
        Tasks: self.tasks1,
        Title: 'List One'
    };
    self.TaskList2 = {
        Tasks: self.tasks2,
        Title: 'List Two'
    };
    self.TaskLists = ko.observableArray([]);
    self.TaskLists.push(self.TaskList1);
    self.TaskLists.push(self.TaskList2);
};

ko.bindingHandlers.sortable.options = {
    placeholder: 'ui-state-highlight',
    start: function (e, ui) {
        var dragElements = $('.ui-state-highlight');
        dragElements.css("height", ui.helper.outerHeight());
    }
};
ko.applyBindings(new ViewModel());

CSS

.frame {
    padding: 10px;
    overflow:auto;
}
.item {
    border: black 1px solid;
    width: 100px;
    background-color: #DDD;
    cursor: move;
    text-align: center;
    margin-top: 2px;
    margin-bottom: 2px;
}
.taskList {
    width: 110px;
    float:left;
    background: lightgreen;
}
.Tasks {
    width:400px;
    border: 1px #eee solid;
    height: 100%;
}
.taskName {
    word-wrap: break-word;
}
.ui-state-highlight {
    background: grey;
    border:1px dashed grey;
}

Вот что я получил на данный момент (fiddle).

Все работает, как и ожидалось, за исключением перемещения списков. При перемещении списка я ожидаю, что заполнитель для перетаскивания будет выглядеть так:

введите здесь описание изображения

но я получаю:

введите здесь описание изображения

Что я делаю неправильно? Как я могу достичь этих ожидаемых результатов?


person GETah    schedule 01.02.2014    source источник


Ответы (1)


Я обнаружил проблему после хорошего ночного сна. В моем дизайне выше было несколько подводных камней:

  • Во вложенных списках не было connectClass, который поможет нокаут-сортируемому узнать, куда можно перетащить элемент. Если оставить это значение неуказанным, один из списков принимает либо элемент, либо полный список для перетаскивания в него.
  • Перетаскиваемый заполнитель имеет другой стиль, чем сами списки. Вот почему перетаскиваемый заполнитель не был правильно отображен (см. приведенный снимок экрана в моем вопросе выше)
  • Был определен только один заполнитель, который применялся как к перетаскиваемому элементу, так и к перетаскиваемому списку, что не очень хорошо.

Вот скрипка, показывающая полное рабочее решение.

HTML

<div class="frame">
    <ul class="Tasks" data-bind="sortable: {data: TaskLists, connectClass: 'columns', options: { placeholder: 'list-highlight'}}">
      <li class="taskList">
        <span data-bind="text: Title" style='background: lightgray;'></span>
        <ul data-bind="sortable: {data: Tasks, connectClass: 'columnItems', options : { placeholder: 'ui-state-highlight'}}">
            <li class="item">
              <span class="taskName" href="#" data-bind="text: name"></span>
            </li>
        </ul>
       </li>
    </ul>
</div>

JS

var Task = function(name) {
    this.name = ko.observable(name);
}

var ViewModel = function() {
    var self = this;
    self.tasks1 = ko.observableArray([]);
    self.tasks2 = ko.observableArray([]);
    for (var i=0;i<5;i++){ 
        self.tasks1.push(new Task("This task belongs to list one"));
        self.tasks2.push(new Task("This task belongs to list two"));
    }
    self.TaskList1 = {Tasks: self.tasks1, Title:'List One'};
    self.TaskList2 = {Tasks: self.tasks2, Title:'List Two'};
    self.TaskLists = ko.observableArray([]);
    self.TaskLists.push(self.TaskList1);
    self.TaskLists.push(self.TaskList2);
};

ko.bindingHandlers.sortable.options = {
        //placeholder: 'ui-state-highlight',
        start: function (e, ui) {
            var dragItems = $('.ui-state-highlight');
            var dragLists = $('.list-highlight');
            var elementClass = ui.helper[0].className;
            if(elementClass === "item")
                  dragItems.css("height",ui.helper.outerHeight());
            if(elementClass === "taskList")
                  dragLists.css("height",ui.helper.outerHeight());
        }
    };
ko.applyBindings(new ViewModel());

CSS

    .frame{
        padding: 10px;
        overflow:auto;
    }

    .item {
       border: black 1px solid;
       width: 100px;
       background-color: #DDD;
       cursor: move;
       text-align: center;
       margin-top: 2px;
       margin-bottom: 2px;
    }

    .list-highlight{
        width: 100px;
        float:left;
        background: gray;
    }

    .taskList{
        width: 110px;
        float:left;
        background: lightgreen;
    }

    .Tasks{
        width:400px;
        border: 1px #eee solid;
        height: 100%;
    }

    .taskName{
        word-wrap: break-word;
    }

    .ui-state-highlight{
        background: grey;
        border:1px dashed grey;
}
person GETah    schedule 02.02.2014
comment
Какая связь (если есть) между именами/значениями connectClass «столбцы» и «columnItems» и вашей ViewModel или CSS? У меня есть (довольно) похожий пример скрипки jsfiddle.net/patware/3VS6s и я не могу сделать это работает как ваша скрипка. - person Patrice Calvé; 26.02.2014
comment
ConnectClass — это класс, который плагин перетаскивания будет искать при перетаскивании html-элементов в документе. Я также использую те же классы для форматирования своих объектов. - person GETah; 27.02.2014
comment
Я не могу найти столбцы или элементы столбцов в вашем html или css. Я ожидал, что ваш html будет класс = столбцы или что-то в этом роде. - person Patrice Calvé; 28.02.2014
comment
О, простите, вы правы! Эти классы не существуют в html. На самом деле они добавляются jQuery во время выполнения :) - person GETah; 28.02.2014