Categorías
desarrollo Internet java

Resaltar una búsqueda con jQuery

Después de algunos meses sin publicar ningún post, y ya pasadas las vacaciones de verano, vuelvo con un tema que seguro que a los que trabajamos diseñando / desarrollando aplicaciones web nos han pedido los usuarios en más de alguna ocasión: Resaltar en la pantalla las palabras introducidas en una búsqueda.

En nuestro caso tenemos una página con un formulario de búsqueda con diferentes campos: una caja de texto (búsqueda por texto), dos campos de fechas (búsqueda por fechas) y una lista desplegable de países. Todos ellos se combinan en una consulta a la Base de datos y muestra un listado con los resultados. Dichos resultados se pueden ampliar en otra página pulsando sobre el registro correspondiente. Cómo os imagináis es en esta página de detalle donde se nos requirió que se resaltaran las palabras introducidas en la búsqueda.

Después de manejar varias opciones (resaltar en BBDD o en la propia aplicación, de manera que al navegador le llegaba el trabajo hecho), me decanté por hacerlo mediante javascript una vez que haya cargado todo el documento en la página. Para ello hemos incluido dentro del proyecto la librería jQuery (de la que ya os he hablado anteriormente) y de un plugin para ella que podéis consultar en http://bartaz.github.com/sandbox.js/jquery.highlight.html.

Básicamente esta librería contiene una función que se encarga de recorrer todas las palabras del contenedor (documento, div, table, etc.) que le especifiquemos y si encuentra alguna de las palabras utilizadas en la búsqueda y que le pasamos como parámetro la envuelve en una etiqueta <span class=»highlight»>:

<script type="text/javascript" src="/js/jquery-1.4.2.js"></script>
<script type="text/javascript" src="/js/jquery-highlight.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$("html").highlight(divide_string("<c:out value='${modelo.form.textoLibre}'/>"),{ wordsOnly: true });
});
</script>

Este código se ejecuta una vez que se ha cargado el documento y llama a la función highlight para que resalte las palabras dentro del elemento HTML del DOM. Asímismo le pasamos la opción wordsOnly:true para que resalte palabras completas (si en la búsqueda se introdujo ‘este’, que no resalte parcialmente Esteban). La función divide_string recibe el texto introducido en la caja de búsqueda y retorna un array de palabras, obviando palabras comunes como artículos y preposiciones. Aquí os paso el código:

function divide_string(cadena)
{
var pattern =                       "\\b(Sr|Sra|Sres|Sta|...más_palabras_comunes...|vuestro|vuestros|y|yo|él|en)\\b";
var re = new RegExp(pattern, "gi");
cadena = cadena.replace(re,"");
var mytool_array = cadena.split(" ");
return mytool_array;
}

Por último indicar que fue necesario realizar modificaciones del plugin ya que no resaltaba las palabras que tenían acento si en la caja de texto buscabas sin acento. Justo antes de crear la expresión regular a evaluar, modificamos el patrón para incluir las vocales acentuadas:

pattern = pattern.replace(/a/gi,"[a|á]");pattern = pattern.replace(/e/gi,"[e|é]");pattern = pattern.replace(/i/gi,"[i|í]");pattern = pattern.replace(/o/gi,"[o|ó]");pattern = pattern.replace(/u/gi,"[u|ú]");
if (settings.wordsOnly) {
pattern = "\\b" + pattern + "\\b";
}
var re = new RegExp(pattern, flag);

Seguro que alguna vez os resulta útil.

Categorías
desarrollo Internet

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

Categorías
desarrollo java

Gestión de Ramas con Subversion y Eclipse IDE

Crear ramas, etiquetar y realizar merge son conceptos comunes para casi todos los sistemas de control de versiones. En este artículo vamos a adentrarnos en la gestión de ramas dentro de un proyecto de desarrollo con Eclipse. Este proyecto lo tenemos enlazado a Subversion utilizando el plugin Subclipse. Gestionar ramas (branching) es una parte fundamental de un control de versiones, se asume que estás familiarizado con los conceptos básicos de Subversion.

¿Qué es una rama?

Se trata de una línea de desarrollo que existe independientemente de otra. Una rama siempre es una copia de otra y desde el momento de copia contiene su propia historia. Subversion permite mantener ramas paralelas de tus ficheros y directorios. Ayuda a duplicar cambios de una rama a otra y, finalmente, puede hacer que se reflejen cambios de tu copia de trabajo en diferentes ramas por lo que puedes mezclar y enlazar diferentes líneas de desarrollo en tu trabajo diario.

Sincronizar ramas Subversion

Crear una rama

Vamos con un caso práctico: Lo normal es que el proyecto esté asociado a la línea principal de desarrollo, que llamamos Trunk. Todas las modificaciones y funcionalidades que se deseen incorporar a la versión de producción deberán estar contenidas en esta rama (pueden existir otras estrategias pero ésta es la que usamos en nuestro trabajo). En un momento determinado nos puede interesar abrir otra línea de desarrollo o rama en la que trabajará de forma independiente en el tiempo un desarrollador o equipo de desarrolladores, por ejemplo. Así dejamos la copia de trabajo del Trunk libre por si es necesario una nueva versión de producción de manera inmediata para corregir una incidencia o incorporar una funcionalidad urgente. Para realizar esto hacemos clic sobre el proyecto con el botón derecho del ratón y vamos a Team -> Branch/Tag. Subversion considera exactamente igual una rama (branch) que una etiqueta (tag), es decir, son copias de un conjunto de carpetas y ficheros (se diferencian en que las ramas tienen history y las etiquetas no).

A continuación seleccionamos el directorio en el que se almacenará esta rama. Todas las ramas se suelen guardar bajo el directorio branches del repositorio por convenio. También debemos indicar la revisión sobre la que queremos crear la rama. Si queremos que se haga desde la última versión de desarrollo, seleccionamos la opción HEAD. Por último podemos indicar un comentario, por ejemplo, «Creación nueva rama».
A partir de este momento la copia de trabajo en eclipse pasa a ser la de la rama (si no es así podemos pulsar sobre el proyecto con el botón derecho del ratón y pulsar sobre Team -> Switch to another Branch/tag/revision… para seleccionar la nueva rama), por lo que cualquier modificación que realicemos y subamos al control de versiones se hará sobre ésta. Se harán las modificaciones oportunas en el desarrollo de nuevas funcionalidades y las subiremos al repositorio haciendo commit.

Mantener sincronizada una rama

De forma periódica es conveniente actualizar esta rama con los posibles cambios introducidos en el trunk, ya que aunque no hemos terminado con nuestro trabajo en la rama, deseamos que los cambios en el trunk no se aparten mucho del contenido de la rama, y la sincronización entre ellas sea menos traumática. Ya dijimos que cada rama tiene una línea de desarrollo independiente.

Para realizar un merge del trunk sobre nuestra rama pulsamos sobre el proyecto y vamos a Team -> Merge. Aparecerá a continuación una pantalla en la que debemos seleccionar el modo en el que haremos merge:

Mantener sincronizada una rama

Seleccionamos la primera opción «Merge a range of revisions» para incorporar los cambios en el trunk o en otra rama. Deberemos elegir a continuación la localización de los elementos a sincronizar, y como ya habíamos dicho lo vamos a realizar desde el trunk. En la siguiente pantalla podemos especificar las opciones del proceso de merge.

Ya que se han hecho cambios en el trunk mientras nosotros trabajábamos en nuestra rama, pueden aparecer conflictos. La pantalla siguiente nos avisa de que hay un conflicto en una clase que debemos resolver. Para resolverlo elegimos la opción Launch a graphical conflict resolution editor y se abrirá un editor gráfico con ambas versiones y los cambios señalizados de forma gráfica.

Resolución de conflicto al hacer merge ramas

Al solucionar manualmente el conflicto y cerrar el editor aparece una pantalla de confirmación de resolución del conflicto. Seleccionamos Yes y pulsamos ok.

Al terminar todo el proceso nos muestra una pantalla de resumen. Ya tenemos en una copia local los cambios del trunk y de la rama. Ahora tenemos que consolidar los cambios a la rama haciendo commit y subiendo las modificaciones al control de versiones (dentro de la rama).

Resumen merge ramas Subversion

Reintegrar una rama

Qué ocurre cuando has finalizado tu trabajo en la nueva línea de desarrollo. Tu nueva funcionalidad está acabada y está preparada para pasar al trunk, por lo que debemos cambiar la rama asociada al proyecto pulsando sobre el proyecto con el botón derecho del ratón y seleccionar Team -> Switch to another Branch/tag/revision… para asociar al trunk.

A continuación hacemos un merge, como ya indicamos en el punto anterior, ahora con la opción Reintegrate a branch.

Reintegrar una rama al trunk de subversion

En la pantalla siguiente seleccionamos la rama desde la que vamos a sincronizar. El proceso posterior es similar a lo comentado en el punto anterior, sin olvidar que la sincronización se realiza sobre la copia local y que debemos consolidar los cambios haciendo commit.

Evidentemente todo el proceso comentado aquí puede diferir de las necesidades de cada proyecto: puede interesar hacer merge de unos pocos paquetes, algunos módulos, o de ciertas revisions, en vez del HEAD. En el mundo real pueden surgir falsos conflictos en la sincronización, errores de versiones, que aprendemos a manejar con la práctica y experiencia. También tenemos que tener en cuenta que las versiones del cliente subversión y del servidor son compatibles (sobre todo al reintegrar una rama en el trunk puede dar algún tipo de problema). Lo que está claro es que dominar este proceso nos puede facilitar la vida en ciertos proyectos donde concurren desarrollos en paralelo de diferentes módulos.

Versiones

Eclipse Galileo Build id: 20100218-1602

Subversion Client Adapter: 1.6.10

Subclipse Integration: 3.0.0

Eclipse Mylin: 3.3.2

Categorías
ebook iphone ipod libros

Ipod Touch como lector de libros electrónicos

Hace 8 meses aproximadamente que adquirí un reproductor iPod Touch de Apple, en la versión de 8Gb, y aparte de llenar mis horas de entretenimiento con música, películas, navegación web, fotos y juegos, ha supuesto una sorpresa para mí el poder utilizarlo como lector de libros electrónicos.

Hay multitud de aplicaciones para ello, aparte de los lectores de documentos en formato Word y PDF’s, la AppStore se llena de aplicaciones como Amazon Kindle (versión iPhone) o Stanza, de la que voy a hablar a continuación:

  • Stanza te permite leer en una resolución con calidad de impresión, con una luminosidad muy suave para lectura nocturna y cansar lo menos posible la vista. Con pequeños e intuitivos taps (golpecitos sobre la pantalla), cambiamos de página, modificamos el nivel de brillo de la pantalla (deslizando el dedo de arriba a abajo o viceversa), marcamos la página para añadir un marcador, etc.
  • Puedes personalizar tus colores, fuentes, líneas y hasta reglas lingüísticas.
  • La organización de la librería es muy intuitiva, ordenando por diferentes criterios. Además puedes navegar entre tus títulos mediante «cover flow».
  • Compartir libros y documentos a través de redes wifi es muy fácil desde múltiples ordenadores en la red.
  • Puedes acceder a una enorme cantidad de libros electrónicos, muchos de ellos gratuitos como los del proyecto Gutemberg. De él puedes descargarte directamente al iPod-iPhone clásicos de la literatura, como ‘el quijote’.
  • Se conecta a varios sitios web de venta de libros, con un volumen de títulos considerable.
  • Soporta gran cantidad de formatos. De forma opcional es posible descargarse Stanza Desktop para el ordenador de sobremesa o portatil, con el que podemos convertir los libros al formato deseado, así como compartir los libros con el iPod. Al contrario que su versión móvil, Stanza Desktop no me ha convencido mucho.

Ni remotamente pensaba al comprar el iPod que lo iba a utilizar para la lectura de libros electrónicos, y aunque todavía uno se resiste a abandonar los ejemplares en papel, últimamente no es nada difícil verme leyendo libros en mi iPod en la cama o en el tranporte público (lo cual es una ventaja de tamaño en los saturados servicios de transporte que utilizo en mi ciudad).

Categorías
blog Internet personal

La importancia de la imagen en Internet

Hace unos días aparecía en las páginas de un periódico nacional, una consulta realizada por un profesional a un experto en el que exponía su experiencia personal en una entrevista de trabajo. En ella comentaba que le formularon preguntas con relación a lo que habían encontrado sobre él en Internet (artículos publicados, presencia en foros, etc.). Al final preguntaba acerca de la importancia de la presencia en Internet y en el perfil nuestro que queremos que aparezca.

xing.com

La respuesta era obvia: igual que cuidamos nuestra presencia en la calle, en nuestro trabajo, debemos cuidar nuestra presencia en la red. Estar presente en la red sirve para ver y ser visto, para aportar y recibir información, pero tiene sus riesgos. Añadía el experto que toda la información nuestra puede ser accesible por todos y reutilizada en cualquier momento. Se incluía un plan de posicionamiento personal planteando los siguientes puntos:

  1. ¿En qué redes profesionales quiero estar? (lindekin, xing,…) A priori, más es mejor.
  2. ¿En qué redes sociales quiero estar? Cuantas menos, mejor
  3. ¿Qué voy a publicar?, pero sobre todo ¿para qué y para quiénes lo voy a publicar? Muy importante tener en cuenta que lo que escribo hoy para un público será accesible mañana para cualquiera

Por último, recomienda ser activo, participar, editar contenidos, ya que si no estamos en los foros profesionales específicos de nuestra función o profesión, dejamos de existir.

Ya comenté en mi primera entrada en el blog mi preocupación por estar presente en Internet, y esa fue una de las razones que me llevaron a crear este blog, pero, ¿tendré que tener más cuidado de lo que publico? Quién sabe si el día de mañana alguien necesite encontrar información sobre mi.