Цикл статей «Документация jqGrid на русском».
Следующая статья — «TreeGrid Adjacency Model в jqGrid».
Предыдущая статья — «Настройка TreeGrid в jqGrid».
Как было сказано в предыдущей статье «Настройка TreeGrid в jqGrid», одной из важных частей является свойство treeReader. Настройка этого свойства и понимание основных принципов Nested Set Model позволит облегчить вашу жизнь.
Настройка treeReader по умолчанию при использовании Nested Set Model выглядит так:
1 2 3 4 5 6 7 |
treeReader : { level_field: "level", left_field:"lft", right_field: "rgt", leaf_field: "isLeaf", expanded_field: "expanded" } |
treeReader автоматически расширяет colModel этими полями, которые добавляются скрытыми в конец colModel. Данные, возвращаемые сервером, должны включать эти поля для построения таблицы. treeReader может быть расширен так, чтобы эти поля удовлетворяли вашим потребностям.
Свойство: level_field
Тип: number
Это поле определяет уровень в иерархии элемента. Обычно корневой элемент имеет уровень 0. Первый дочерний элемент имеет уровень 1 и так далее. Эта информации нужна таблице для задания отступов каждого элемента.
Свойство: left_field
Тип: number
rowid поля слева.
Свойство: right_field
Тип: number
rowid поля справа.
Свойство: leaf_field
Тип: boolean
Это поле указывает таблице, что этот элемент является листом. Возможные значения: true, false. К элементу-листу присоединяется картинка, и этот элемент может быть развёрнут или свёрнут.
Свойство: expanded_field
Тип: boolean
Указывает таблице, должен ли этот элемент развёртываться при загрузке (true или false). Если этот элемент не имеет значений, то устанавливается false. Данные могут быть пусты для этого элемента, но этот элемент не может быть удалён.
Другое свойство, которое может быть изменено, это tree_root_level. По умолчанию оно имеет значение 0. Это свойство указывает, какой уровень имеет корневой элемент.
Что отправляется на сервер
В случае автозагружаемых узлов отправляются следующие параметры. Расширяется массив postData. Смотрите статью «Настройка TreeGrid в jqGrid».
1 2 3 4 5 6 7 8 |
postData : { ... nodeid:rc.id, n_left:rc.lft, n_right:rc.rgt, n_level:rc.level, ... } |
- nodeid — это id текущей развёрнутой записи.
- n_left — содержит левое значение для текущей развёрнутой строки.
- n_right — содержит значение справа для текущей развёрнутой строки.
- n_level — содержит уровень текущей развёрнутой строки.
Пример
Для того чтобы понимать процесс настройки treeGrid, здесь представлен полный пример:
Подготовка данных
Предположим, что мы имеем таблицу account, где некоторые счета являются дочерними к основным счетам, и некоторые счета не имеют дочерних счетов. В NestedSetModel таблица может выглядеть так:
account_id, name, account_number, Debit, Credit, Balance, lft, rgt
где:
- account_id — уникальный id счета (в нашей таблице это должен быть rowid)
- lft — указывает на поле слева.
- rgt — указывает на поле справа.
В MySQL эта таблица может выглядеть так:
1 2 3 4 5 6 7 8 9 10 11 |
CREATE TABLE accounts ( account_id int(11) NOT NULL AUTO_INCREMENT, name varchar(30) NOT NULL, acc_num varchar(10) NULL, debit decimal(10,2) DEFAULT '0.00', credit decimal(10,2) DEFAULT '0.00', balance decimal(10,2) DEFAULT '0.00', lft int(11) NOT NULL, rgt int(11) NOT NULL, PRIMARY KEY (`account_id`) ); |
Добавим данные:
1 2 3 4 5 6 7 8 |
INSERT INTO accounts VALUES (1, 'Cash', '100', 400.00, 250.00, 150.00, 1, 8); INSERT INTO accounts VALUES (2, 'Cash 1', '1', 300.00, 200.00, 100.00, 2, 5); INSERT INTO accounts VALUES (3, 'Sub Cash 1', '1', 300.00, 200.00, 100.00, 3, 4); INSERT INTO accounts VALUES (4, 'Cash 2', '2', 100.00, 50.00, 50.00, 6, 7); INSERT INTO accounts VALUES (5, 'Bank''s', '200', 1500.00, 1000.00, 500.00, 9, 14); INSERT INTO accounts VALUES (6, 'Bank 1', '1', 500.00, 0.00, 500.00, 10, 11); INSERT INTO accounts VALUES (7, 'Bank 2', '2', 1000.00, 1000.00, 0.00, 12, 13); INSERT INTO accounts VALUES (8, 'Fixed asset', '300', 0.00, 1000.00, -1000.00, 15, 16); |
С этой информацией мы можем построить treeGrid.
Подготовка таблицы
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<script> ... jQuery("#treegrid").jqGrid({ treeGrid: true, treeGridModel: 'nested', ExpandColumn : 'name', url: 'server.php?q=tree', datatype: "xml", mtype: "POST", colNames:["id","Account","Acc Num", "Debit", "Credit","Balance"], colModel:[ {name:'id',index:'id', width:1,hidden:true,key:true}, {name:'name',index:'name', width:180}, {name:'num',index:'acc_num', width:80, align:"center"}, {name:'debit',index:'debit', width:80, align:"right"}, {name:'credit',index:'credit', width:80,align:"right"}, {name:'balance',index:'balance', width:80,align:"right"} ], height:'auto', pager : "#ptreegrid", caption: "Treegrid example" }); ... <script> |
Так как jqGrid сейчас не поддерживает постраничную навигацию, то при использовании treeGrid элементы постраничной навигации будут заблокированы автоматически.
Серверный код — загружаем один раз
Загрузка всех узлов сразу — это упрощение, используемое когда в таблице мало элементов. Для этого наш SQL-запрос может быть таким:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
SELECT node.account_id, node.name, node.acc_num, node.debit, node.credit, node.balance, (COUNT(parent.name) - 1) AS level, node.lft, node.rgt FROM accounts AS node, accounts AS parent WHERE node.lft BETWEEN parent.lft AND parent.rgt GROUP BY node.name ORDER BY node.lft; |
В Nested Set model определение, является ли узел листом очень простое. Для этого нужно просто выполнить сравнение rgt = lft+1 .
Теперь мы готовы для написания серверного кода. Ниже примеры кодов на PHP и MySql для xml и JSON. Смотрите коды, чтобы увидеть, где добавлены дополнительные элементы.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php // Этот запрос определяет общее количество записей в дереве (может быть пропущен) $result = mysql_query("SELECT COUNT(*) as count FROM accounts"); $row = mysql_fetch_array($result,MYSQL_ASSOC); $count = $row['count']; // Получение данных $SQL = "SELECT " ."node.account_id, " ."node.name, " ."node.acc_num, " ."node.debit, " ."node.credit, " ."node.balance, " ."(COUNT(parent.name) - 1) AS level, " ."node.lft, " ."node.rgt " ."FROM accounts AS node, " ."accounts AS parent " ."WHERE node.lft BETWEEN parent.lft AND parent.rgt " ."GROUP BY node.name " ."ORDER BY node.lft"; $result = mysql_query( $SQL ) or die("Couldn’t execute query.".mysql_error()); |
С использованием XML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?php if ( stristr($_SERVER["HTTP_ACCEPT"],"application/xhtml+xml") ) { header("Content-type: application/xhtml+xml;charset=utf-8"); } else { header("Content-type: text/xml;charset=utf-8"); } $s = "<?xml version='1.0' encoding='utf-8'?>"; $s .= "<rows>"; $s .= "<page>1</page>"; $s .= "<total>1</total>"; $s .= "<records>".$count."</records>"; while($row = mysql_fetch_array($result,MYSQL_ASSOC)) { $s .= "<row>"; $s .= "<cell>". $row[account_id]."</cell>"; // id строки указан в colModel. Нет необходимости указывать id здесь. $s .= "<cell>". $row[name]."</cell>"; $s .= "<cell>". $row[acc_num]."</cell>"; $s .= "<cell>". $row[debit]."</cell>"; $s .= "<cell>". $row[credit]."</cell>"; $s .= "<cell>". $row[balance]."</cell>"; $s .= "<cell>". $row[level]."</cell>"; // level element $s .= "<cell>". $row[lft]."</cell>"; // left_field element $s .= "<cell>". $row[rgt]."</cell>"; // right_field element if($row[rgt] == $row[lft]+1) $leaf = 'true';else $leaf='false'; // this determines if the node is aleaf $s .= "<cell>".$leaf."</cell>"; // isLief element $s .= "<cell>false</cell>"; // развёрнутый элемент. Мы ставим в false $s .= "</row>"; } $s .= "</rows>"; echo $s; ?> |
Используя JSON:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
header("Content-type: text/html;charset=utf-8"); $response->page = 1; $response->total = 1; $response->records = $count; $i=0; while($row = mysql_fetch_array($result,MYSQL_ASSOC)) { if($row[rgt] == $row[lft]+1) $leaf = 'true';else $leaf='false'; $response->rows[$i]['cell']=array($row[account_id], $row[name], $row[acc_num], $row[debit], $row[credit], $row[balance], $row[note], $row[level], $row[lft], $row[rgt], $leaf, 'false' ); $i++; } echo json_encode($response); ?> |
Если нужно, чтобы все узлы были развёрнуты после загрузки данных, то нужно установить последнее значение в true.
Серверный код — автоматическая подгрузка дерева
Если есть относительно большие данные с глубокой структурой, то лучше загружать данные только те, которые нужны сейчас, а остальные только при клике на родительскую запись. Поэтому сначала отображаем только корневые элементы. При клике на корневой элемент таблица автоматически пытается загрузить нужную информацию, передавая необходимые параметры на сервер. Здесь level_field и isLeaf очень важны.
В этом случае мы можем использовать предыдущий запрос, которые возвращает только элементы запрошенного уровня. Запрос может быть оптимизирован, но это выходит за рамки этого объяснения.
С помощью JSON:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
<?php $ADDWHERE = ''; $node = (integer)$_REQUEST["nodeid"]; // detect if here we post the data from allready loaded tree // we can make here other checks if( $node >0) { $n_lft = (integer)$_REQUEST["n_left"]; $n_rgt = (integer)$_REQUEST["n_right"]; $n_lvl = (integer)$_REQUEST["n_level"]; $ADDWHERE = " AND lft > ".$n_lft." AND rgt < ".$n_rgt; } else { // initial grid $n_lvl =0; } $SQL1 = "SELECT " ."node.account_id, " ."node.name, " ."node.acc_num, " ."node.debit, " ."node.credit, " ."node.balance, " ."(COUNT(parent.name) - 1) AS level, " ."node.lft, " ."node.rgt " ."FROM accounts AS node, " ."accounts AS parent " ."WHERE node.lft BETWEEN parent.lft AND parent.rgt ".$ADDWHERE ." GROUP BY node.name " ." ORDER BY node.lft"; header("Content-type: text/html;charset=utf-8"); $response->page = 1; $response->total = 1; $response->records = $count; $i=0; while($row = mysql_fetch_array($result,MYSQL_ASSOC)) { if($row[rgt] == $row[lft]+1) $leaf = 'true';else $leaf='false'; if( $n_lvl == $row[level]) { // we output only the needed level $response->rows[$i]['cell']=array($row[account_id], $row[name], $row[acc_num], $row[debit], $row[credit], $row[balance], $row[note], $row[level], $row[lft], $row[rgt], $leaf, 'false' ); } $i++; } echo json_encode($response); ?> |
Цикл статей «Документация jqGrid на русском».
Следующая статья — «TreeGrid Adjacency Model в jqGrid».
Предыдущая статья — «Настройка TreeGrid в jqGrid».