Кусок меню, которое сейчас делаю. Добавляются-удаляются пункты, сортируется и проч. Особо не отлаживал, так что буду раз замечаниям и правкам.
Было бы хорошо сделать добавление пунктов в закрытый пункт (В демке
jQuery Sortable есть случай, когда элемент на таб перетаскивается, но пока не дошло как это здесь сделать)
<!doctype html>
<html>
<head>
<style>
ul {
list-style: none;
padding: 0;
margin: 0;
}
li {background: #888; border: solid 1px red;}
li li {background: #aaa;}
li li li {background: #eee;}
li li li li {background: #fff;}
</style>
<script src="http://code.jquery.com/jquery-2.0.1.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min.js"></script>
<script src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
<script src="http://tamtakoe.ru/angular-ui-sortable.js"></script>
<script language="javascript" type="text/javascript">
angular.module('treeMenu', ['ui.sortable'])
function TreeCtrl ($scope) {
var tree = [{
id: 1,
name: "Пункт1",
editor: false,
res_type: 'page',
vis: false,
sub: [{
id: 2,
name: "Пункт1-1",
editor: false,
res_type: 'page',
vis: false,
sub: []
},{
id: 8,
name: "Пункт1-2",
editor: false,
res_type: 'page',
vis: false,
sub: []
}]
},{
id: 5,
name: "Пункт2",
editor: false,
res_type: 'page',
vis: false,
sub: []
}],
blankItem = {
id: null,
name: '',
editor: true,
res_type: 'page',
vis: false,
sub: []
};
/*MainMenu.query(function(data) {
tree = one2multi(data); //Преобразование линейного массива в многомерный
addBlank();
$scope.tree = tree;
menuCollapse(Global.menuItemId) //Активация определенного пункта меню
});*/
addBlank();
$scope.tree = tree;
menuCollapse(0)
//Открытие заданного пункта меню [и закрытие остальных]
$scope.open = menuCollapse
function menuCollapse (id, accordeon) {
var acc = []
function walk(children) {
var i = children.length;
while (i--) {
if (children[i].id == id) {
for (var j = 0, n = acc.length; j < n; j++) {
acc[j].vis = true
}
children[i].vis = !children[i].vis
} else {
if (typeof accordeon === 'undefined' || accordeon === true) children[i].vis = false
acc.push(children[i])
walk(children[i].sub)
acc.pop()
}
}
}
walk(tree);
}
//Редактирование и добавление элемента
$scope.edit = function(item) {
item.editor = !item.editor;
if (!item.editor) {
if (item.id !== null || typeof item.$save == 'function') {
//Сохранение
console.log(item)
/*item.$save();*/
} else {
//Добавление
/*MainMenu.add(function (data) {*/
var data = { //Якобы полученный пустой новый элемент, созданный на сервере
id: 9,
name: '',
editor: false,
res_type: 'page',
vis: false,
sub: []
}
//Находим подмассив в который нужно добавить элемент
function walk(children) {
var i = children.length;
while (i--) {
if (children[i] === item) {
//Добавляем айдишник и введенный текст в новый элемент и сохраняем
var newitem = angular.extend(data, item, {id: data.id})
children.push(angular.copy(blankItem));
children[i] = newitem;
/*newitem.$save()*/
console.log(item)
sort();
return
} else {
walk(children[i].sub)
}
}
}
walk(tree);
/*})*/
}
}
}
//Удаление элемента
$scope.del = function (item) {
//Находим подмассив из которого нужно удалить элемент
function walk(children) {
var i = children.length;
while (i--) {
if (children[i] === item) {
/*children[i].$remove(function (data) {
sort();
});*/
children.splice(i, 1);
sort();
return
} else {
walk(children[i].sub)
}
}
}
walk(tree);
/*typeRes[index].vis = false*/
}
//Добавление пустых элементов
function addBlank () {
function walk(children) {
var i = children.length;
while (i--) {
children[i].editor = false;
walk(children[i].sub)
}
children.push(angular.copy(blankItem));
}
walk(tree);
}
//Сохранение порядка элементов
$scope.sort = sort;
function sort () {
var sortArr = [],//айдишники в порядке расположения
pidsArr = [] //id-элемента: id-родителя
function walk(children, pid) {
for (var i = 0, n = children.length; i < n; i++) {
if (children[i].id) {
sortArr.push(children[i].id)
pidsArr.push(pid)
walk(children[i].sub, children[i].id);
}
}
}
walk($scope.tree, null);
/*MainMenu.save({sort: sortArr, pids: pidsArr}, function (data) {
})*/
$scope.$$phase || $scope.$apply(); //Если область видимости не находится в фазе digest, запускаем её с помощью $apply(). Делается для того, чтобы информация о сортировке сразу же поступила на сервер.
console.log(sortArr)
console.log(pidsArr)
}
}
</script>
</head>
<body ng-app="treeMenu">
<script type="text/ng-template" id="tree_item.html">
<div>
<a href="" ng-hide="item.editor" ng-click="open(item.id)"><b>{{item.name}}</b></a>
<span ng-show="item.editor">
<input type="text" ng-model="item.name" placeholder="Новый пункт"/>
<select ng-model="item.res_type">
<option value="page">страница</option>
<option value="catalog">раздел каталога</option>
</select>
<a href="" ng-click="del(item)">×</a>
</span>
<a href="" ng-model="item.editor" ng-click="edit(item)">ред./сохр.</a>
</div>
<ul ui-sortable="{items:'.sortable',
connectWith: '.menu',
revert: 0,
delay: 100,
distance: 20,
stop: sort}"
ng-model="item.sub"
ng-show="item.vis"
class="menu">
<li ng-repeat="item in item.sub" ng-include="'tree_item.html'" class="sortable"></li>
</ul>
</script>
<div ng-controller="TreeCtrl">
<ul ui-sortable="{items:'.sortable',
connectWith: '.menu',
revert: 0,
delay: 100,
distance: 20,
stop: sort}"
ng-model="tree"
class="menu">
<li ng-repeat="item in tree" ng-include="'tree_item.html'" class="sortable"></li>
</ul>
</div>
</body>
</html>