« Сложные интерфейсы на javascript вместе Yahoo UI. Часть 19 | Наводим порядок в разработке ПО вместе с maven. Часть 1 » |
Сложные интерфейсы на javascript вместе Yahoo UI. Часть 20
Эта статья завершит рассказ о компоненте TreeView. С его помощью мы можем отображать на веб-странице иерархическую информацию в форме дерева. Если для простеньких сайтов мы вполне можем обойтись загрузкой информации из статического источника данных, т.е. данные встроены в саму веб-страницу. То для серьезных приложений, работающих с большими объемами информации, такая методика не подойдет: данные должны загружаться динамически, по мере необходимости – и это тема сегодняшней статьи.Методика создания такого дерева TreeView, которое бы загружало информацию по мере необходимости, а не всю сразу, не особенно отличаются для разных языков или платформ. К примеру всем нам знакомый windows explorer (не браузер). Хотя отображение структуры файловой системы в форме дерева очень удобно, но если бы нужная для него информация загружалась бы сразу и вся, то время запуска explorer-а было бы огромным. Ведь нужно просканировать все диски и все папки на них, найти для каждой папки все файлы (а это сотни и сотни тысяч объектов). Для веб-приложений нужно не только заботиться о минимизации нагрузки на сервер (ведь производительности всегда не хватает, особенно на “дешевых” virtual-ных хостингах), но еще нужно думать и об объеме трафика передаваемого по сети. С другой стороны видеть панацею в методике “грузим информацию по требованию” не стоит. Бич многих новичков, познакомившихся с идеями ajax в том, что они учитывают фактор накладных расходов на обслуживание каждого асинхронного запроса. К примеру, если вы разворачиваете узел дерева, тут же отправляя запрос на сервер за списком дочерних узлов к нему. То, вот вопрос: будет ли это правильным, если количество возвращаемых узлов не велико, скажем 3-5 штук? Ведь каждый запрос на сервер требует время (пусть и не значительное) на установление подключения, на передачу данных, их кодирование и декодирование, не забывайте еще и о затратах времени на, собственно, обработку запроса и формирование ответа (те самые 3-5 узлов). Идеальным вариантом было бы совместить статическое и динамическое наполнение дерева информацией. Когда условная функция “getChildNodes (узел)” (вызываемая динамически с помощью ajax) возвращает не только список узлов непосредственно вложенных внутрь узла “по которому click-нул пользователь и хочет его раскрыть”. Но также, если какой-то из дочерних узлов содержит небольшое количество подчиненных узлов, то и их перечень можно было вы вернуть заранее, не дожидаясь еще одного клика пользователя по дереву. В следующем примере я покажу, как создать TreeView, отображающий информацию из файловой системы. Первым уровнем вложенности будет перечень дисков; для простоты соответствующий набор узлов можно создать вручную, например, так:
tree = new YAHOO.widget.TreeView("treeplaceholder");
tree.setDynamicLoad(loadNodeData, 1);
var drive_d = new YAHOO.widget.TextNode("D:/", tree.getRoot());
var drive_e = new YAHOO.widget.TextNode("E:/", tree.getRoot());
drive_d.isLeaf = false;
drive_e.isLeaf = false;
tree.render ();
function loadNodeData (node, onLoadCompleteHandler){
var n = node;
var path2open = '';
while (n.parent){
path2open = n.label + '/' + path2open
n = n.parent;
}
var callback = {
success: function(oResponse) {
var json = YAHOO.lang.JSON.parse (oResponse.responseText);
for (var i = 0; i < json.length; i++){
var fnode = new YAHOO.widget.TextNode(json[i].file, node);
fnode.isLeaf = json[i].type == 'file' || json[i].children == 0; }
onLoadCompleteHandler();
},
failure: function(oResponse) {
alert (oResponse.responseText);
onLoadCompleteHandler();
}
/*
argument: {
"node": node,
"onLoadCompleteHandler": onLoadCompleteHandler
}
*/
};
YAHOO.util.Connect.asyncRequest('POST', 'loadtree.php' , callback, "path2open="+ path2open );
}
Устройство php-скрипта, формирующего массив файлов внутри каталога тривиально:
$path2open = iconv("UTF-8" , "WINDOWS-1251", $_REQUEST['path2open']);
function scan ($path2open, $level = 0){
$dh = opendir ($path2open);
$final = array ();
while (($file = readdir($dh)) !== false){
if ($file == '..' || $file == '.') continue;
$fullname = $path2open . '/' . $file;
$type = is_dir ($fullname)?'dir':'file';
$children = 0;
if ($type == 'dir' && $level == 0)
$children = count(scan ($fullname, 1)) > 0;
$final [] = array ('file' => iconv("WINDOWS-1251" , "UTF-8", $file), 'type' => $type, 'children' => $children );
}
closedir ($dh);
return $final;
}
print json_encode (scan ($path2open, 0) );
На этой картинке я поймал момент обработки запроса и то, как выглядит узел дерева в промежутке между отправкой запроса и приходом ответа (иконка анимации).
Следующим шагом улучшения примера будет настройка индивидуального внешнего вида для каждого из узлов. Так узлы, соответствующие каталогам должны иметь картинки-иконки в виде папок. А узлы, соответствующие файлам, получат пиктограммы, соответствующие типу файла (т.е. файлы word, excel и архивы будут иметь различный внешний вид). Первая часть задания (с картинками каталогов) решается на счет раз-два: YUI компоненты поддерживают скины, и в составе YUI есть уже подготовленный css-файл “assets/css/folders/tree.css” (он идет вместе с одним из примеров TreeView). Сначала я скопировал всю папку assets с css-стилями и файлами картинок в папку с примером. Затем для удобства я переопределил все css-правила в css-файле следующим образом: было “.ygtvloading” и стало “.folder .ygtvloading”. Хотя YUI имеет стандартизированную методику создания css-скинов и их загрузки с помощью YUI-loader, но в большинстве случаев, когда необходимо на одной странице разместить несколько одинаковых компонентов (те же два TreeView), но при этом имеющие различное оформление, то лучше подправить файлы css-стилей. Например, добавив для всех стилей, используемых для оформления компонента TreeView, префикс (в моем примере “.folder”). Это даст нам возможность избирательно применять css-скины для различных компонентов, просто создав на html-странице блок div с css-классом “folder”, и разместив внутри которого TreeView:
// подключаем стили
<link rel="stylesheet" type="text/css" href="assets/css/folders/tree.css">
<div class="folder"> <!—применяем их -->
<div id="treeplaceholder"></div>
</div>
Хотя TreeView смотрится гораздо лучше, но есть еще куда стремиться. Первое улучшение связано с тем, что в YUI TreeView нет такого стандартного css-стиля. Который назначался бы иконке узла, являющегося каталогом, но при этом не содержащего внутри себя других узлов (хотя это обычная ситуация для файловой системы, когда каталог пустой). К счастью, в YUI предусмотрены инструменты позволяющие настроить внешний вид для каждого из узлов дерева индивидуально. Для этого нужно сразу после создания узла дерева назначить ему свойство “labelStyle” и “contentStyle” (к сожалению, contentStyle может быть применен к узлам типа HtmlNode). Так, вернувшись к старому примеру, я немного поменял код создания узлов дерева. Теперь, если узел соответствует каталогу, но внутри него нет дочерних файлов и подкаталогов, то узел получает специальный css-стиль:
if (json[i].type == 'dir' && json[i].children == 0)
fnode.contentStyle = 'folder_leaf';
.folder_leaf {
background:transparent url(img/folder_leaf.png) no-repeat;
}
.folder_leaf .ygtvlabel {
margin-left: 20px !important;
}
Подводя некоторые итоги и завершая рассказ об YUI, я хотел бы обратить ваше внимание на одну из самых полезных методик по созданию веб-интерфейсов – это создание собственных надстроек над стандартными классами. Дело в том, что даже если мы не создаем классы, расширяющие возможности стандартных компонентов YUI, то все равно объем кода, занимающегося конфигурированием компонентов очень велик. Этот код включает в себя и настройку внешнего вида и функции обработчики различных событий. Объединив все это вместе, мы представляем страницу не в виде: компонент DataTable, настроенный для отображения списка товаров, плюс еще один DataTable, настроенный для … Вместо этого мы создадим js-файл, назовем его goods.js, внутри этого файла создадим собственный класс GoodsTable производный от DataTable. Конструктор такого класса будет принимать в качестве параметра уже не информацию о колонках, их render-ах, ширине и заголовках, а более общие параметры: html элемент страницы, внутрь которого нужно поместить DataTable, путь к php-скрипту поставляющему данные. Т.к. компоненты на странице должны взаимодействовать, то мы создадим собственную систему обмена сообщениями, не тривиальными, вроде “была выделена строка”, а высокоуровневыми или бизнес-событиями. Например, были внесены изменения в запись, или была завершена загрузка информации для очередной страницы DataTable. Таким образом, мы создадим self-contained модули или строительные блоки, из которых можно собрать веб-интерфейс. Эти блоки можно будет тестировать и разрабатывать не зависимо друг от друга, распределять эту работу между несколькими членами команды и самое важное, повторно использовать в нескольких проектах.
« Сложные интерфейсы на javascript вместе Yahoo UI. Часть 19 | Наводим порядок в разработке ПО вместе с maven. Часть 1 » |