Ordenar los nodos de un TreePanel Más videos
Descripción del tema
La demostración
El día de hoy solamente veremos cómo podemos ordenar las categorías existentes, en el siguiente tutorial prometo mostrar cómo podemos agregar o eliminar categorías. Te recomiendo visitar el ejemplo que he preparado.Ejemplo del tutorial
Material de apoyo
Antes de continuar descarguemos el material de apoyo para este tutorial. Lo descomprimimos y copiamos los archivos a nuestro servidor Web que instalamos al inicio del curso, esto es necesario porque usaremos Ajax, si es necesario cambia las rutas a la librería ExtJS dentro del archivo “tree-order.html”.Creación de la base de datos
Lo primero que haremos será crear la tabla que usaremos para almacenar las categorías, vamos a crear una tabla que se llame “categories” y que contenga los campos “id, id_parent, category, order_number y description”.Descripción de la tabla "categories"
CREATE TABLE categories ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY , id_parent INT NULL , category VARCHAR( 255 ) NOT NULL , order_number INT NULL, description TEXT NOT NULL ) ENGINE = INNODB;Ahora vamos a crear algunas categorías para que podemos probar el funcionamiento del tutorial, aquí dejo unos cuantos “inserts” a la tabla “categories”:
INSERT INTO categories (id, id_parent, category, description) VALUES (NULL, NULL, 'Music', 'The music category'), (NULL, NULL, 'Movies', 'All about movies and stuff'), (NULL, NULL, 'Games', 'Are you a gamer?'), (NULL, 1, 'Classic', 'For those who love a quiet moment'), (NULL, 1, 'Rock', 'Let\'s rock and roll!'), (NULL, 1, 'Pop', 'Do you like pop music?'), (NULL, 2, 'Action', 'Actions movies for everyone'), (NULL, 2, 'Romantic', 'Watch this with your girlfriend'), (NULL, 3, 'Sports', 'Want to play a game?'), (NULL, 3, 'First person shooter', 'Let\'s kill some fools'), (NULL, 3, 'Racing', 'For those who like to drive!'), (NULL, 9, 'Soccer', 'All about foot ball soccer'), (NULL, 9, 'Hockey', 'Ready for the ice?');Aquí lo único interesante es el campo “id_parent”, este campo tendrá el “id” de la categoría “padre”, de esta manera podremos construir una estructura de árbol con “n” cantidad de niveles, los otros campos creo que son muy descriptivos por lo tanto no hablaré de ellos.
Información de prueba
Creación de la estructura de árbol
El siguiente paso es crear una estructura de árbol con los registros que tenemos en nuestra base de datos, he creado una clase en PHP que nos hará la vida mucho más sencilla./** * This class creates a Tree structure of information for the TreePanel component * of the ExtJS library. * * @author Crysfel Villa * @date 12/18/2009 * */ class TreeExtJS{ private $tree = array(); private $index = array(); private $cont = 0; /** * This method inserts a node to the Tree, the child param may contain an * "id" property that will be use as a "key", if the child param doesn't contains * an "id" property a generated "key" is given to the node. * * @child the node to insert * @parentKey(optional) The parent key where the node will be inserted, if null * the node is inserted in the root of the Tree */ public function addChild($child,$parentKey = null){ $key = isset($child["id"])?$child["id"]:'item_'.$this->cont; $child["leaf"] = true; if($this->containsKey($parentKey)){ //added to the existing node $this->index[$key] =& $child; $parent =& $this->index[$parentKey]; if(isset($parent["children"])){ $parent["children"][] =& $child; }else{ $parent["leaf"] = false; $parent["children"] = array(); $parent["children"][] =& $child; } }else{ //added to the root $this->index[$key] =& $child; $this->tree[] =& $child; } $this->cont++; } /** * Return a node by the given key * @key */ public function getNode($key){ return $this->index[key]; } /** * @TODO Remove the node from the Tree * @key */ public function removeNode($key){ //unset($this->index[key]); } /** * Check if exist a node with the given key */ public function containsKey($key){ return isset($this->index[$key]); } /** * Return a representation of the Tree structure in JSON format */ public function toJson(){ return json_encode($this->tree); } }He documentado los métodos que contiene esta clase, te recomiendo copiar/pegar el código en un archivo que se llame “TreeExtJs.class.php” (ya viene en el material de apoyo), la idea de esta clase es ir insertando nodos dentro de algún nodo existente en el árbol o bien a la raíz, una vez construida la estructura podremos generar una representación de esta en formato JSON para que se lo enviemos al componente TreePanel y este pueda desplegarla correctamente. Es importante mencionar que es la primera versión de esta clase, y que en el futuro planeo agregarle más funcionalidades y mejoras, pero por ahora hace lo necesario para este tutorial.
Generar el JSON a desplegar
Ahora tenemos que conectarnos a la base de datos y sacar la información que necesitamos desplegar, vamos a escribir el siguiente código dentro el archivo “tree-order.php”:// Include the Tree class to generate the TreePanel JSON easily include("TreeExtJS.class.php"); // Make a MySQL Connection and select the database $conn = mysql_connect("localhost", "root", "") or die(mysql_error()); mysql_select_db("testing") or die(mysql_error()); // Retrieve all the data from the "categories" table $result = mysql_query("SELECT * FROM categories order by id_parent,order_number asc") or die(mysql_error());Notemos que estamos haciendo uso de “order by” para que las categorías sean regresadas ordenadas por el “id_parent” y “order_number”, esto es de suma importancia ya que de esta manera nos será más sencillo crear la estructura de árbol. Lo siguiente es crear un “array” con la información que hemos sacado de la base de datos, esto lo hacemos de la siguiente manera:
// Create an array of the data $data = array(); while($row = mysql_fetch_array( $result )){ array_push($data,array( "id" => $row["id"], "idParent" => $row["id_parent"], "text" => $row["category"], "orderNumber" => $row["order_number"], "description" => $row["description"] )); }Aquí no hay nada complicado, solamente estamos “populando” la colección “$data” con los registros de la base de datos. Por último necesitamos crear la estructura de árbol haciendo uso de la clase “TreeExtJs”, esto será muy sencillo.
// Creating the Tree $tree = new TreeExtJS(); for($i=0;$i<count($data);$i++){ $category = $data[$i]; $tree->addChild($category,$category["idParent"]); } echo $tree->toJson();Mediante un ciclo vamos recorriendo la información que sacamos de la base de datos y vamos agregando nuevos nodos al árbol, aquí es donde es de suma importancia que vengan ordenados los elementos por el campo “id_parent” ya debemos insertar primero los nodos padre para que al insertar los hijos no se ocasione ningún error. No olvidemos cerrar la conexión a la base de datos.
mysql_close($conn);Hemos terminado la parte más complicada de este tutorial, ahora vamos a escribir el JavaScript necesario para desplegar correctamente la información que creamos en el servidor.
Creación del TreePanel
Editamos el archivo “tree-order.js” y dentro de la función “init” creamos el componente que necesitamos.var tree = new Ext.tree.TreePanel({ border: false, autoScroll:true, dataUrl:'tree-order.php', enableDD: true, // Step 1 root: new Ext.tree.AsyncTreeNode({ text: 'Categories', draggable: false //Step 2 }) }); var win = new Ext.Window({ title:'Reorder the nodes', layout:'fit', width:300, height:400, items: tree }); win.show();Anteriormente hemos visto como crear un TreePanel, en el código anterior solamente hay dos diferencias con respecto a los tutoriales pasados. En el paso uno habilitamos que los nodos puedan ser arrastrados y soltados (Drag and Drop), esto nos permitirá que el usuario pueda reacomodar las categorías a su gusto. En el paso dos le indicamos al nodo “root” que no se pueda arrastrar, esto es importante para que el usuario no pueda mover el nodo principal.
Creación de un TreePanel
Información cargada de la base de datos MySQL
Drag and Drop a los elementos
Restricciones del componente
Guardando el orden
Hasta ahora podemos ordenar los elementos en el TreePanel, pero si actualizamos la página vemos que el orden es el mismo que teníamos al principio, para que los cambios puedan persistir debemos guardar los cambios efectuados en nuestra base de datos. Debemos buscar el evento adecuado para guardar los cambios, podríamos crear un botón “guardar” para que el usuario guarde los cambios cuando crea que es necesario, o bien podríamos hacerlo más transparente al usuario y guardar los cambios automáticamente cuando suceda cualquier movimiento en los nodos. Para este tutorial haremos la segunda opción, para eso debemos utilizar el evento “movenode” que se disparará cuando un nodo cambió de lugar.tree.on('movenode',function(tree,node,oldParent,newParent,index ){ //save the new order });Podemos ver en los parámetros que uno lleva por nombre “newParent”, necesitamos utilizar este parámetro para iterar por todos sus hijos de primera generación, es decir aquellos que sean descendientes directos de él, luego sacamos el “ID” de cada uno de ellos para enviarlos en el orden correcto al servidor y que éste los guarde en la base de datos.
var nodes = []; newParent.eachChild(function(n){ nodes.push(n.attributes.id); //Step 1 }); tree.el.mask('Saving…', 'x-mask-loading'); //Step 2 Ext.Ajax.request({ url: 'tree-order.php', //Step 3 params:{ //Step 4 updateOrder: true, parent: newParent.attributes.id, nodes: nodes.join(',') }, success: function(){ tree.el.unmask(); //Step 5 }, failure: function(){ tree.el.unmask(); //Step 6 Ext.Msg.alert('Error','Error saving the changes'); } });En el paso uno recolectamos los “ID” de cada nodo, esto es necesario para generar el orden que guardaremos en la base de datos. En el paso dos enmascaramos el TreePanel y mostramos un mensaje de “Saving…” esto es importante para que el usuario se dé cuenta que se están guardando sus cambios. En el paso tres indicamos la “url” que procesará la petición Ajax, en este caso usaremos el mismo PHP, pero si estuviésemos usando algún Framework como: CodeIgniter, Ruby on Rails, Groovy and Grails, etc., pondríamos un “controller” diferente. En el paso cuatro definimos los parámetros que enviaremos al servidor cuando se realice la petición Ajax, estos parámetros son “updateOrder” con el cual decidiremos si vamos hacer un “update” o un “select” a la tabla en la base de datos, el parámetro “parent” es importante porque permitiremos que el usuario cambie los nodos inclusive a otras “ramas”, por lo tanto necesitamos saber el padre de los nodos que enviamos en el parámetro “nodos”. El paso cinco se ejecutará si se realizó satisfactoriamente la actualización, aquí solamente quitamos la máscara que pusimos al inicio de la petición Ajax. El paso seis se ejecutará si ha sucedido algún error en el servidor o en la petición, aquí solamente quitamos la máscara y enviamos un mensaje de error al usuario para informarle que los cambios no se guardaron.
Guardando los cambios den la base de datos
// Include the Tree class to generate the TreePanel JSON easily include("TreeExtJS.class.php"); // Make a MySQL Connection and select the database $conn = mysql_connect("localhost", "root", "") or die(mysql_error()); mysql_select_db("testing") or die(mysql_error()); if(isset($_POST["updateOrder"])){ // Step 1 //Aquí vamos a crear el UPDATE a la tabla categories }else{ //aquí dejamos el código necesario para generar el JSON del… // TreePanel que escribimos anteriormente } mysql_close($conn);En el paso uno solamente estamos tomando la decisión de hacer el UPDATE o hacer el SELECT, esto es definido por el parámetro “updateOrder”, si el parámetro existe en el request haremos un UPDATE a la tabla, si no existe entonces se hace todo lo que ya escribimos anteriormente. Para guardar los cambios lo hacemos de la siguiente manera:
$nodes = $_POST["nodes"]; //Step 1 $ids = explode(",",$nodes); $idParent = (int)$_POST["parent"]; //Step 2 for($i=0;$i<count($ids);$i++){ $id = (int)$ids[$i]; $query = sprintf("UPDATE categories SET order_number = %d,id_parent = %d WHERE id = %d", $i,$idParent,$id); //Step 3 mysql_query($query); //Step 4 }Primero tomamos los nodos que editaremos y creamos un arreglo con cada “ID”. El segundo paso es tomar el parámetro “parent” y convertirlo a entero. En el tercer paso creamos el query para actualizar el campo “order_number” y “id_parent”, lo hacemos de esta manera para asegurarnos que no hagan un SQL Injection, los parámetros “id” e “idParent” deben ser enteros. En el paso cuatro ejecutamos el query para que se guarden los cambios correspondientes. Ahora solo queda probar la aplicación, debería ser capaz de mostrar y guardar los cambios efectuados inclusive si la ejecutamos de diferentes computadoras, esto es posible porque la información esta contenida en la base de datos y no solamente en la memoria de nuestra computadora u ordenador.
El orden persiste
Conclusiones
Hemos visto como es muy sencillo ordenar los nodos de un TreePanel, la parte más complicada en mi opinión es crear la estructura de arbol, esto lo había comentado en tutoriales anteriores pero ahora hemos visto como crearla. Recuerda seguirnos en Twitter para estar actualizado de los nuevos tutoriales, tenemos algunas sorpresas para el próximo año que seguramente les gustarán, sigan pendientes y nos vemos en la segunda parte de este tutorial.Te gustaría recibir más tutoriales como este en tu correo?
Este tutorial pertenece al curso Aprendiendo Ext JS 3, te recomiendo revises el resto de los tutoriales ya que están en secuencia de menor a mayor complejidad.
Si deseas recibir más tutoriales como este en tu correo te recomiendo registrarte al curso, si ya eres miembro solo identifícate y registrate al curso, si no eres miembro te puedes registrar gratuitamente!
Si no gustas registrarte en este momento no es necesario! Aún así puedes recibir los nuevos tutoriales en tu correo! Jamás te enviaremos Spam y puedes cancelar tu suscripción en cualquier momento.
Regístrate a este curso
Este tutorial pertenece al curso Aprendiendo Ext JS 3, revisa todos los tutoriales que tenemos en este mismo curso ya que están en secuencia y van de lo más sencillo a lo más complicado.
Tendrás acceso a descargar los videos, códigos y material adicional.
Podrás resolver los ejercicios incluidos en el curso así como los Quizzes.
Llevarás un registro de tu avance.
13Comentarios
¡Genial!
fenomenal, gracias crysfel
crysfel, una pregunta porque al correr el ejemplo no despliega bien los nodos y solo muestra el root "categories". a otra cosa me baje el codigo fuente y como que hace falta algo porque tampoco corre, muchas gracias
Eso es porque alguien movió todos los nodos al root, por ahora el ejemplo no permite transformar una hoja en rama, sería bueno hacer un tut al respecto, por otro lado para ejecutar el ejemplo en tu máquina necesitas un servidor web y una base de datos MySQL, luego cambiar el passwd y usuario para hacer la conexión correctamente. saludos
Que tal Crysfel, Bladimir Balbin se refiere ál demo que tienes como demostración del tutorial ya que no refleja exactamente lo que explicas; como él dice, sólo se muestra el root "categories" con todos los datos en un sólo nivel. En el caso del código fuente que acompaña al tuto, éste no viene con los archivos que en teoría deberían ser, ya que no están los siguientes archivos: - tree-order.js - tree-order.html En lugar de éstos, agregaste dos archivos del tutorial anterior: - tree-show-files.js - tree-show-files.html Debo decir que siguiendo el tuto y creando los archivos no hay ningún problema con el mismo, el detalle estpa sólo en el demo y su código fuente. Saludos!
me surge una duda en este ejemplo, ya que no se en que parte del js debo insertar el codigo para que guarde los cambios en la base de datos... podrias porfavor ayudarme con el codigo fuente de este java script... muchisimas gracias
Soy nuevo con extJs y creo que son buenos tutoriales: paso a paso. En este ejemplo tengo un problema y no funciona. Creo que es problema del parametro que envio a la base de datos. No se porque pero con Tamper veo que envia: node = xnode-3 ¿ no deberia de ser nulo o un numero ?
Excelente tutorial referente a lo que dice Crysfel de convertir una hoja en rama, si tal hoja tine leaf:false dejara que le agreges hojas y se convertira en rama
Si yo Tengo el mismo problema
Hola amigo, una pregunta habra una manera de que pueda obtener el codigo entero de este ejemplo, he estado uniendo el codigo pero me salio al final, y he probado con unos cambios pero no me funciona y me urge mucho correr este ejemplo. Muchas gracias.
Hay un error en los scripts que se descargan, para que funcionen correctamente deben reemplazar en: 'tree-show-files' linea 14: dataUrl:'get-files.php', por: linea 14: dataUrl:'tree-order.php',
Buenas, tengo un problema con un treepanel en el cual tengo un padre y hijo mediante mysql pero quisiera que ese hijo tambien pueda contar con otro hijo adicional y asi los demas. Ojala puedan orientarme aqui pego una parte del codigo en mencion. Gracias. //Building the menu..Creando el Menu function menuMain() { $session_data = $this->session->userdata('logged_in'); $data['username'] = $session_data['username']; $username=$session_data['username']; $this->db->select('permissions.Menu_id as menuId'); $this->db->from('permissions'); $this->db->join('users','permissions.Group_id=users.grupoId'); $this->db->join('menu','permissions.Menu_id = menu.id'); $this->db->where('users.username',$username); $resultdb = $this->db->get(); $folder = array(); if($resultdb->num_rows()!=0){ $in = '('; /* fetch associative array */ foreach($resultdb->result_array() as $user){ $in .= $user['menuId'] . ","; } $in = substr($in, 0, -1).')' ; /* free result set */ $resultdb->free_result(); $resultdb = $this->db->query("SELECT * FROM MENU WHERE parent_id IS NULL AND id in". $in); if ($resultdb->result_array()!=0) { foreach($resultdb->result_array() as $r){ $sqlquery=$this->db->query("SELECT * FROM MENU WHERE parent_id=".$r['id']. " AND id in". $in); // check if have a child node if ($nodes = $sqlquery) { // determine number of rows result set $count = $nodes->num_rows(); if ($count > 0){ // if have a child $r['leaf'] = false; $r['items'] = array(); foreach($nodes->result_array() as $item){ $item['leaf'] = true; $r['items'][] = $item; } } else { // if have no child $r['leaf'] = true; } $folder[] = $r; } } } } echo json_encode(array( "items" => $folder )); } }
Tu pregunta va orientada a como estructurar tu base de datos para guardar esa información? o como generar el arbol desde php?