init
This commit is contained in:
parent
06f61d8ce8
commit
f301cb744d
2271 changed files with 103162 additions and 0 deletions
|
@ -0,0 +1,76 @@
|
|||
# Получение данных
|
||||
|
||||
Данные регионов можно хранить в разном виде.
|
||||
|
||||
Наиболее естественное представление дерева -- в виде вложенного объекта: свойство `children` содержит поддеревья.
|
||||
|
||||
Выглядит это так:
|
||||
|
||||
```js
|
||||
var regions = [
|
||||
{
|
||||
title: 'Россия',
|
||||
id: 1,
|
||||
children: [
|
||||
{
|
||||
title: 'Центр',
|
||||
id: 2,
|
||||
children: [ ... поддеревья ... ]
|
||||
},
|
||||
...
|
||||
}
|
||||
}
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
У такого вложенного объекта есть важный недостаток: сложно перейти напрямую к узлу по ID. Нужно "прыгать" по дереву.
|
||||
|
||||
Поэтому может быть более удобен другой вариант:
|
||||
|
||||
```js
|
||||
var regions = [
|
||||
{
|
||||
title: 'Россия',
|
||||
id: 1,
|
||||
children: [ 2 ]
|
||||
},
|
||||
{
|
||||
title: 'Центр',
|
||||
id: 2,
|
||||
children: [ ... ]
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
..То есть, массив содержит все узлы дерева, и каждый узел хранит в `children` список `id` детей.
|
||||
|
||||
Но и это не совсем удобно. Ведь хочется по ID получить данные. Значит, нужно хранить не массив, а объект вида `id => { title: .., id: .., children: [... ] }`.
|
||||
|
||||
<ul>
|
||||
<li>Свойство `children` может отсутствовать, если детей нет.</li>
|
||||
<li>`id` есть и в ключе объекта и в данных узла. Так удобнее.</li>
|
||||
<li>Список внешних узлов дерева содержится в корневом узле без имени, с `id = 0`.</li>
|
||||
</ul>
|
||||
|
||||
**Выберите наиболее симпатичную структуру и получите её из исходного документа.**
|
||||
|
||||
# Данные
|
||||
|
||||
Скрипт для получения данных в последнем формате, описанном выше: [fetch.js](/files/tutorial/widgets/checkbox-tree/fetch.js).
|
||||
|
||||
Результат в файле (после `JSON.stringify`): [regions.js](/files/tutorial/widgets/checkbox-tree/regions.js).
|
||||
|
||||
# Исправления
|
||||
|
||||
Желательно сделать следующие исправления:
|
||||
|
||||
<ul>
|
||||
<li>Переделать вёрстку. В частности, заменить текстовые + - на оформление при помощи CSS.</li>
|
||||
<li>Оформить всё в виде виджета с шаблоном.</li>
|
||||
<li>Использовать ленивый рендеринг! Не рисовать всё дерево сразу (зачем рисовать то, чего не видно?), а дорисовывать при открытии.</li>
|
||||
|
||||
# Решение
|
||||
|
||||
[edit src="solution"/]
|
1
02-ui/05-widgets/08-widget-tasks-2/05-tree-checkboxes/solution/.plnkr
Executable file
1
02-ui/05-widgets/08-widget-tasks-2/05-tree-checkboxes/solution/.plnkr
Executable file
|
@ -0,0 +1 @@
|
|||
{"name":"checkbox-tree","plunk":"XexJRV6rO0w15179jqGC"}
|
23
02-ui/05-widgets/08-widget-tasks-2/05-tree-checkboxes/solution/fetch.js
Executable file
23
02-ui/05-widgets/08-widget-tasks-2/05-tree-checkboxes/solution/fetch.js
Executable file
|
@ -0,0 +1,23 @@
|
|||
var result = {
|
||||
0: { children: [] }
|
||||
};
|
||||
|
||||
$('div[id^="region"]').each(function() {
|
||||
el = $(this);
|
||||
var id = el.attr('id').slice(6);
|
||||
result[id] = {
|
||||
title: el.children('label').html(),
|
||||
children: [],
|
||||
id: id
|
||||
};
|
||||
|
||||
var parent = el.parent().closest('div[id^="region"]');
|
||||
if (parent.length) {
|
||||
var pid = parent.attr('id').slice(6);
|
||||
} else {
|
||||
pid = 0;
|
||||
}
|
||||
|
||||
result[pid].children.push(+id);
|
||||
|
||||
});
|
75
02-ui/05-widgets/08-widget-tasks-2/05-tree-checkboxes/solution/index.html
Executable file
75
02-ui/05-widgets/08-widget-tasks-2/05-tree-checkboxes/solution/index.html
Executable file
|
@ -0,0 +1,75 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link href="tree.css" rel="stylesheet">
|
||||
|
||||
<script src="http://code.jquery.com/jquery.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
|
||||
<script src="regions.js"></script>
|
||||
<script src="tree.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script id="tree-template" type="text/template">
|
||||
<ul>
|
||||
<% $(children).each(function() {
|
||||
var nodeData = data[this];
|
||||
var hasChildren = nodeData.children && nodeData.children.length;
|
||||
%>
|
||||
<li data-id="<%=nodeData.id%>" class="<%=hasChildren ? 'tree-closed' : ''%>">
|
||||
<% if (hasChildren) { %> <span class="tree-toggler"></span> <% } %>
|
||||
<input type="checkbox" value="<%=nodeData.id%>">
|
||||
<%=nodeData.title%>
|
||||
</li>
|
||||
<% }); %>
|
||||
</ul>
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// требование - как можно меньше DOM-элементов
|
||||
// работа с данными из 1000 узлов
|
||||
/* пример структуры данных
|
||||
var data = [
|
||||
{
|
||||
children: [1,2,3]
|
||||
},
|
||||
{
|
||||
title: 'Россия',
|
||||
id: 1,
|
||||
children: [4,5]
|
||||
},
|
||||
{
|
||||
title: 'Украина',
|
||||
id: 2
|
||||
},
|
||||
{
|
||||
title: 'Беларусь',
|
||||
id: 3
|
||||
},
|
||||
{
|
||||
title: 'Север',
|
||||
id: 4
|
||||
},
|
||||
{
|
||||
title: 'Юг',
|
||||
id: 5
|
||||
}
|
||||
|
||||
];
|
||||
*/
|
||||
|
||||
var tree = new Tree({
|
||||
template: _.template($('#tree-template').html().trim()),
|
||||
data: regions
|
||||
});
|
||||
|
||||
tree.getElement().appendTo('body');
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
37
02-ui/05-widgets/08-widget-tasks-2/05-tree-checkboxes/solution/tree.css
Executable file
37
02-ui/05-widgets/08-widget-tasks-2/05-tree-checkboxes/solution/tree.css
Executable file
|
@ -0,0 +1,37 @@
|
|||
.tree, .tree ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.tree .tree-open ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
.tree .tree-closed ul {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tree-toggler {
|
||||
color: blue;
|
||||
cursor: pointer;
|
||||
float: left;
|
||||
margin-left: -16px;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.tree-open .tree-toggler {
|
||||
background: url(http://js.cx/tree/minus.gif) no-repeat center;
|
||||
}
|
||||
|
||||
|
||||
.tree-closed .tree-toggler {
|
||||
background: url(http://js.cx/tree/plus.gif) no-repeat center;
|
||||
}
|
||||
|
||||
.tree li {
|
||||
line-height: 1em;
|
||||
padding-left: 16px;
|
||||
}
|
74
02-ui/05-widgets/08-widget-tasks-2/05-tree-checkboxes/solution/tree.js
Executable file
74
02-ui/05-widgets/08-widget-tasks-2/05-tree-checkboxes/solution/tree.js
Executable file
|
@ -0,0 +1,74 @@
|
|||
|
||||
function Tree(options) {
|
||||
var self = this;
|
||||
|
||||
var template = options.template;
|
||||
|
||||
var data = options.data;
|
||||
|
||||
this.getElement = function() {
|
||||
if (!this.elem) render();
|
||||
return this.elem;
|
||||
}
|
||||
|
||||
function onTogglerClick(e) {
|
||||
self.toggle( $(e.currentTarget.parentNode) );
|
||||
}
|
||||
|
||||
function onCheckboxChange(e) {
|
||||
var checked = e.target.checked;
|
||||
var id = e.target.value;
|
||||
var node = $(e.currentTarget.parentNode);
|
||||
|
||||
if (checked) {
|
||||
self.open(node);
|
||||
}
|
||||
|
||||
node.find('input').prop('checked', checked);
|
||||
}
|
||||
|
||||
this.toggle = function(node) {
|
||||
ensureChildrenRendered(node);
|
||||
node.toggleClass("tree-open tree-closed");
|
||||
};
|
||||
|
||||
this.open = function(node) {
|
||||
ensureChildrenRendered(node);
|
||||
node.addClass("tree-open").removeClass("tree-closed");
|
||||
}
|
||||
|
||||
this.close = function(node) {
|
||||
ensureChildrenRendered(node);
|
||||
node.addClass("tree-closed").removeClass("tree-open");
|
||||
}
|
||||
|
||||
function ensureChildrenRendered(node) {
|
||||
var ul = node.children('ul');
|
||||
|
||||
if (!ul.length) {
|
||||
renderChildren( node.data('id') ).appendTo(node);
|
||||
|
||||
if (node.children('input').is(':checked')) {
|
||||
node.find('input').prop('checked', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderChildren(id) {
|
||||
return $(template({
|
||||
children: data[id].children,
|
||||
data: data
|
||||
}));
|
||||
}
|
||||
|
||||
function render() {
|
||||
|
||||
self.elem = renderChildren(0)
|
||||
.addClass('tree');
|
||||
|
||||
self.elem.on('click', '.tree-toggler', onTogglerClick);
|
||||
|
||||
self.elem.on('change', 'input', onCheckboxChange);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
# Дерево с чекбоксами
|
||||
|
||||
[importance 5]
|
||||
|
||||
Вы получили проект, в котором есть дерево с чекбоксами.
|
||||
|
||||
К сожалению, оно "кривое" -- некрасивое, плохо закодированное и с ошибками. Ваша задача -- сделать "как надо".
|
||||
|
||||
Образец:
|
||||
|
||||
[iframe src="task" height=400 link edit border=1]
|
||||
|
||||
Особенности работы:
|
||||
<ul>
|
||||
<li>При изначальном рисовании все элементы закрыты и не отмечены.</li>
|
||||
<li>Установка отметки в чекбоксе раскрывает узел и отмечает все элементы под ним.</li>
|
||||
<li>Снятие отметки из чекбокса -- снимает отметки из всех элементов под ним.</li>
|
||||
</ul>
|
||||
|
||||
Требования к реализации:
|
||||
<ul>
|
||||
<li>Все данные для дерева (регионы) нужно хранить в JS. Как именно -- определяете вы сами. Можно в одном объекте, можно -- ещё как-то.</li>
|
||||
<li>Программист, который делал этот код, сейчас недоступен. Ваша задача -- восстановить данные регионов из дерева примера.</li>
|
||||
<li>Можно считать, что название региона всегда занимает только 1 строку.</li>
|
||||
<li>Дерево должно легко работать с 1000 узлов (в примере их около 450).</li>
|
||||
<li>Используйте минимум DOM-элементов.</li>
|
||||
</ul>
|
||||
|
||||
Исходный документ содержит "образец" дерева.
|
||||
|
||||
[edit task src="task"/]
|
||||
|
||||
P.S. При получении данных из дерева-образца вам поможет `JSON.stringify(объект)`. Он превратит объект в строку, которую можно положить в файл.
|
1
02-ui/05-widgets/08-widget-tasks-2/05-tree-checkboxes/task/.plnkr
Executable file
1
02-ui/05-widgets/08-widget-tasks-2/05-tree-checkboxes/task/.plnkr
Executable file
|
@ -0,0 +1 @@
|
|||
{"name":"checkbox-tree-src","plunk":"Hdhnty2bvLMMM0VH9jjn"}
|
|
@ -0,0 +1 @@
|
|||
{"name":"checkbox-tree-src","plunk":"Hdhnty2bvLMMM0VH9jjn"}
|
File diff suppressed because one or more lines are too long
356
02-ui/05-widgets/08-widget-tasks-2/05-tree-checkboxes/task/index.html
Executable file
356
02-ui/05-widgets/08-widget-tasks-2/05-tree-checkboxes/task/index.html
Executable file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue