AngularJS - Как сделать перетаскиваемое дерево?
Я хочу создать древовидную структуру, где пользователь может перетаскивать листья. У меня есть отправная точка следующим образом:
HTML
<div ng:controller="controller">
<ul ui-sortable ng-model="items" ui-options="{connectWith: '.item'}" class="item">
<li ng-repeat="item in items" class="item">
{{ item.name }}
<ul ui-sortable ng-model="item.children" ui-options="{connectWith: '.item'}" class="item">
<li ng-repeat="item in item.children" class="item">{{ item.name }}</li>
</ul>
</li>
</ul>
<pre>{{ items | json }}</pre>
</div>
<script src="http://code.angularjs.org/1.0.2/angular.min.js"></script>
<script src="https://raw.github.com/angular-ui/angular-ui/master/build/angular-ui.min.js"></script>
CoffeeScript
myapp = angular.module 'myapp', ['ui']
myapp.controller 'controller', ($scope) ->
$scope.items = [
{id: 1, name: 'Item 1', children: [
{id: 5, name: 'SubItem 1.1', children: [
{id: 11, name: 'SubItem 1.1.1', children: []},
{id: 12, name: 'SubItem 1.1.2', children: []}
]},
{id: 6, name: 'SubItem 1.2', children: []}
]},
{id: 2, name: 'Item 2', children: [
{id: 7, name: 'SubItem 2.1', children: []},
{id: 8, name: 'SubItem 2.2', children: []}
{id: 9, name: 'SubItem 2.3', children: []}
]},
{id: 3, name: 'Item 3', children: [
{id: 10, name: 'SubItem 3.1', children: []}
]}
]
angular.bootstrap document, ['myapp']
Код также находится в этом JSFiddle: http://jsfiddle.net/bESrf/1/
В моем "реальном" коде вместо одного уровня для детей я извлек второй <ul>
в шаблон и отображал его рекурсивно, что прекрасно работает, но я не смог найти способ сделать это в JSFiddle.
Что было бы лучшим способом сделать это рекурсивно и все же позволить перетаскивание, которое изменило бы массив объектов и подобъектов, представленных ng-моделью?
3 ответа
Посмотрите на этот пример: http://jsfiddle.net/furf/EJGHX/
Я только что завершил это решение, так что оно еще не задокументировано должным образом, но вы сможете найти его для своего решения.
Вам нужно будет использовать несколько вещей:
-
ezTree
директива - визуализировать дерево - Вложенный подключаемый модуль Manuele J Sarfatti для jQuery UI
- (необязательно)
uiNestedSortable
директива - включить nestedSortable из вашего шаблона. - код контроллера для обновления вашей модели - обратитесь к
$scope.update
С использованием ezTree
директива
Учитывая рекурсивную структуру данных:
$scope.data = {
children: [{
text: 'I want to create a tree like structure...',
children: [{
text: 'Take a look at this example...',
children: []
}]
}]
};
Этот шаблон создаст дерево:
<ol>
<li ez-tree="child in data.children at ol">
<div>{{item.text}}</div>
<ol></ol>
</li>
</ol>
ez-tree
выражение должно быть написано как item in collection at selector
где item
это повторяющийся ребенок (аля ng-repeat
), collection
является коллекцией корневого уровня, и selector
CSS-селектор для узла внутри шаблона, где директива должна повторяться Имя свойства терминала в коллекции, в данном случае children
будет использоваться для рекурсии дерева, в этом случае child.children
, Это может быть переписано, чтобы быть настраиваемым, но я оставлю это как упражнение для читателя.
С помощью uiNestedSortable
директива
<ol ui-nested-sortable="{ listType: 'ol', items: 'li', doNotClear: true }"
ui-nested-sortable-stop="update($event, $ui)">
</ol>
ui-nested-sortable
атрибут должен содержать конфигурацию JSON для плагина nestedSortable. Плагин требует, чтобы вы указали listType
а также items
, Мое решение требует, чтобы doNotClear
быть true
, Назначьте обратные вызовы для событий, используя ui-nested-sortable-*eventName*
, Моя директива предоставляет необязательные аргументы $ event и $ ui для обратных вызовов. Обратитесь к документации nestedSortable для других опций.
Обновление вашей модели
Есть более чем один способ снять кожу с этой кошки. Вот мой. В событии stop он извлекает дочернее свойство области действия элемента, чтобы определить, какой объект был перемещен, дочернее свойство родительской области действия элемента, чтобы определить назначение объекта, и положение элемента, чтобы определить положение объекта. в пункте назначения. Затем он обходит структуру данных, удаляет объект из исходного положения и вставляет его в новое положение.
$scope.update = function (event, ui) {
var root = event.target,
item = ui.item,
parent = item.parent(),
target = (parent[0] === root) ? $scope.data : parent.scope().child,
child = item.scope().child,
index = item.index();
target.children || (target.children = []);
function walk(target, child) {
var children = target.children,
i;
if (children) {
i = children.length;
while (i--) {
if (children[i] === child) {
return children.splice(i, 1);
} else {
walk(children[i], child)
}
}
}
}
walk($scope.data, child);
target.children.splice(index, 0, child);
};
Попробуйте Angular-NestedSortable, это плагин Angularjs, который может сортировать вложенные списки и связывать данные, и ему не нужно зависеть от jQuery. https://github.com/jimliu/Angular-NestedSortable
Небольшое редактирование скрипки от furf, чтобы она работала в IE.
IE выдает ошибку на insertNode, когда второй аргумент имеет значение null, поэтому в этом случае вместо него используется appendNode.
http://jsfiddle.net/michieljoris/VmtfR/
if (!cursor) parentNode.appendChild(cached.element);
else parentNode.insertBefore(cached.element, cursor);
Плагин Nested Sortable встроен в js, потому что IE выдает несоответствие типов MIME при включении из github.