Цикл статей «Документация jqGrid на русском».
Следующая статья — «Непрокручиваемые колонки в jqGrid (Frozen columns)».
Предыдущая статья — «TreeGrid Nested Set Model в jqGrid».
Как было расписано в статье «Настройка TreeGrid в jqGrid», одно из важнейших свойств — это treeGrid. Настройка этого свойства и понимание базовых принципов Adjacency Model облегчат вашу жизнь.
Настройка treeGrid
Настройка treeReader по умолчанию при использовании treeGrid с Adjacency Model:
1 2 3 4 5 6 |
treeReader = { level_field: "level", parent_id_field: "parent", // then why does your table use "parent_id"? leaf_field: "isLeaf", expanded_field: "expanded" } |
treeReader автоматически расширяет colModel этими полями, добавляя их скрытыми в конец colModel. Данные, возвращаемые сервером, должны включать эти поля. treeReader может быть расширен так, чтобы эти поля соответствовали вашим потребностям.
Единственное отличие от Nested Set Model в том, что left_field и right_field заменены parent_id_field. Этот элемент указывает, что запись имеет родительскую запись с id равным parent_id_field. Если родительский id равен NULL, то элемент является корневым элементом.
Свойство: level_field
Тип: number
Это свойство определяет уровень иерархии элемента. Обычно корневые элементы имеют уровень 0. Первый дочерний элемент имеет уровень 1 и так далее. Эта информация нужна для установки смещения для каждого элемента.
Свойство: parent_id_field
Тип: смешанный
Указывает, что запись родительскую запись с id равным parent_id_field. Если id родительской записи NULL, то этот элемент является корневым элементом.
Свойство: leaf_field
Тип: boolean
Это свойство указывает, что элемент является листом. Возможные значения: true, false. К листьям присоединена другая картинка, и листья не могут быть развёрнуты и свёрнуты.
Свойство: expanded_field
Тип: boolean
Указывает таблице, должен ли элемент быть развёрнут при загрузке (true или false). Если не указано, то используется false. Данные могут быть пусты для этого элемента, но этот элемент не может быть удалён.
Что отправляется на сервер
В случае автоматической загрузки узлов дерева отправляются следующие параметры (также расширяется postData). Более подробно можно почитать в статье «Настройка TreeGrid в jqGrid».
1 2 3 4 5 6 7 |
postData : { ... nodeid:rc.id, parentid:rc.parent_id, n_level:rc.level ... } |
- nodeid — это id текущей развёрнутой записи.
- parentid — id родительской записи для текущей развёрнутой строки.
- n_level — уровень иерархии текущей развёрнутой строки.
Пример
Подготовка данных
Давайте предположим, что у нас есть таблица account, где некоторые счета являются дочерними к главным счетам, и некоторые счета не имеют дочерних счетов. В Adjacency model таблица может выглядеть так:
account_id, name, account_number, Debit, Credit, Balance, parent_id
где:
- account_id — уникальный id счёта (в нашей таблице это должен быть rowid).
- parent_id — id родительского счёта, то есть для нас свойство parent_id_field.
В синтаксисе MySQL эта таблица будет выглядеть так:
1 2 3 4 5 6 7 8 9 10 |
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', parent_id int(11) DEFAULT 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, NULL); INSERT INTO accounts VALUES (2, 'Cash 1', '1', 300.00, 200.00, 100.00, 1); INSERT INTO accounts VALUES (3, 'Sub Cash 1', '1', 300.00, 200.00, 100.00, 2); INSERT INTO accounts VALUES (4, 'Cash 2', '2', 100.00, 50.00, 50.00, 1); INSERT INTO accounts VALUES (5, 'Bank''s', '200', 1500.00, 1000.00, 500.00,NULL); INSERT INTO accounts VALUES (6, 'Bank 1', '1', 500.00, 0.00, 500.00, 5); INSERT INTO accounts VALUES (7, 'Bank 2', '2', 1000.00, 1000.00, 0.00, 5); INSERT INTO accounts VALUES (8, 'Fixed asset', '300', 0.00, 1000.00, -1000.00, NULL); |
С этими данными мы можем построить 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: 'adjacency', 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> |
Серверный код. Загрузка всех узлов сразу
Загрузка всех узлов сразу работает хорошо при относительно небольшом количестве элементов и небольшом количестве уровней дерева.
Загрузка данных в Adjacency model немного более сложная, так как она требует рекурсии, и при большой глубине дерева может потребовать большого количества времени. Есть некоторые технологии, решающие эту проблему, но в нашем случае мы будем использовать стандартный способ. Автоматическая загрузка узлов дерева (описана ниже) более проста и не требует рекурсии.
Используя 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
<?php // Сначала определяем листья $SQLL = "SELECT t1.account_id FROM accounts AS t1 LEFT JOIN accounts as t2 " ." ON t1.account_id = t2.parent_id WHERE t2.account_id IS NULL"; $result = mysql_query( $SQLL ) or die("Couldn t execute query.".mysql_error()); $leafnodes = array(); while($rw = mysql_fetch_array($result,MYSQL_ASSOC)) { $leafnodes[$rw[account_id]] = $rw[account_id]; } // Рекурсивная функция, которая выполяет всю работу. function display_node($parent, $level) { global $leafnodes; if($parent >0) { $wh = 'parent_id='.$parent; } else { $wh = 'ISNULL(parent_id)'; } $SQL = "SELECT account_id, name, acc_num, debit, credit, balance, parent_id FROM accounts WHERE ".$wh; $result = mysql_query( $SQL ) or die("Couldn t execute query.".mysql_error()); while($row = mysql_fetch_array($result,MYSQL_ASSOC)) { echo "<row>"; echo "<cell>". $row[account_id]."</cell>"; echo "<cell>". $row[name]."</cell>"; echo "<cell>". $row[acc_num]."</cell>"; echo "<cell>". $row[debit]."</cell>"; echo "<cell>". $row[credit]."</cell>"; echo "<cell>". $row[balance]."</cell>"; echo "<cell>". $level."</cell>"; if(!$row[parent_id]) $valp = 'NULL'; else $valp = $row[parent_id]; // parent field echo "<cell><![CDATA[".$valp."]]></cell>"; if($row[account_id] == $leafnodes[$row[account_id]]) $leaf='true'; else $leaf = 'false'; // isLeaf comparation echo "<cell>".$leaf."</cell>"; // isLeaf field echo "<cell>false</cell>"; // expanded field echo "</row>"; // recursion display_node((integer)$row[account_id],$level+1); } } 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"); } $et = ">"; echo "<?xml version='1.0' encoding='utf-8'?$et\n"; echo "<rows>"; echo "<page>1</page>"; echo "<total>1</total>"; echo "<records>1</records>"; // Вызываем функцию на корневом уровне display_node('',0); echo "</rows>"; ?> |
Серверный код. Автоматическая загрузка дерева.
Автоматическая загрузка дерева рекомендуется при использовании adjacency model в jqGrid. Здесь можно сделать простой запрос без всяких рекурсий.
Используя 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
<?php // Определяем узлы дерева $SQLL = "SELECT t1.account_id FROM accounts AS t1 LEFT JOIN accounts as t2 " ." ON t1.account_id = t2.parent_id WHERE t2.account_id IS NULL"; $resultl = mysql_query( $SQLL ) or die("Couldn t execute query.".mysql_error()); $leafnodes = array(); while($rw = mysql_fetch_array($resultl,MYSQL_ASSOC)) { $leafnodes[$rw[account_id]] = $rw[account_id]; } // Получаем параметры из таблицы $node = (integer)$_REQUEST["nodeid"]; $n_lvl = (integer)$_REQUEST["n_level"]; 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"); } $et = ">"; echo "<?xml version='1.0' encoding='utf-8'?$et\n"; echo "<rows>"; echo "<page>1</page>"; echo "<total>1</total>"; echo "<records>1</records>"; if($node >0) { check to see which node to load $wh = 'parent_id='.$node; // родители $n_lvl = $n_lvl+1; // вывод следующего уровня } else { $wh = 'ISNULL(parent_id)'; // корни } $SQL = "SELECT account_id, name, acc_num, debit, credit, balance, parent_id FROM accounts WHERE ".$wh; $result = mysql_query( $SQL ) or die("Couldn t execute query.".mysql_error()); while($row = mysql_fetch_array($result,MYSQL_ASSOC)) { echo "<row>"; echo "<cell>". $row[account_id]."</cell>"; echo "<cell>". $row[name]."</cell>"; echo "<cell>". $row[acc_num]."</cell>"; echo "<cell>". $row[debit]."</cell>"; echo "<cell>". $row[credit]."</cell>"; echo "<cell>". $row[balance]."</cell>"; echo "<cell>". $n_lvl."</cell>"; if(!$row[parent_id]) $valp = 'NULL'; else $valp = $row[parent_id]; echo "<cell><![CDATA[".$valp."]]></cell>"; if($row[account_id] == $leafnodes[$row[account_id]]) $leaf='true'; else $leaf = 'false'; echo "<cell>".$leaf."</cell>"; echo "<cell>false</cell>"; echo "</row>"; } echo "</rows>"; ?> |
adjacency [əˈdʒeɪs(ə)nsɪ] — соседство, смежность, примыкание, близость
Цикл статей «Документация jqGrid на русском».
Следующая статья — «Непрокручиваемые колонки в jqGrid (Frozen columns)».
Предыдущая статья — «TreeGrid Nested Set Model в jqGrid».
Добрый день! Пробовал пример, описанный в документации и здесь adjacency модель. Но дерево не раскрывается. Пробовал оба примера из вашей статьи. Раскрывается только первый уровень, второй уровень не раскрывается, даже запрос не отправляется. В чем может быть проблема? Использую jqgrid 4.7.
Я бы порекомендовал попробовать обновить jqGrid и проверить там. Вполне возможно, что в в вашей версии можно было использовать только один уровень. Но так особых проблем не должно возникать, вроде.