Tablas con DisplayTag y Ajax

Durante los dos últimos años, mi equipo y yo hemos introducido en los proyectos J2EE una librería de tags JSP para la publicación de datos de forma tabular. Se trata de DisplayTag, una librería open source muy potente que nos facilita la visualización de tablas de datos y la ordenación y paginación de los mismos empleando muy poco código. Para incluirla basta con añadir en la página JSP el tag <display:table>, indicarle el nombre de las columnas, así como el nombre de la colección de datos que enviamos desde el servidor en la respuesta. La librería se encarga de generar el código html para presentar los datos en la tabla, generar los controles de paginación y enlaces de ordenación de las columnas.

Una de las funcionalidades que no incluye esta librería es la utilización de AJAX,  que nos acerca una serie de técnicas de programación para la creación de aplicaciones interactivas. Cada vez que pulsamos para ordenar la tabla o para pasar a la siguiente página de resultados se realiza una petición al servidor y éste devuelve una nueva página recargando la anterior con la nueva información. Sería conveniente que por cada petición de ordenar o cambiar la página de resultados se recargase únicamente la información de la tabla de forma asíncrona y no toda la página.

Solución con Ajax Tags

La librería Ajax Tags es un conjunto de tags JSP que simplifica el uso de la tecnología AJAX en las páginas JSP. Además incorpora un tag específico para permitir y habilitar AJAX en las Displaytags comentadas más arriba. Para utilizarlo basta con envolver la tabla con el tag <ajax:displayTag> de manera que sólo se refresque el área contenida en éste y no toda la página:

<ajax:displayTag id="displayTagFrame" ajaxFlag="displayAjax">

    <display:table name="service.allCars" pagesize="10" scope="page"
        defaultsort="1" defaultorder="descending" export="true" id="row"
        excludedParams="ajax">
        <display:column property="make" title="Make" sortable="true"
            headerClass="sortable" />
        <display:column property="model" title="Model" sortable="true"
            headerClass="sortable" />
        <display:column title="Link" media="html">
            <a href="http://www.${row.make}.com">${row.make} Web Page</a>
        </display:column>
        <display:column title="Link" media="excel xml"> www.${row.make}.com </display:column>
    </display:table>

</ajax:displayTag>

¿Cómo funciona? Engloba todo el código html de la tabla en un div con el id indicado como parámetro, sobreescribe el método onclick de cada enlace de la tabla por un código que se encarga de realizar la llamada al servidor y recargar únicamente el div indicado con el código de la tabla (obviando todo el código que no sea la tabla).

Aun resultando así de fácil nos hemos encontrado dos problemas:

  1. Nosotros utilizamos Struts/Tiles para componer la maquetación de las páginas (aunque nuestro framework sea Spring), de manera que dividimos la página en varias JSPs (cabecera, menú, pie y contenido). Cómo hemos dicho anteriormente ajaxtags obvia el código que no sea de la tabla pero únicamente lo hacía para el contenido, por lo que en el div volvía a aparecer la cabecera, el pie, etc. La solución fue crear una definición en la configuración de Tiles que sólo incluyese el contenido y si en la petición venía cierto parámetro, redirigir desde el servidor a ésta.
  2. Ajaxtags sobreescribe el evento onclick de los enlaces, pero también sobreescribe el atributo href con un javascript://nop/. Esto nos supone un problema si el sitio tiene alguna restricción de accesibilidad, puesto que en navegadores que no tengan javascript activo (o no soporten js) no funcionará la paginación y ordenación de la tabla.

Solución con jQuery

jQuery es una librería Javascript que simplifica el desplazamiento entre documentos, manipulación del DOM, la gestión de eventos, los efectos y animaciones y la interacción mediante Ajax para un rápido desarrollo web. Esta solución se basa en la funcionalidad load de jQuery, la cual permite cargar html externo desde una fuente remota e inyectarlo en el DOM.

Vamos a verlo más fácil con el siguiente ejemplo:

<div id="tablaresultados">

    <display:table name="modelo.resultado"
        requestURI="/catalog/busquedaTexto.do" pagesize="10" cellspacing="0"
        decorator="es.displaytag.decorator.BuscaTextoTableDecorator"
        id="resultados">
        <display:column title="Título" property="titulo" sortable="true" />
        <display:column title="Signatura" property="signatura" sortable="true" />
        <display:column title="Fechas" property="fecha" style="text-align:center" sortable="true" />
    </display:table>

</div>

El único requisito en la tabla será que la contenga una capa con el id que deseemos. En esta solución sí que es necesario introducir código javascript en la página:

<script>

    if (!com) var com = {};
    es.displaytag= {
            onPeopleTableLoad: function() {

                // Se llama cuando se cargan los datos
                $("table#resultados th.sortable").each(function() {
                    // Itera sobre cada cabecera de columna conteniendo la clase sortable, de manera
                    // que
                    // podemos sobreescribir la gestión de click via ajax, en lugar de
                    // permitir al navegador seguir un enlace normal
                    $(this).click(function() {
                        // "this" es el elemento th ordenable
                        var link = $(this).find("a").attr("href") + " #tablaresultados";
                        $("div#tablaresultados").load(link, {}, es.displaytag.onPeopleTableLoad);
                        // Paramos la propagación de eventos, sin permitir que el navegador ejecute el
                        // href
                        return false;
                    });
                });
                $("div#tablaresultados .pagelinks a").each(function() {
                    // Itera sobre cada enlace de paginación para sobreescribirlo
                    $(this).click(function() {
                        var link = $(this).attr("href") + " #tablaresultados";
                        $("div#tablaresultados").load(link, {}, es.displaytag.onPeopleTableLoad);
                        return false;
                    });
                });
            }
    };

    $(document).ready(function() {
        // Carga inicial cuando el DOM está preparado. Se asume que vas a inyectar
        // dentro de un div
        // con el id "tablaresultados" que existe en la página.
        $("div#tablaresultados").load("/path/remoto/que/genera/tabla/arriba", {}, es.displaytag.onPeopleTableLoad);
    });

</script>

Para evitar el problema 1 comentado en la solución con ajaxtags, añadimos a los enlaces el id “#tablaresultados” para indicarle que recargue únicamente la capa que contiene la tabla. El problema 2 no lo tenemos con esta solución puesto que no se sobreescribe el atributo href. En el caso de mostrar la tabla en un navegador que no interprete javascript la tabla funcionará normalmente recargando toda la página.

Conclusiones

Personalmente me inclino por la solución con jQuery, ya que en nuestro caso no es necesario modificar código en el servidor y porque necesitamos cumplir con criterios de accesibilidad.

Seguramente existen multitud de componentes o tablas con Ajax para incorporar a las aplicaciones web, pero si ya tienes una página con displaytag, el código en el servidor que la alimenta y deseas incorporar ajax y mantener lo que tenías, estas dos soluciones son unas muy buenas opciones.

Actualización

(25-03-2011) Posterior a esta entrada se desarrolló un plugin jQuery (displaytag-ajax) que provee la capacidad de refresco AJAX a tablas generadas mediante Display Tag. Debo dar las gracias a Yajairo87 ya que gracias a su comentario pude descargármelo y probarlo (funciona perfectamente). Si tenéis curiosidad y miráis el código comprobaréis que realiza el mismo proceso comentado en esta entrada. Podéis descargarlo en la siguiente dirección: http://joseantoniosaiz.es/samples/displaytag-ajax/displaytag-ajax-1.2_0.zip

23 opiniones en “Tablas con DisplayTag y Ajax”

  1. Hola Oscar. Por lo que he podido ver es complicado. No hay ningún atributo que indique si se permite sobreescribir el href, lo hace siempre. Si exploras el onclick generado verás que la urlBase va dentro de la llamada javascript, por si la puedes extraer y volver a sobreescribir el href.
    A la desesperada siempre puedes descargarte el código fuente de AjaxTags para ver la línea de código en la que se sobreescribe el href por javascript:nop/

  2. muy bueno el aprote, yo uso he usado tambien el displayTag y con este tip de tile solucionaste uno de los grandes problemas que he tenido…. muchisimas gracias por el aporte

  3. Hola Yajairo87,

    Agradezco tu comentario y tu enlace. Lo he estado probando y funciona perfectamente bien. He actualizado el post incluyendo un enlace al plugin.

    Gracias. Un saludo.

  4. Hola como están, tengo una inquietud lo que sucede es que cuando pagino se me pierden los estilos de la página y la paginación de la misma, mi código es el siguiente:

    Agradezco cualquier ayuda que me puedan brindar
    Muchas gracias

  5. Buenas José Antonio,

    primero de todo, felicitarte por el contenido del post. Realmente útil 🙂
    Segundo…estoy intentando utilizar el plugin de Jquery para ajaxizar la tabla con displaytag pero no acabo de comprender qué le he de devolver a la llamada ajax que realiza.
    El código de la llamada ajax es:
    function addClickEvent(ctx, element,url){
    $(element).click(
    function(){
    jQuery.ajax(
    {
    url: url,
    success: function(data){
    filteredResponse = $(data).find(this.selector);
    if(filteredResponse.size() == 1){
    $(this).html(filteredResponse);
    }else{
    $(this).html(data);
    }
    $(this).displayTagAjax();
    } ,
    data: ({“time”:new Date().getTime()}),
    context: ctx
    });
    }
    );
    }

    Lo que no me queda claro, es que tipo de data le he de devolver en la llamada. Html? json?

    Muchas gracias de antemano!
    Jordi

  6. Hola Jordi,

    Como comento en el post el comportamiento estándard (sin ajax) de la tabla displaytag es que cada vez que pulsamos sobre un enlace para ordenar la tabla o para cambiar de página en la misma, se realiza una petición al servidor y se sirve de nuevo toda la página, es decir se recarga por completo. Esto es lo que intentamos evitar, que no se refresque toda la página sino sólo la tabla.

    Con Ajax, y respondiendo a tu pregunta, el servidor debe devolver toda la página como en el escenario que he descrito (por lo tanto, HTML) pero el plugin jQuery se encarga de extraer de la misma el fragmento de código HTML de la tabla y lo inyecta en la capa que contiene el displaytable de forma automática. Además, con esto aseguramos la accesibilidad, ya que si un navegador no tiene activado javascript, la página seguirá funcionando recargándose por completo.

    Espero que haya aclarado tu duda. Muchas gracias por tu amabilidad. Saludos

  7. Muchas gracias José Antonio!

    La verdad que me ha quedado muchísimo más claro con tu respuesta 🙂

    De nuevo, mil gracias!
    Jordi

  8. Muchas gracias por el aporte, estoy empezando a probar el plugin de jquery para el ajax y hasta ahora me esta resolviendo los problemas que tenia el ajax:displaytag.

  9. Excelente página, es justo lo que estaba buscando, consulto a los expertos, alguien sabe como aplicar filtros o búsquedas con displaytag sobre las columnas o algún otro tipo de búsqueda ?? es lo que me falta.

    Estoy trabajando con Spring 3 y eclipse Indigo, estoy intentando con jqGrid pero no me funciona JSON en el controller me genera objetos JSON pero no logro mostrarlos en la grilla, tambien estoy trabajando con TILES, y creo este es mi problema, pero ya tenia paginacion con displaytag y buscaba actualizarme para el uso de JQUERY pero esta página me ayudo en este tema pero me falta la búsqueda.

    Si alguien me puede ayuda please.

    Saludos.

  10. Hola que tal.
    Me parece interesante el aporte.
    Estoy intentando hacer una tabla con display tag, utilizando Ajax, struts 2, tiles y jquery.
    La tabla se carga por medio de un ArrayList de objetos bean, generado desde el action que se llama por medio de Ajax-jquery. El tema es que ese action me devuelve un objeto JSON del tipo
    {pedidosfacturaCliente:[{nombreCliente:Pablo Perez,pedido:512},{nombreCliente:Juan Alonso,pedido:215}]}, que representa un arrayList de objetos y es lo que utilizo en el name del display:table para rellenar mi tabla.
    Cabe decir lo que veo que mucha gente no dice, y es que lo que esta en el name es un atributo arraylist de objetos que forma parte de mi clase action, y para que el display tag me lo reconozca lo debo meter dentro del action en una variable de sesión.
    En definitiva me displaya la lista, pero después de dar a actualizar pantalla, y eso no esta bien.
    Sospecho que el plugin jquery.displaytag-ajax-1.2.js no funciona bien.
    Les pongo lo que tengo en el body de la jsp:

    En las cabeceras de la jsp…

    $(document).ready(function(){
    $( function(){
    alert(“hola1”);
    $(“#displayTabla”).displayTagAjax();
    alert(“hola2”);
    });
    …….
    Llamada a Ajax…
    var idCliente=datosCliente.substring(0,datosCliente.indexOf(‘ ‘));

    $.ajax({
    url: ‘pedidosFacturasClienteAjax.action’,
    type: ‘POST’,
    cache:false,

    data: {“idCliente”:idCliente},

    success: function (resp) {

    var objeto= JSON.stringify(resp);
    $(“#displayTabla”).displayTagAjax();
    },
    error: function(e) {
    alert(‘Erroooor: ‘+e);
    }
    });//fin $ajax

    Llevo varios dias intentando que funciona y que solo se me cargue el div, pero no hay forma de hacerlo.
    Agradeceria respuestas interesantes.
    Mi correo es jalber78@gmail.com
    Gracias y un saludo

  11. estoy trabajando con struts 2 y para presentar mis datos use jqGrid pero al realizar los cambios en la fila no guarda en la base de datos postgresql

    si pudieran ayudar con eso se los agradeceria…

Deja un comentario

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