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

Un arbol con jsTree y ASP.NET MVC (I) – Instalacion

Hace ya bastante tiempo que vengo trabajando con ASP.NET MVC y jQuery y una de las necesidades que tuve fue la de mostrar una estructura jerarquica como un arbol. En ASP.NET Webforms lo teniamos un poco mas simple porque el framework incorpora un componente Tree, pero como no estamos con WebForms y prometimos no mirar atras, tuve que buscar una alternativa jQuery que me permita hacer lo que busco y aunque existen varias alternativas, algunas por el lado de google, que no necesariamente son jQuery-friendly, me fui por jsTree.

Dejenme decir que este plugin es uno que no tiene una buena documentacion, tiene poco mantenimiento y esta bastante “alejado” del camino que tomo jQuery hace unos anios, pero aun asi decidi usarlo por su excelente performance y por darme excelentes resultados de satisfaccion con el cliente y practicamente cero reclamos con relacion a sus capacidades.

Bueno aqui les doy los pasos que debemos seguir para configurarlo, la descarga la pueden realizar desde esta pagina, y aunque el archivo que descargues tiene instrucciones de instalacion, estas son parcas o escuetas, entonces yo recomiendo instalar de la siguiente manera:

Los elementos mas importantes en la instalacion son estos dos que estan marcados

image

En el proyecto de ASP.NET MVC deben insertar los archivos en la siguiente estructura:

image

Aqui pueden empezar las observaciones, porque desde todo punto de vista el colocar imagenes y temas en la carpeta de scripts deja mucho que desear, pero este setup es el setup por defecto, para evitar hacer esto deberiamos hacer una modificacion en el archivo .js, pero eso va tambien en contra de lo que quisiera hacer, asi que en aras de mantener la simplicidad no modifiquemos nada y coloquemos los archivos como les sugiero (por ahora)

Finalmente en el archivo _Layout.cshtml, que hace las veces de “MasterPage” colocamos la siguiente linea:

image

Con todo esto ya tenemos listo el plugin de jsTree para ser utilizado.

En el siguiente articulo muestro como utilizarlo para mostrar datos en el arbol.

Un saludo