miércoles, 27 de agosto de 2014

Convertir una maqueta en HTML en una página Web con contenido dinámico (2)

En el post anterior hablábamos del proceso para convertir una maqueta en HTML con una cantidad fija de información en una Web con contenido dinámico. Ahora, veremos cómo convertir una maqueta con una cantidad variable de información en una Web con contenido dinámico.

Ejemplo de sección con una cantidad variable de información


Volviendo al ejemplo de la Web de la escuela, supongamos que hay una página en la que se publica la lista de estudiantes que cumplen años en un día determinado. Para ello, será posible indicar una fecha y pulsar un botón  para obtener la lista de alumnos correspondientes. La sección maquetada podría quedar como sigue:

<div id="cumples">
    <input type="date"> <button>Buscar</button>
    <ul>
        <li>
            <img src="img/persona.png">
            <label>Juan Pérez</label>
        </li>
        <li>
            <img src="img/persona.png">
            <label>Manuel López</label>
        </li>
        <li>
            <img src="img/persona.png">
            <label>María González Gurrierrez</label>
            </li>
        <li>
            <img src="img/persona.png">
            <label>Clara Martínez</label>
            </li>
        <li>
            <img src="img/persona.png">
            <label>Julián del Corral</label>
        </li>
    </ul>
</div>

Cuyo resultado es el siguiente (después de formatearlo un poco con CSS):



Supongamos ahora que en dicha Web, hay un servicio REST que, al consultarlo indicándole una fecha, devuelve un JSON la lista de los alumnos que cumplen años. Por ejemplo, el JSON devuelto podría tener la estructura siguiente

[
    {
        "nombre": "María López González",
        "foto": "img/alumnos/maria_lopez.jpg"
    },
    {
        "nombre": "Juan Pérez González",
        "foto": "img/alumnos/juan_perez.jpg"
    },
    {
        "nombre": "Laura Gutierrez Pérez",
        "foto": "img/alumnos/laura_gutierrez.jpg"
    }
]

Veamos ahora, cómo transformar la maqueta para mostrar dinámicamente el contenido del JSON obtenido desde el servicio REST.

Proceso para convertir el contenido de una maqueta en HTML con una cantidad variable de información en una Web con contenido dinámico


Hay varias formas para generar una cantidad dinámica de contenido, aunque todas requieren lo mismo: insertar nuevos elementos en la página. Ahora, la forma de hacer estas inserciones, puede variar mucho. En el post XXX se muestra cómo insertar nodos nuevos (etiquetas HTML) mediante jQuery. Aunque el método ahí mostrado es más flexible y potente que el que mostraremos a continuación, el que sigue es más simple y directo; siendo un paso previo natural al uso de frameworks para el uso de plantillas HTML, como Mustage.

Entonces, el proceso sería el siguiente:

  1. Identificar el evento ante el cuál queremos actualizar la plantilla con los datos dinámicos.
  2. Localizar la sección de datos que se repiten
  3. Modificar la plantilla:
    1. Eliminar todos los elementos que se repiten, excepto el primero
    2. Si no lo están, encerrar los datos que se repiten en un elemento agrupador.
    3. Asignar un identificador único al elemento agrupador.
    4. Crear un bloque script con un identificador único y tipo "text/html"
    5. Mover el primer conjunto de datos que se repite, localizado en el paso 2, al bloque script del paso 3 - 4.
    6. Remplazar cada texto o atributo a modificar dinámicamente por el nombre del dato desde donde se extraerá, encerrado entre llaves dobles.
  4. Crear una cadena en JavaScript con la plantilla del HTML que deberá repetirse, extrayéndolo del contenido de la etiqueta script, del paso 3 - 4.
  5. Crear una función JavaScript para generar HTML, a partir de una plantilla HTML. Para ello, deberá recibir una cadena con una plantilla de una estructura HTML, un diccionario con el valor a remplazar por cada nombre de la plantilla, y deberá devolver una cadena con el HTML resultante de los remplazos.
  6. Crear una función JavaScript que modifique el HTML de la página para mostrar una lista de datos. Para ello, para cada elemento de la lista y para cada dato de dicho elemento: llamar a la función del paso 5, indicándole la plantilla HTML del paso 4, el nombre del dato y el dato en sí.
  7. Crear una función en JavaScript que cargue los datos que se quieren mostrar y llame a la función del paso 6 con los datos cargados.
  8. Cuando se produzca el evento deseado (identificado en el paso 1), llamar a la función de carga de datos (creada en el paso 7).
  9. Fin.

Para ilustrar el proceso anterior, veamos cómo dinamizar los datos de la plantilla de los "alumnos que cumplen año".

Ejemplo del proceso, ilustrado y explicado, paso a paso


1 Identificar el evento ante el cuál queremos actualizar la plantilla con los datos dinámicos


En la Web de lista de alumnos que cumplen años, primero hay que seleccionar la fecha del cumpleaños en cuestión y luego pulsar el botón de búsqueda; por lo que el evento para actualizar la plantilla será cuando se pulse en el botón para buscar.

2 Localizar la sección de datos que se repiten


En este caso, los datos que se repiten son la foto y el nombre, que se corresponden con una etiqueta img y label, encerradas por una etiqueta li. Por lo tanto, la sección de datos que se repiten son las etiquetas li y su contenido:

<li>
    <img src="img/persona.png">
    <label>Juan Pérez</label>
</li>
<li>
    <img src="img/persona.png">
    <label>Manuel López</label>
</li>
<li>
    <img src="img/persona.png">
    <label>María González Gurrierrez</label>
</li>
<!-- ... -->

3 - 1 Eliminar todos los elementos que se repiten, excepto el primero


En este caso, la lista pasaría de tener varios li, a tener uno solo:

<ul>
    <li>
        <img src="img/persona.png">
        <label>Juan Pérez</label>
    </li>
</ul>

3 - 2 Si no lo están, encerrar los datos que se repiten en un elemento agrupador


La sección que se repite es la contenida por las etiquetas li que están contenidas por una etiqueta ul; por lo que no es necesario encerrarlas en  ningún elemento agrupador. Si en lugar de una etiqueta li, la sección repetida fuera un div, por ejemplo, es posible que hubiera que encerrarlo en otro div; por ejemplo. No obstante, en este caso, los datos que se repiten ya están encerrados en un elemento agrupador: ul.

3 - 3 Asignar un identificador único al elemento agrupador


Ya que el elemento agrupador es la etiqueta ul y ésta contiene la lista de alumnos que cumplen años, identificaremos a dicho elemento con el id "alumnos":

<ul id="alumnos">
    <li>
        <img src="img/persona.png">
        <label>Juan Pérez</label>
    </li>
</ul>

3 - 4 Crear un bloque script con un identificador único y tipo "text/html"


El bloque script será el contenedor de la plantilla de los datos que se repiten para cada alumno y, aunque puede estar en cualquier parte de la página, es una buena práctica ponerlo lo más cerca posible de la zona donde vamos a insertar los datos posteriormente:

<ul id="alumnos">
    <li>
        <img src="img/persona.png">
        <label>Juan Pérez</label>
    </li>
</ul>
<script id="plantillaAlumno" type="text/html">
</script>

3 - 5 Mover el conjunto de datos que se repite (localizado en el paso 2), al bloque script del paso 3 - 4


Aquí se trata, simplemente de mover la etiqueta li y su contenido, hacia dentro de la etiqueta script del paso anterior:

<ul id="alumnos">
</ul>
<script id="plantillaAlumno" type="text/html">
    <li>
        <img src="img/persona.png">
        <label>Juan Pérez</label>
    </li>
</script>

3- 6 Remplazar cada texto o atributo a modificar dinámicamente por el nombre del dato desde donde se extraerá, encerrado entre llaves dobles


En este punto modificaremos cada elemento que deba cambiar dinámicamente por el nombre de la variable o el atributo del objeto en el que tendremos dicha información en JavaScript. En este caso, se trata de la Url de la fotografía y del nombre del estudiante por lo que la plantilla quedaría, finalmente, así:

<div id="cumples">
    <input id="fecha" type="date"> <button id="buscar">Buscar</button>
    <ul id="alumnos">
    </ul>
    <script id="plantillaAlumno" type="text/html">
        <li>
            <img src="{{foto}}">
            <label>{{nombre}}</label>
        </li>
    </script>
</div>

El motivo para poner el nombre del dato encerrado en llaves dobles es evitar colisiones entre el nombre de nuestra variable y elementos válidos del HTML, de modo que, cuando generemos el contenido dinámico no sustituyamos por accidente una parte de nuestra estructura HTML. Del mismo modo, encerramos nuestra plantilla en una etiqueta script con tipo "text/html" para que el marcado HTML de la plantilla no genere ningún elemento visual que deforme la información de la página.

4 Crear una cadena con la plantilla del HTML que deberá repetirse, extrayéndolo del contenido de la etiqueta script, del paso 3 - 4.


La cadena la crearemos extrayendo el contenido de la etiqueta script identificada como plantilla:

var plantillaAlumno = $('#plantillaAlumno').html();

5 Crear una función JavaScript para generar HTML, a partir de una plantilla HTML.


La función que se ilustra a continuación es un elemento reaprovechable del proceso; es decir: que podremos usar esta función en cualquier caso, independientemente del marcado HTML que hayamos puesto en la plantilla, siempre y cuando, cumplamos con encerrar entre llaves dobles los nombres de los datos a remplazar. De modo que, el siguente es un código que podremos copiar y pegar de una Web a otra sin ningún cambio:

/**
 * Genera código HTML a partir de una plantilla y datos de sustitución
 * @param {string} pantilla - Cadena con la plantilla HTML
 * @param {Object} datos - Diccionario de nombres de datos a remplazar 
 *      con su respectivo valor
 */
function generarHtml(plantilla, datos) {
    var html = plantilla;                   // Inicializar el HTML
    var nombresDeDatos = Object.keys(datos);// Obtener lista de datos a remplazar
    nombresDeDatos.forEach(function (nombreDato) {  // Por cada dato a remplazar:
        var nombre = new RegExp('{{' + nombreDato + '}}', 'g'); // Obtener nombre
        var valor = datos[nombreDato];              // Obtener valor de remplazo
        html = html.replace(nombre, valor);         // Remplazar nombre por valor
    });
    return html;                                    // Devolver HTML generado
}

6 Crear una función JavaScript que modifique el HTML de la página para mostrar una lista de datos


Esta función, básicamente llamará a la función del paso anterior, por cada elemento a mostrar e insertará en el DOM el HTML generado con cada dato:

/**
 * Actualiza la lista de alumnos cuyo cumpleaño coincide con la fecha indicada
 * @param {object[]} listaAlumnos - Lista de objetos con los datos de los
 *      alumnos: nombre y foto
 */
function actualizarListaAlumnos(listaAlumnos) {
    $('#alumnos').html('');                  // Inicializar la lista HTML
    listaAlumnos.forEach(function (alumno) { // Por cada alumno:
        var html = generarHtml(plantillaAlumno, alumno); // Generar su HTML
        $('#alumnos').append(html);          // Añadir el HTML generado a la Web
    });
}

7 Crear una función en JavaScript que cargue los datos que se quieren mostrar y llame a la función del paso 6 con los datos cargados


Como hemos supuesto que existe un servicio REST que, dada una fecha, devuelve un JSON con la lista de alumnos que cumplen años en dicha fecha, vamos a necesitar:


  1. Identificar la Url del servicio REST
  2. Construir la Url de la consulta con la fecha seleccionada
  3. Pedir los datos JSON mediante Ajax
  4. Llamar a la función del paso 6, con los datos obtenidos.

Para simplificar, supongamos que el servicio REST está en la misma Web, en la Url relativa "api/cumpleanyos" y que llamaremos a la función actualizarListaAlumnos, quedando la función como sigue:

/**
 * Carga la lista de alumnos que cumplen años y actualiza la interfaz 
 * con los datos cargados
 */
function cargarCumpleanyos() {
    var fecha = $('#fecha').val();          // Obtener fecha de consulta
    var url = '/api/cumpleanyos/' + fecha;  // Generar Url de consulta
    $.getJSON(url, function (result) {      // Solicitar lista de alumnos
        if (result) {                       // Si se obtiene alguna lista
            actualizarListaAlumnos(result); // Actualizar lista de alumnos
        }
    });
}

8 Cuando se produzca el evento deseado (identificado en el paso 1), llamar a la función de carga de datos (creada en el paso 7).


Finalmente, el evento generado por el usuario que desencadena la ejecución de la búsqueda y la actualización dinámica de los contenidos de la página:

$('#buscar').click(cargarCumpleanyos);

Poniéndolo todo junto 


Para ver el resultado completo de todo lo anterior, ir a los siguiente enlaces, donde están las páginas funcionando y el código fuente de las mismas:



Conclusiones y siguientes pasos


La dinamización de una maqueta en HTML que deba tener una cantidad variable de elementos repetitivos es más compleja que la de una maqueta con una cantidad fija de elementos y requiere una reestructuración de la maqueta. Sin embargo, con organización y metódica pueden obtenerse muy buenos resultados con muy poco código en JavaScript, reduciéndose el proceso en general a:

  1. Modificar la maqueta para:
    1. Prepararla para su actualización desde JavaScript.
    2. Crear una plantilla de los elementos repetitivos.
  2. Crear funciones en JavaScript para cargar y visualizar el contenido dinámico, remplazando en la plantilla los valores cargados.
  3. Ejecutar las funciones de carga y visualización, luego de la ejecución de un evento determinado.

Ahora, una vez vista la forma de generar estos contenidos dinámicos de forma manual, lo siguiente es aprender a hacerlo usando un framework o librería apropiados para esta tarea que, con toda seguridad, nos facilitará el proceso y proveerá de muchas más opciones.

Una buena opción para continuar es el tutorial interactivo de Knockout disponible en http://learn.knockoutjs.com, donde enseñan los conceptos y la práctica de la separación de la presentación de la información, de la lógica de su operación, mientras aprendemos a usar la biblioteca.



Si te ayudó este tutorial, compártelo con otros y si tienes alguna sugerencia o cambio para mejorarlo... haz tus comentarios.

domingo, 24 de agosto de 2014

Convertir una maqueta en HTML en una página Web con contenido dinámico (1)

Después de maquetar una página, el paso siguiente es modificarla para que su contenido pase a ser dinámico. Lo ideal sería utilizar un framework o una librería que facilitara este trabajo, como un sistema de plantillas como Mustache o un framework más completo como Knockout o AngularJS; pero la base del proceso siempre es la misma, tanto si usas un framework como si lo haces todo a mano.

Aquí vamos a explicar el proceso, así que, para no desviarnos con las particularidades de uno u otro framework, vamos a dinamizar esa maqueta en HTML con JavaScript "a secas", sin el apoyo de ninguna libería, ni siquiera jQuery.

Para ello, vamos a dividir el proceso en dos casos cuyas soluciones presentaremos por separado: dinamizar el contenido de una maqueta con una cantidad fija de información y, después, dinamizar el contenido de una maqueta con una cantidad de información variable.

Ejemplo de sección con una cantidad fija de información


Supongamos que tenemos la Web de una escuela en la que, cada día, se publican los datos de un alumno destacado mostrando el nombre del alumno, su foto y un pequeño párrafo diciendo los motivos por los que se le considera un alumno destacado. La sección maquetada podría ser como sigue:

<div id="alumnoDestacado">
<h3>Nombre del alumno</h3>
<img src="img/persona.png" />
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
   Mauris vehicula dictum lectus vel auctor.</p>
</div>

Cuyo resultado es el siguiente (después de formatearlo un poco con CSS):

Nombre del alumno


Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris vehicula dictum lectus vel auctor.

Supongamos ahora que en dicha Web, hay un servicio REST que, al consultarlo, devuelve un JSON con los datos del alumno destacado. Por ejemplo, el JSON podría tener la estructura siguiente:

{
    "nombre": "María López González",
    "foto": "img/alumnos/maria-lopez-gonzalez.jpg",
    "motivo": "Galardonada en las Olimpiadas Municipales de Lengua Inglesa"
}

En la maqueta anterior, aún no hay cambios en el contenido pues es solo una maqueta. Entonces: ¿cómo transformar la maqueta para mostrar dinámicamente el contenido del JSON obtenido desde servicio REST?

Proceso para convertir el contenido de una maqueta en HTML con una cantidad fija de información en una Web con contenido dinámico


Por supuesto, hay muchas formas de solucionar este problema. Aquí propondremos una (de entre tantas) en la que basarse para buscar alternativas y soluciones propias. De modo que el proceso quedaría como sigue:

  1. Identificar el evento ante el cuál queremos actualizar la plantilla con los datos dinámicos.
  2. Modificar la plantilla HTML asignando un identificador único a cada elemento cuyo contenido queremos actualizar dinámicamente (desde JavaScript).
  3. Crear una función en JavaScript que reciba como parámetro los datos a mostrar y modifique el HTML para reflejar los cambios. Para ello, para cada dato a actualizar:
    1. Obtener el elemento HTML con el identificador correspondiente al dato a actualizar
    2. Modificar el atributo a actualizar, con el dato correspondiente
  4. Crear una función en JavaScript que cargue los datos que se quieren mostrar y llame a la función del paso 3 con los datos cargados.
  5. Cuando se produzca el evento deseado (identificado en el paso 1), llamar a la función de carga de datos (creada en el paso 4).
  6. Fin.

Para ilustrar el proceso anterior, veamos cómo dinamizar los datos de la plantilla del "alumno destacado".

Ejemplo del proceso, ilustrado y explicado, paso a paso


1 Identificar el evento ante el cuál queremos actualizar la plantilla con los datos dinámicos


En la Web de la escuela, el proceso de selección del alumno destacado se hace una vez al día; por tanto, solo vamos a actualizar la plantilla cuando se cargue la página. Por tanto, el evento para actualizar la plantilla será cuando la página se ha cargado y está lista para su actualización.

2 Modificar la plantilla HTML asignando un identificador único a cada elemento cuyo contenido queremos actualizar dinámicamente (desde JavaScript)


En este caso, los datos a mostrar son el nombre, la foto y la descripción que se corresponderán con las etiquetas h3, img y p, respectivamente. De modo que, asignaremos un id único a cada etiqueta para su actualización desde JavaScript, quedando la plantilla modificada, como sigue:

<div id="alumnoDestacado">
<h3 id="nombre">Nombre del alumno</h3>
<img id="foto" src="img/persona.png" />
<p id="motivo">Lorem ipsum dolor sit amet, consectetur adipiscing elit.
   Mauris vehicula dictum lectus vel auctor.</p>
</div>

3 Crear una función en JavaScript que reciba como parámetro los datos a mostrar y modifique el HTML para reflejar los cambios.


Este es el pollo del arroz con pollo: aquí es donde hay que poner énfasis pues es la parte donde se hace la actualización de los datos en cuestión. Para ello, hay que intentar ser claros y consecuentes con los nombres que se asignen a variables y funciones. La función que vamos a implementar va a actualizar los datos del alumno destacado así que la llamaremos actualizarAlumnoDescataco. Luego, recibirá los datos del alumno destacado, por lo que tendrá un parámetro que llamaremos datosAlumno. Finalmente, va a actualizar los datos nombre, foto y motivo; por lo que el proceso de actualización será:

  1. Localizar el elemento que contendrá el nombre y actualizar el texto del mismo, con el nombre del alumno.
  2. Localizar el elemento que contendrá la foto y actualizar la url de la imagen, con la url de la foto del alumno.
  3. Localizar el elemento que contendrá el motivo y actualizar el texto del mismo, con el motivo por el que se destaca el alumno.

Teniendo en cuenta lo anterior, el código de la función quedaría más o menos como sigue:

/**
* Actualiza los datos de un alumno, en la sección de alumno destacado
* @param datosAlumno - Objeto con los datos del alumno: nombre, foto y motivo
*/
function actualizarAlumnoDescatado(datosAlumno) {
    $('#nombre').text(datosAlumno.nombre);
    $('#foto').attr('src', datosAlumno.foto);
    $('#motivo').text(datosAlumno.motivo);
}

4 Crear una función en JavaScript que cargue los datos que se quieren mostrar y llame a la función del paso 3 con los datos cargados


Usualmente, la fuente de obtención de datos puede variar: puede venir de un servicio REST propio, de uno externo, o ser un dato calculado o generado a partir de otros ya cargados. De modo que la implementación de esta función variará, según la fuente de los datos. No obstante, en el caso del alumno destacado hemos supuesto que la Web cuenta con un servicio REST que devuelve un JSON con los datos del alumno; por lo que, en este punto, necesitaremos:

  1. Identificar la Url del servicio REST
  2. Pedir los datos JSON mediante Ajax
  3. Llamar a la función del paso 3, con los datos obtenidos.

Una vez más, intentaremos ser organizados y consecuentes con los nombres, así que supongamos que el servicio REST está en la misma Web, en la Url relativa "api/alumnoDestacado" y que llamaremos a la función cargarAlumnoDestacado, quedando la función como sigue:

/**
* Carga los datos del alumno destacado y actualiza la interfaz 
* con los datos cargados
*/
function cargarAlumnoDestacado() {
    var url = "api/alumnoDestacado";
    $.getJSON(url, function (result) {
        if (result) {
            actualizarAlumnoDestacado(result);
        }
    });
}

Esta implementación, por supuesto, es muy básica e ignora muchos posibles casos de fallo: ¿Qué pasa si no se obtienen resultados del servicio REST? ¿Qué pasa si se produce un error en la llamada Ajax? ¿Qué pasa si los datos no tienen la estructura esperada...? Estos y muchos otros fallos quedan aquí sin tratar, porque complejizarían el código de la función y lo que pretendemos aquí es mostrar lo mínimo necesario para lograr un objetivo: cargar los datos desde un servicio y mostrarlo en nuestra Web. De modo que, queda como ejercicio personal extender éste código y tratar todos los casos que se nos puedan ocurrir.

5 Cuando se produzca el evento deseado (identificado en el paso 1), llamar a la función de carga de datos (creada en el paso 4)


Este paso final es el aglomerador: aquí es donde pondremos a funcionar nuestro código, partiendo del acontecidmiento de un evento determinado. En nuestro ejemplo, definimos que la carga y visualización del alumno destacado se produciría una única vez, después de haber cargado la página; por lo que la llamada a la función de carga de datos quedaría como sigue:

$(document).ready(cargarAlumnoDestacado);

Poniéndolo todo junto


Para ver el resultado completo de todo lo anterior, ir a los siguiente enlaces, donde están las páginas funcionando y el código fuente de las mismas:


Conclusiones y siguientes pasos


Como hemos visto hasta aquí, el proceso para convertir el contenido de una maqueta en HTML en una Web con contenido dinámico puede reducirse a:

  1. Modificar la maqueta para prepararla para su actualización desde JavaScript.
  2. Crear funciones en JavaScript para cargar y visualizar el contenido dinámico.
  3. Ejecutar las funciones de carga y visualización, luego de la ejecución de un evento determinado.

No obstante, las particularidades de la implementación varían si la cantidad de contenido dinámico es fija o variable. Aquí mostramos el proceso para convertir el contenido de una maqueta en HTML con una cantidad fija de información en una Web con contenido dinámico y en el post siguiente veremos el proceso para convertir el contenido de una maqueta en HTML con una cantidad variable de información en una Web con contenido dinámico.

Si tienes alguna duda, comentario o sugerencia... ponla en los comentarios.