I want to create a tree like structure where the user can drag and drop leaves. I have a starting point as follows:
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']
The code is in this JSFiddle as well: http://jsfiddle.net/bESrf/1/
On my “real” code, instead of only having one level for children, I extracted the second <ul> into a template and rendered it recursively, which works fine, but I couldn’t find a way to do it in JSFiddle.
What would be the best way to render it recursively and still allow dragging and dropping that would change the array of objects and sub-objects represented by ng-model?
Take a look at this example: http://jsfiddle.net/furf/EJGHX/
I just completed this solution so it is not yet properly documented, but you should be able to mine it for your solution.
You will need to use a few things:
ezTreedirective – to render the treeuiNestedSortabledirective – to enable nestedSortable from your template.$scope.updateUsing the
ezTreedirectiveGiven a recursive data structure:
This template will build the tree:
The
ez-treeexpression should be written asitem in collection at selectorwhereitemis the iterated child (alang-repeat),collectionis the root-level collection, andselectoris the CSS selector for the node inside the template where the directive should recurse. The name of the terminal property of the collection, in this casechildrenwill be used to recurse the tree, in this casechild.children. This could be rewritten to be configurable but I’ll leave that as an exercise for the reader.Using
uiNestedSortabledirectiveThe
ui-nested-sortableattribute should contain a JSON configuration for the nestedSortable plugin. The plugin requires that you specifylistTypeanditems. My solution requires thatdoNotClearbetrue. Assign callbacks to events usingui-nested-sortable-*eventName*. My directive supplies optional $event and $ui arguments to callbacks. Refer to nestedSortable’s documentation for other options.Updating your model
There is more than one way to skin this cat. Here’s mine. On the stop event, it extracts the child property of the element’s scope to determine which object was moved, the child property of the element’s parent’s scope to determine the destination of the object, and the position of the element to determine the position of the object at its destination. It then walks the data structure and removes the object from its original position and inserts it into its new position.