jueves, 22 de enero de 2015

TreeList con Kendo UI for ASP.NET MVC

Este post va dirigido a los que hemos trabajado con Kendo UI para ASP.NET MVC y no hemos podido hacer un simple TreeList en menos de 1 hora.

Primero, tenía instalada la versión de Kendo 2014.2.1008 y escribía en mi vista:
@(Html.Kendo().TreeList

Y arrojaba error ya que no encontraba el .TreeList, luego viendo lo ejemplo de otras versiones me di cuenta que está incluido en la versión 2014.3.1316 por lo que recomiendo bajar esta. Además que actualmente es la última (por lo que averigüé el TreeList se agregó a Kendo en la versión 2014.3.1119).

La documentación es mucha, de hecho me perdí tanto leer, al menos en Telerik encontré:

http://docs.telerik.com/kendo-ui/api/javascript/ui/treelist
http://docs.telerik.com/kendo-ui/aspnet-mvc/helpers/treelist/overview
http://demos.telerik.com/kendo-ui/treelist/index

Luego tenemos los ejemplos que vienen en el ZIP del Kendo comercial (pagado):

- telerik.ui.for.aspnetmvc.2014.3.1316.commercial.zip, para MVC/ASPX, tiene muchos ejemplos, uno de TreeList que es muy complejo quizá para alguien que quiere hacer su primer TreeList.
- telerik.kendoui.professional.2014.3.1316.commercial.zip, para JQuery, con otros muchos ejemplos pero el código de los controladores se leen de un servicio en línea por ejemplo:
var service = "http://demos.telerik.com/kendo-ui/service";

Luego de ver mucho código, partí de cero y era más simple de lo que pensaba.

Primero tenemos una vista que llama a otra vista Header para la carga de los JS/CSS necesario, como JQuery/Kendo:
<! DOCTYPE html >
<html>
<head>
    @Html.Partial("~/HeaderCssJs.cshtml")
</head>

    

    <div class="demo-section k-header">
        @(Html.Kendo().TreeList<models .miviewmodel="">()
        .Name("treelist")
        .Columns(columns =>
        {
            columns.Add().Field(e => e.FirstName).Width(220).TemplateId("photo-template");
            columns.Add().Field(e => e.LastName).Width(160);
            columns.Add().Field(e => e.Address).Title("Address");            
        })
        .Filterable()
        .Sortable()
        .DataSource(dataSource => dataSource
            .Read(read => read.Action("Read", "Home"))
            .ServerOperation(false)
            .Model(m =>
            {
                m.Id(f => f.EmployeeId);
                m.ParentId(f => f.ReportsTo);
                m.Field(f => f.FirstName);
                m.Field(f => f.LastName);
                m.Field(f => f.ReportsTo);
            })
        )
        .Height(540)
        )
    </div>
<style>
        .employee-photo {
            display: inline-block;
            width: 40px;
            height: 40px;
            border-radius: 50%;
            background-size: 40px 44px;
            background-position: center center;
            vertical-align: middle;
            line-height: 41px;
            box-shadow: inset 0 0 1px #999, inset 0 0 10px rgba(0,0,0,.2);
        }

        .employee-name {
            display: inline-block;
            vertical-align: middle;
            line-height: 41px;
            padding-left: 10px;
        }
    </style>
</body>
En el Controlador tenemos 3 actions.

El que carga inicialmente la Vista:
public ActionResult Index()
{
    return View();
}
El Reader que se llama desde el Evento del TreeList:
public JsonResult Read([DataSourceRequest] DataSourceRequest request)
{
   var result = GetDetalle().ToTreeDataSourceResult(request);           
   return Json(result, JsonRequestBehavior.AllowGet);
}
El que carga el enumerador. Lo mejor es que no importa el orden que se hagan los Add, el Tree List de Kendo se encarga de ordenar los hijos:
private IEnumerable GetDetalle()
{
    List<MiViewModel> lista = new List< MiViewModel >();
    lista.Add(new MiViewModel () { EmployeeId = 9, FirstName = "xxxxx", LastName = "yyy", hasChildren = false, ReportsTo = 3, Address = "999", Color = "rojo" });
    lista.Add(new MiViewModel () { EmployeeId = 1, FirstName = "naldo", LastName = "Gonz", hasChildren = true, ReportsTo = null, Address = "123", Color = "rojo" });
    lista.Add(new MiViewModel () { EmployeeId = 2, FirstName = "pepe", LastName = "Super", hasChildren = false, ReportsTo = 1, Address = "456", Color = "rojo" });
    lista.Add(new MiViewModel () { EmployeeId = 3, FirstName = "juan", LastName = "Man", hasChildren = false, ReportsTo = null, Address = "789", Color = "verde" });
    return from a in lista
           select a;
}
En mi caso tengo una carpeta “img” con dos imágenes “rojo.jpg” y “verde.jpg”.

Por ultimo la clase MiViewModel:
using System.Web;

namespace Models
{
    public class MiViewModel
    {
        public MiViewModel() { }       
        public int? EmployeeId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Pos { get; set; }
        public string Color { get; set; }
        public string Address { get; set; }
        public bool hasChildren { get; set; }        
        public int? ReportsTo { get; set; }       
    }        
}

El resultado es:


Ahora veamos algunas cosillas que podemos agregar. Por ejemplo cambiar el texto que muestra el botón de arriba, en vez de "Add New Record", ponemos "Crear":
@(Html.Kendo().TreeList<Modelo>()
.Toolbar(toolbar => toolbar.Create().Text("Crear"))
Como hacer botones propios en el header del TreeList
En la definición del TreeList:
.Toolbar(toolbar =>
{
   toolbar.Custom().Name("botoncrear").Text("Crear");
   toolbar.Custom().Name("mycommand").Text("View X");
})
Luego en la sección scripts ponemos el handler del botón que lee una propiedad del modelo por ejemplo (en base a la celda seleccionada) y lo muestra en un alert:

Como dejar una fila seleccionable (al hacer clic en una celda toda la fila queda seleccionada):
@(Html.Kendo().TreeList<Modelo>()
  .Selectable(true)
Como obtener dicha fila seleccionada con jQuery:
var treeList = $("#contenedor").data("kendoTreeList");
var row = treeList.select();       
Como cambiarle el estilo a una fila seleccionada, por ejemplo seleccionarla y dejarla con estilo oblique (parecido a Italic):
var treeList = $("#contenedor").data("kendoTreeList");
var row = treeList.select();            
row.css('font-style', 'oblique');

Espero les sirva.