Lista Expansible Dinámica con jQuery & AspNet MVC – AspNet WebForms

Uff largo tiempo sin bloguear!!! Había estado viviendo la vida de los mortales por un tiempo, a veces durmiendo temprano, descansando, jugando Wii, en fin… de vez en cuando no está mal ser un ser humano común y silvestre xD

Pero aquí regresando al mundo bloggero empiezo dedicando este post a jQuery, la librería de javascript por defecto en cualquier aplicación web bajo cualquier plataforma. La problemática del post es el uso de jQuery para crear listas que se pueden expandir o colapsar para mostrar información de cualquier tipo y darle un mejor uso al espacio en pantalla, lo cual le da al usuario el control sobre lo que ve en su entorno de trabajo.
Decidi hacer el ejemplo bajo 2 paradigmas: AspNet Webforms y AspNet MVC3, aunque el esquema es de una plantilla puede aplicar para cualquier plataforma (java, go, scala, etc), ya que el “truco” esta del lado del cliente con javascript.
Ok, el caso es el siguiente:

Tenemos contenido dinamico que lo queremos ocultar o mostrar al hacer click en un link, que tiene la logica para mostrar u ocultar lo que contiene.
El html es el siguiente:

<div>
<div class="expandable">
<div>
<div>
                    <strong>Expand ME!!!!</strong></div>
<div class="content">
                    Aqui va el contenido
                    <img src="imagen1.jpg" alt="" /></div>
</div>
</div>
<div id="read-more" class="read-more"></div>
</div>

Esta es la plantilla de html para cualquier elemento que desee implementar la funcionalidad de poder expandirse/contraerse. Lo importante en este html:

  • El div principal que contiene los datos tiene como estilo la clase “expandible”, MUY importante ya que como veremos mas adelante con DOM obtendremos todos los elementos html que tengan ese estilo para darle la funcionalidad requerida.
  • Hay 1 div sin contenido: read-more que ademas del id estan enlazados al css por la clase indicada. En este elemento es donde vamos a agregar dinamicamente un link con el evento y el handler para expandir/colapsar el detalle

Ok, si lo vemos sin ningun esfuerzo adicional es un div que tiene otro div con mas html… entonces como le damos la funcionalidad de esconder el html? Comenzamos dandole ciertas propiedades a través de CSS a los elementos de la plantilla:

.expandable
{
    position: relative;
    overflow: hidden;
}

Para evitar cualquier desorden de UI colocamos como relative la posicion de los elementos, y el overflow es la que nos da el truco de esconder el detalle del elemento a mostrar, ya que al evaluar que el espacio en altura es menor a lo que ocupa lo que se quiere mostrar simplemente esconde todos los elementos.
Ahora vamos al javascript:


$(function doExpandable() {
    var items = $('.expandable');
    items.each(function () {
        var defHeight = $(this).height();
        var element = $(this);
        var slideHeight = 25;
        if (defHeight >= slideHeight) {
            element.css('height', slideHeight + 'px');
            var moreElement = element.siblings().filter(function () {
                return $(this).attr('id') == "read-more";
            });
            moreElement.append('<a id="link" href="#">more</a>');
            moreElement.children().click(function () {

                var curHeight = element.height();
                if (curHeight == slideHeight) {
                    element.animate({
                        height: defHeight
                    }, "normal");
                    $(this).html('close');
                } else {
                    element.animate({
                        height: slideHeight
                    }, "normal");
                    $(this).html('more');
                }
                return false;
            });
        }
    }
    );

});

Haciendo uso de los selectors por clase (atributo class) de jQuery, obtenemos todos los elementos configurados como “expandable”
y para cada elemento se hacen los siguienes procesos:

  • Se calcula si la altura es mayor al valor inicial (el valor que tendra el elemento estando colapsado:25)
  • Si es asi modificamos la propiedad heigth del elemento
  • Accedemos a los nodos adyacentes (siblings) y filtramos los que tengan el id “read-more”, que hasta este momento es un div vacio
  • Dinamicamente se agrega un link al div “read-more”
  • Agregamos el handler al click en el link donde animanos el cambio de tamaño usando el animate() de jQuery, y cambiamos el texto del link dependiendo si colapsa o expande el elemento.

Hay ciertos detalles importantes en el javascript, por ejemplo el almacenar en var element el elemento expansible que se esta iterando, ya que es la manera en la que puede ser referenciado por los metodos anónimos.

Creando un UserControl

Uniendo la plantilla html y un control Repeater podemos hacer un user control para crear una lista dimámica. Primero creamos una clase con las propiedades necesarias:

public class Item
{
    public int ID { get; set; }
    public string Title { get; set; }
    public string Text { get; set; }
    public string Image { get; set; }

	public Item()
	{
	}
}

Y creamos el control ascx con la plantilla:

<asp:Repeater ID="dataRepeater" runat="server">
 <ItemTemplate>
 <div>
 <div class="expandable" id="<%# Eval("ID")%>">
 <div>
 <div>
 <strong>
 <%# Eval("Title")%></strong></div>
 <div class="content">
 <%# Eval("Text")%>
 <img src='<%#Eval("Image")%>' alt=""/>
 </div>
 </div>
 </div>
 <div id="read-more" class="read-more">
 </div>
 </div>
 </ItemTemplate>
</asp:Repeater>

Del lado del server le ponemos una propiedad datasource para hacer el bind al repeater

public IEnumerable<Item> Datasource { get; set; }

Luego simplemente colocamos el control en cualquier aspx y o llenamos de datos para crear la lista:

protected void Page_Load(object sender, EventArgs e)
{
 if (!Page.IsPostBack)
 {
 var data = new List<Item>();
 data.Add(new Item { ID = 1, Title = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr",
 Text = "Item 1 text",
 Image = "images/image1.jpg"
 });
 expandableList.Datasource = data;
 }
 }

Ahora en MVC

En aspnet mvc es muy sencillo ya que el esquema de plantillas que se utiliza nos facilita el trabajo.
En el Controller cargamos la lista de datos que pasamos a la vista


public ActionResult Index()
 {
var data = new List<Item>();
data.Add(new Item
{ ID = 1, Title = "Expand Me!!!", Text = "Item 1 Text", Image = "images/image1.jpg"
});
return View(data);
 }

Y en la vista


@model IEnumerable<JQueryExpandableMVC.Models.Item>
<script src="../../Scripts/expandable.js" type="text/javascript"></script>
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />

@foreach (JQueryExpandableMVC.Models.Item item in @Model)
{
<div>
<div class="expandable">
<div>
<div>
<strong>@item.Title</strong>
</div>
<div class="content">@item.Text
<img src='@item.Image' alt=""/>
</div>
</div>
</div>
<div id="read-more" class="read-more">
</div>
</div>
}

Bueno, con eso termino este post y dejo el link para descargar el código fuente.