Un arbol con jsTree y ASP.NET MVC (II) – Poblando Datos

Luego del primer post de la serie, donde muestro como instalar y configurar jsTree, aqui les mostrare como poblar datos en el arbol.

jsTree, tiene lo que el llama un JSON_DATA plugin, mas informacion referida a esto la pueden encontrar en su documentacion aqui, la siguiente imagen muestra una captura de pantalla de la pagina de documentacion.

image

Como podran observar el tema no es muy explicativo que digamos y las multiples preguntas empiezan a aparecer, como usar jsTree con MVC, como le cargo datos, etc, etc.

El primer paso es definir un contendor que sera el lugar donde el arbol sera renderizado, para ello creamos una vista llamada DemoTree que tiene la siguiente apariencia general y dentro definimos un div, tal como muestro a continuacion, el div e este caso se llama “componentsTree”:

image

Luego de tener el div establecido necesitamos invocar al plugin, para ello utilizamos jsTree y su metodo de inicializacion, tal como se muestra a continuacion:

image

Para que puedan copiar el metodo, se los coloco en su integridad aqui:

1 <script type="text/javascript"> 2 3 $(document).ready(function () { 4 5 $("#componentsTree").jstree({ 6 json_data: { 7 ajax: { 8 //url: $.pathDomain("Puesto/FillComponentsTree"), 9 url: "Puesto/FillComponentsTree", 10 type: "POST", 11 error: function (XMLHttpRequest, textStatus, errorThrown) { 12 }, 13 success: function (data, textStatus, jqXHR) { 14 if ("Saved" in data) { 15 } 16 }, 17 complete: function () { 18 } 19 } 20 }, 21 ui: { select_limit: 1, initially_select: ["#rootComponent"] }, 22 plugins: ["json_data", "ui", "themes"], 23 core: { 24 html_titles: true 25 } 26 27 }).bind("select_node.jstree", function (e, data) { 28 indexNodeSelected = data.rslt.obj.attr('id'); 29 typeNode = data.rslt.obj.attr('typeNode'); 30 featureType = data.rslt.obj.attr('featureType'); 31 32 }).bind("before.jstree", function (e, data) { 33 }); 34 35 }); 36 </script>

El metodo de inicializacion contiene secciones importantes, entre ellas:

  • url: que especifica el Action Method de ASP.NET MVC que sera invocado para cargar el arbol.
  • type: que especifica el tipo de request que se enviara al action method.
  • ui: que especifica valores de inicializacion de jsTree, aqui les propongo la configuracion mas generica que yo utilice. Para mas informacion consultar la documentacion.
  • eventos: que permiten determinar cuando se cargaron correctamente los elementos del arbol y tambien cuando se selecciono un item del arbol

El segundo paso paso es crear un grupo de clases que seran la estructura de datos que espera jsTree, estas clases C#, luego seran serializadas a JSON, lo que proporcionara la informacion que necesita el plugin.

1 public class JsTreeNode 2 { 3 public TreeAttribute attr { get; set; } 4 5 public Data data { get; set; } 6 7 public string state { get; set; } 8 9 public List<JsTreeNode> children { get; set; } 10 } 11 12 public class TreeAttribute 13 { 14 public string id { get; set; } 15 16 public string rel { get; set; } 17 18 public string mdata { get; set; } 19 20 public string TypeNode { get; set; } 21 22 //public FeatureType FeatureType { get; set; } 23 } 24 25 public class Data 26 { 27 public string title { get; set; } 28 29 public string icon { get; set; } 30 }

En tercer lugar y no menos importante es tener una estructura que almacene datos jerarquicamente, esta estructura puede ser cualquiera que Uds tengan actualmente ya sea que provenga de una base de datos o sean simples datos en memoria. Esta estructura puede parecerse a la siguiente a la siguiente:

1 /// <summary> 2 /// Estructura que soporta la construccion de jerarquias. 3 /// </summary> 4 public class TreeElement 5 { 6 //Importante y Requerido 7 public string Id { get; set; } 8 9 //Importante para mostrar el texto 10 public string Description { get; set; } 11 12 //No relevante y es solo un ejemplo de datos adicionales 13 public string ComponentType { get; set; } 14 15 //Importante y requerido 16 public string ParentId { get; set; } 17 }

La estructura arriba mostrada solo almacena un elemento jerarquico y las propiedades mas importantes son Id y ParentId, esta ultima propiedad es solo el identificador del padre del elemento actual. Estos elementos seran almacenados en una lista como veremos posteriormente.

En cuarto lugar les muestro a continuacion, el grupo de metodos que utilice en el controlador, el mas importante: FillComponentsTree.

1 public ActionResult DemoTree() 2 { 3 return View(); 4 } 5 6 [AcceptVerbs(HttpVerbs.Post)] 7 public ActionResult FillComponentsTree() 8 { 9 try 10 { 11 IList<TreeElement> components = GetElements(); 12 var data = RenderTreeView(components); 13 return data; 14 } 15 catch (Exception ex) 16 { 17 return Json(new {Saved = false, Message = ex.Message }); 18 } 19 } 20 21 private IList<TreeElement> GetElements() 22 { 23 IList<TreeElement> elements = null; 24 var service = new PuestoService(); 25 elements = service.GetElementsForTree(); 26 return elements; 27 } 28 29 private ActionResult RenderTreeView(IList<TreeElement> components) 30 { 31 if (components.Count() > 0) 32 { 33 var parent = (from c in components 34 where c.ParentId == null 35 select c).FirstOrDefault(); 36 37 JsTreeNode rootNode = CreateJsTreeNode(parent); 38 PopulateTree(parent, rootNode, components); 39 40 return Json(rootNode); 41 } 42 return Json(new JsTreeNode()); 43 } 44 45 private void PopulateTree(TreeElement parent, JsTreeNode rootNode, IList<TreeElement> components) 46 { 47 var childs = from c in components 48 where c.ParentId == parent.Id 49 select c; 50 51 if (childs.Count() <= 0) 52 rootNode.state = null; 53 54 rootNode.children = new List<JsTreeNode>(); 55 foreach (var c in childs) 56 { 57 JsTreeNode node = CreateJsTreeNode(c); 58 rootNode.children.Add(node); 59 PopulateTree(c, node, components); 60 } 61 } 62 63 private JsTreeNode CreateJsTreeNode(TreeElement nodElement) 64 { 65 //var fileName = PathDomain + "/Content/treeview/icons/" + c.ComponentType + ".png"; 66 JsTreeNode node = new JsTreeNode() 67 { 68 attr = new TreeAttribute() 69 { 70 id = nodElement.Id, 71 TypeNode = nodElement.ComponentType, 72 //FeatureType = c.FeatureTypesSupported, 73 //rel = c.ToJSON(), 74 mdata = "{ draggable : true, max_children : 100, max_depth : 100 }" 75 }, 76 data = new Data 77 { 78 title = nodElement.Description 79 //icon = System.IO.File.Exists(Server.MapPath("~/Content/treeview/icons/" + parent.ComponentType + ".png")) ? fileName : PathDomain + "/Content/treeview/icons/generic_icon.png" 80 }, 81 state = "open" 82 }; 83 return node; 84 }

Finalmente para los que han podido seguirme hasta este punto, hay una clase llamada PuestoService y que es la que proporciona el metodo GetElementsForTree, este metodo cargara, dinamicamente o estaticamente el arbol de la siguiente manera:

1 public class PuestoService 2 { 3 public IList<TreeElement> GetElementsForTree() 4 { 5 List<TreeElement> arbol = new List<TreeElement>(); 6 arbol.Add(new TreeElement { Id = "1", Description = "Raiz", ParentId = null, ComponentType = "None" }); 7 arbol.Add(new TreeElement { Id = "2", Description = "Mamiferos", ParentId = "1", ComponentType = "1" }); 8 arbol.Add(new TreeElement { Id = "3", Description = "Carnivoros", ParentId = "1", ComponentType = "2" }); 9 arbol.Add(new TreeElement { Id = "4", Description = "Ballena", ParentId = "2", ComponentType = "X" }); 10 arbol.Add(new TreeElement { Id = "5", Description = "Lobo", ParentId = "3", ComponentType = "Z" }); 11 arbol.Add(new TreeElement { Id = "6", Description = "Delfin", ParentId = "2", ComponentType = "X" }); 12 arbol.Add(new TreeElement { Id = "7", Description = "Orca", ParentId = "2", ComponentType = "Z" }); 13 arbol.Add(new TreeElement { Id = "8", Description = "Delfin Botella", ParentId = "6", ComponentType = "E" }); 14 return arbol; 15 } 16 }

La clase PuestoService, puede ser tan compleja como quieran y podria estar recuperando los datos desde una base de datos directamente o quiza utilizar un servicio WCF para tal fin, en este caso nuevamente en aras de mantener la simplicidad cargamos datos estaticamente.

El arbol se muestra tal como en la siguiente captura:

image

Hasta el momento todo esta muy bien, todos los datos definidos estaticamente aparecen practicamente de inmediato, sin embargo los problemas pueden aparecer si el arbol tiene ingentes cantidades de datos, esto podria retrasar considerablemente la renderizacion del arbol y de la aplicacion en general y justamente este problema sera atacado en el ultimo articulo de la serie, prontamente.

Saludos

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *