AJAX
¿Qué es AJAX?
Imaginemos que estamos haciendo un formulario web de registro de
clientes que tiene cuarenta campos. Tres de esos campos son combos
para “departamento”, “provincia” y
“distrito”. Al seleccionar un “departamento”,
el combo “provincia” se debe actualizar con el contenido
correspondiente. De igual manera, al seleccionar una “provincia”:
sus distritos correspondientes deberán aparecer en su
respectivo combo. Para implementarlo tenemos dos maneras:
- Colocar en el evento “onchange” de los combos un
submit() para que se envíe el formulario actual al
servidor, y éste devuelva el mismo formulario (sin perder los
valores de los demás campos) sólo para que actualice
las opciones de los combos afectados. Como se puede predecir, se
estaría desperdiciando el ancho de banda enviando todo un
formulario sólo para cambiar uno o dos campos. Eso, sin
contar que la programación de los submit() en el
lado del servidor debe estar contemplada para saber que lo que hizo
fue cambiar un combo o se presionó en el botón
“guardar” - Cargar en javascript todos los
departamentos (26 opciones, considerando a Callao), todas las
provincias (195) y todos los distritos (1680), programar en
javascript, y actualizar los combos según convenga. En este
caso, se estaría enviando todas las opciones a elegir al
cliente sólo para que seleccione tres de todos ellos.
¿Por qué no combinamos ambas formas?
AJAX es una técnica en desarrollo web que consiste en la
mezcla de tecnologías existentes:
- XML / XSLT para el envío de datos en XML del servidor
al cliente (aunque también puede ser texto plano). - DOM + Javascript para la manipulación de los datos
enviados del servidor. - HTML / XHTML + CSS que
involucra a la presentación en lado del cliente.
La técnica consiste en pedir al servidor sólo lo
necesario para que nos devuelva tan sólo lo necesario. Si bien
el formulario – o cualquier página – presentada
por el servidor se hizo una vez, pueden haber partes de esa misma
página que se actualicen después y en cualquier
momento; de ahí el nombre AJAX: Asynchronous Javascript And
XML (Javascript y XML asíncronos).
Por ejemplo, cuando se va a redactar un correo electrónico
desde Yahoo! Mail, se puede escribir parte de la dirección,
nombre o alias del destinatario e irá apareciendo un listado
de los posibles nombres tomados de la lista de contactos.
A continuación, se mostrará cómo implementar
AJAX en nuestras aplicaciones Web. Primero, de una manera nativa.
Después, utilizando Ajax-Tags, una potente biblioteca de tags
disponible en http://ajaxtags.sourceforge.net/.
Conociendo AJAX
Nuestro ejemplo consiste en listar nombres de tipos de producto en
un select. Al seleccionar uno de estos, se mostrarán
sus productos asociados en otro select.
Primero, debemos describir la forma cómo se van a recibir
los productos de un determinado tipo. Podemos utilizar el formato
text/plain, pero, a todas luces, el más recomendable es el XML
ya que podemos enviar en un sóolo archivo varios datos
estructurados.
Entonces, haremos un Servlet que devolverá un XML con los
productos de un determinado tipo enviado por parámetro. Por lo
tanto, utilizaremos el esquema MVC para recuperar la información
de una base de datos. Pero, para evitarnos problemas en la
configuración y llenado de información de la base de
datos, utilizaremos listas bajo el patrón DAO.
Paso 1: Preparando el DAO
Existirán dos clases que serán los beans de Tipo
y de Producto.
package com.jugperu.tutores.ajax.beans;
public class Tipo {
private String id;
private String tipo;
public Tipo(String id, String nombre) {
this.id=id;
this.tipo =nombre;
}
public String getId() {
return id;
}
public String getTipo() {
return tipo;
}
public void setId(String id) {
this.id = id;
}
public void setTipo(String tipo) {
this.tipo = tipo;
}
}
package com.jugperu.tutores.ajax.beans;
public class Producto {
private String id;
private String producto;
private Tipo tipo;
public Producto(String id, String nombre,Tipo tipo) {
this.id=id;
this.producto =nombre;
this.tipo=tipo;
}
public String getId() {
return id;
}
public String getProducto() {
return producto;
}
public Tipo getTipo() {
return tipo;
}
public void setId(String id) {
this.id = id;
}
public void setProducto(String producto) {
this.producto = producto;
}
public void setTipo(Tipo tipo) {
this.tipo = tipo;
}
}
Nuestra interfaz DAO.
package com.jugperu.tutores.ajax.dao;
import java.util.List;
import com.jugperu.tutores.ajax.beans.Producto;
public interface MercadoDAO {
public List<Producto> productosPorTipo(String tipo);
}
y la implementación para manejo de listas. En el constructor,
prepararemos la data para nuestro ejemplo.
package com.jugperu.tutores.ajax.dao;
import java.util.ArrayList;
import java.util.List;
import com.jugperu.tutores.ajax.beans.Producto;
import com.jugperu.tutores.ajax.beans.Tipo;
import java.util.*;
public class ListasMercadoDAO implements MercadoDAO {
private static List<Tipo> tipos = new ArrayList();
private static List<Producto> productos = new ArrayList();
static {
Tipo frutas, verduras, carnes;
tipos.add(frutas = new Tipo("frt", "Frutas"));
productos.add(new Producto("mzn", "Manzanas", frutas));
productos.add(new Producto("per", "Peras", frutas));
productos.add(new Producto("uva", "Uvas", frutas));
tipos.add(verduras = new Tipo("ver", "Verduras"));
productos.add(new Producto("lec", "Lechuga", verduras));
productos.add(new Producto("pap", "Papas", verduras));
productos.add(new Producto("esp", "Espinaca", verduras));
tipos.add(carnes = new Tipo("crn", "Carnes"));
productos.add(new Producto("bis", "Bisket", carnes));
productos.add(new Producto("lom", "Lomo", carnes));
productos.add(new Producto("pol", "Pollo", carnes));
}
public List<Producto> productosPorTipo(String tipo) {
List lista = new ArrayList();
for (Iterator<Producto> iter = productos.iterator(); iter.hasNext(); ) {
Producto pro = iter.next();
if (pro.getTipo().getId().equals(tipo)) {
lista.add(pro);
}
}
return lista;
}
}
Y ahora nuestro DAOFactory.
package com.jugperu.tutores.ajax.dao;
public class DAOFactory {
public static final int LISTAS = 1;
public static MercadoDAO crearMercado(int tipoRDBMS) {
switch (tipoRDBMS) {
case LISTAS:
return new ListasMercadoDAO();
}
return null;
}
}
Este DAO nos permitirá, a parte de tener una buena costumbre
de tener todo en orden, reutilizarlo para otros ejemplos.
Paso 2: Creando el Servlet que devuelve XML
Nuestro servlet, simplemente, obtendrá el parámetro
“tipo”, lo buscará en la 'base de datos',
preparará el XML y lo devolverá al cliente. Si no
existiese, o no hubiera enviado algún parámetro, el
servlet no devolverá nada.
package com.jugperu.tutores.ajax.servlets;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import com.jugperu.tutores.ajax.dao.MercadoDAO;
import com.jugperu.tutores.ajax.dao.DAOFactory;
import com.jugperu.tutores.ajax.beans.Producto;
public class ProductoPorTipoServlet extends HttpServlet {
private static final String CONTENT_TYPE = "text/xml";
public void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
response.setContentType(CONTENT_TYPE);
MercadoDAO dao = DAOFactory.crearMercado(DAOFactory.LISTAS);
String tipo = request.getParameter("tipo");
List<Producto> prods = null;
if (tipo != null) {
prods = dao.productosPorTipo(tipo);
if (prods.size() > 0) {
System.out.println("obteniendo productos de "+tipo);
PrintWriter out = response.getWriter();
out.println("<?xml version=\"1.0\"?>");
out.println("<productos>");
for (Iterator<Producto> iter = prods.iterator(); iter.hasNext(); ) {
Producto prod = iter.next();
out.println("<producto id=\"" + prod.getId() + "\" >" +
prod.getProducto() + "</producto>");
}
out.println("</productos>");
return;
}
}
System.out.println("no se obtuvo nada de "+tipo);
response.setStatus(response.SC_NO_CONTENT);
}
}
Agregamos la definición de este servlet en nuestra aplicación
web, es decir, editamos el archivo web.xml y colocamos las siguientes
líneas.
<servlet>
<servlet-name>ProductoPorTipo</servlet-name>
<servlet-class>com.jugperu.tutores.ajax.servlets.ProductoPorTipoServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ProductoPorTipo</servlet-name>
<url-pattern>/servlets/ajax/productoportipo</url-pattern>
</servlet-mapping>
Ahora, ejecutamos nuestra aplicación, llamados al servlet y le
damos un parámetro, por ejemplo “tipo=frt”.
Paso 3: Haciendo que un cliente acceda al XML
utilizando Javascript
El cliente será un html que muestre dos select: uno con los
tipos, y otro de los productos pero no tendrá valores. Lo
fuerte de este cliente es el javascript que obtendrá el XML y
preparará los valores obtenidos para agregar opciones al
select de productos.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1" lang="es">
<title>combo</title>
<script type="text/javascript">
function mostrarProductos(){
var tipo=document.getElementById("tipo");
//esta será nuestra petición
var url='<%=request.getContextPath()%>/servlets/ajax/productoportipo?tipo='+tipo.value;
//obtenemos el objeto para manejar peticiones en XML...
if (window.XMLHttpRequest) req = new XMLHttpRequest();
//.. tambien puede estar como ActiveX
else if (window.ActiveXObject) req = new ActiveXObject("Microsoft.XMLHTTP");
//asociamos un metodo alevento que se ejecuta a medida que vaya leyendo el XML
req.onreadystatechange = processRequest;
//pedimos el xml
req.open("GET", url, true);
//no enviamos nada y termina
req.send(null);
}
function processRequest(){ //este metodo se ejecuta a medida que va cargando el XML.
/** los estados pueden ser
0: no se ha iniciado
1: esta cargando
2: ya ha cargado
3: esta intercambiando con el cliente
4: ha terminado
*/
if (req.readyState==4){
/*y cuando haya terminado procesa el mensaje
(200: el requerimiento ha sido satisfactorio)*/
if (req.status==200) parseMessages();
//sino, hubo un problema al obtener el XML
else alert("no se pudo obtener los datos de la marca");
}
}
function parseMessages(){
//obtenemos el XML
var response=req.responseXML.documentElement;
//obtenemos de nuestro HTML la zona donde escribiremos el resultado
var cboProducto=document.getElementById("producto");
cboProducto.disabled=false;
var productos=response.getElementsByTagName("producto");
cboProducto.options.length=productos.length;
for(i=0;i<productos.length;i++){
var prod=productos[i];
cboProducto.options[i].value=prod.getAttribute("id");
cboProducto.options[i].innerHTML=prod.firstChild.data;
}
}
</script>
</head>
<body>
<h1>Ajax Combo</h1>
<form action="."> Tipo:
<select name="tipo" id="tipo" onchange="mostrarProductos()">
<option value=" ">Elija un tipo</option>
<option value="frt">Frutas</option>
<option value="ver">Verduras</option>
<option value="crn">Carnes</option>
</select>
Producto:
<select name="producto" id="producto" disabled="disabled">
<option value=" ">Elija un producto</option>
</select>
</form>
</body>
</html>
Ahora, abrimos esta página y vemos los resultados al
seleccionar un tipo de producto.
AJAX-TAGS
Hasta el momento no hemos hecho algo extraordinario en Java con lo
que respecta a AJAX. Este ejemplo fue para entender la forma cómo
interactúa el javascript del cliente con el servidor
utilizando XML. Podemos predecir que programar todos los javascript
para nuestros formularios pueda resultar un suplicio.
Afortunadamente, unos programadores han elaborado unos taglibs
que nos permitirá crear aplicaciones web utilizando AJAX en
poco tiempo.
AJAX Tag Library permite:
- Crear autocompletes en campos text.
- Llenar opciones de un select basado en la selección
de otro campo. - Mostrar globos de algún texto seleccionado en la
página web. - Refrescar campos de formulario, y
- Alternar imágenes
dependiendo del estado de un campo (como encendido/apagado)
Podemos encontrar esta biblioteca en
http://ajaxtags.sourceforge.net/
Realizaremos el mismo ejemplo anterior, pero utilizando AjaxTags.
Por tanto, reutilizaremos el DAO y cambiaremos el Servlet que genera
el XML y el cliente.
Paso 2a: Creando el Servlet que devuelve XML
AjaxTags recibe XML que tengan el siguiente formato.
<?xml version="1.0" encoding="UTF-8"?>
<ajax-response>
<response>
<item>
<name>Registro 1</name>
<value>1</value>
</item>
<item>
<name>Registro 2</name>
<value>2</value>
</item>
<item>
<name>Registro 3</name>
<value>3</value>
</item>
</response>
</ajax-response>
Podemos hacer que nuestro Servlet cree el XML en ese mismo formato, o
podemos utilizar una clase de AjaxTags
(org.ajaxtags.helpers.AjaxXmlBuilder) que arma el XML
dependiendo del parámetro que reciba. También, provee
una clase abstracta Servlet (org.ajaxtags.servlets.BaseAjaxServlet)
que tiene todo el código necesario para devolver un XML al
cliente, sólo nos toca armar la lista de elementos del XML.
package com.jugperu.tutores.ajax.servlets;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.jugperu.tutores.ajax.dao.DAOFactory;
import com.jugperu.tutores.ajax.dao.MercadoDAO;
import org.ajaxtags.helpers.AjaxXmlBuilder;
import org.ajaxtags.servlets.BaseAjaxServlet;
public class AjaxTagsProductoPorTipoServlet extends BaseAjaxServlet {
public String getXmlContent(HttpServletRequest request,
HttpServletResponse response) throws
Exception {
MercadoDAO dao = null;
dao = DAOFactory.crearMercado(DAOFactory.LISTAS);
String tipo = request.getParameter("tipo");
List lista = dao.productosPorTipo(tipo);
return new AjaxXmlBuilder().addItems(lista, "producto", "id").toString();
}
}
También existe la clase org.ajaxtags.servlets.BaseAjaxAction
que es similar a BaseAjaxServlet sólo que está
orientado para Struts.
Ahora, configuraremos nuestra aplicación web para que
contemple este servlet.
<servlet>
<servlet-name>AjaxTagsProductoPorTipo</servlet-name>
<servlet-class>com.jugperu.tutores.ajax.servlets.AjaxTagsProductoPorTipoServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AjaxTagsProductoPorTipo</servlet-name>
<url-pattern>/servlets/ajax/ajaxtags-productoportipo</url-pattern>
</servlet-mapping>
Ejecutamos el servlet y le damos el mismo parámetro para ver
el resultado en nuestro navegador.
Paso 3a: Haciendo que un cliente acceda al XML
utilizando AjaxTags
Ahora, nuestro jsp. En este caso, se llamará a dos
javascript que vienen incluidos en el ajaxtags.
<%@taglib uri="http://ajaxtags.org/tags/ajax" prefix="ajax"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:set var="contextPath"><%=request.getContextPath() %></c:set>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1" lang="es">
<script type="text/javascript" src="<c:out value='${contextPath}'/>/js/prototype-1.3.1.js"></script>
<script type="text/javascript" src="<c:out value='${contextPath}'/>/js/ajaxtags-1.1.5.js"></script>
<title>ajaxtags-combo</title>
</head>
<body>
<h1>AjaxTags Combo</h1>
<form action="."> Tipo:
<select name="tipo" id="tipo">
<option value=" ">Elija un tipo</option>
<option value="frt">Frutas</option>
<option value="ver">Verduras</option>
<option value="crn">Carnes</option>
</select>
Producto:
<select name="producto" id="producto">
<option value=" ">Elija un producto</option>
</select>
</form>
<ajax:select baseUrl="${contextPath}/servlets/ajax/ajaxtags-productoportipo"
parameters="tipo={tipo}" source="tipo"
target="producto"/>
</body>
</html>
Ejecutamos ahora el jsp y veremos los resultados:
Autocomplete
También se puede hacer un input text que tenga
autocomplete. Para ello, se debe implementar un método
en ListasMercadoDAO que se encargue de devolver una lista
con todos los productos cuyos nombres comiencen con determinada letra
o letras.
public List<Producto> productosPorNombre(String nombre) {
if (nombre.trim().equals("")) {
return null;
}
List lista = new ArrayList();
for (Iterator<Producto> iter = productos.iterator(); iter.hasNext(); ) {
Producto pro = iter.next();
if (pro.getProducto().toLowerCase().startsWith(nombre.toLowerCase())) {
lista.add(pro);
}
}
return lista;
}
Debería existir, naturalmente, la declaración de este
método en la clase interfaz MercadoDAO.
También, se debe crear un servlet que reciba el
parámetro del cliente, le pida al DAO los productos y que
devuelva el XML.
package com.jugperu.tutores.ajax.servlets;
import java.util.*;
import javax.servlet.http.*;
import com.jugperu.tutores.ajax.beans.*;
import com.jugperu.tutores.ajax.dao.*;
import org.ajaxtags.helpers.*;
import org.ajaxtags.servlets.*;
public class AjaxTagsProductoPorNombre extends BaseAjaxServlet {
public String getXmlContent(HttpServletRequest request,
HttpServletResponse response) throws
Exception {
MercadoDAO dao = null;
dao = DAOFactory.crearMercado(DAOFactory.LISTAS);
String producto = request.getParameter("producto");
List<Producto> lista = dao.productosPorNombre(producto);
System.out.println("se encontraron "+lista.size()+" que cumplen con '"+producto+"'" );
return new AjaxXmlBuilder().addItems(lista, "producto", "tipo.tipo").
toString();
}
}
Declaramos el servlet en web.xml
<servlet>
<servlet-name>AjaxTagsProductosPorNombre</servlet-name>
<servlet-class>com.jugperu.tutores.ajax.servlets.AjaxTagsProductoPorNombre</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AjaxTagsProductosPorNombre</servlet-name>
<url-pattern>/servlets/ajax/ajaxtags-productoPorNombre</url-pattern>
</servlet-mapping>
Y creamos el jsp que se presentará al usuario.
<%@taglib uri="http://ajaxtags.org/tags/ajax" prefix="ajax"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:set var="contextPath"><%=request.getContextPath() %></c:set>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<link href="css/ajaxtags-sample.css" rel="stylesheet" rev="stylesheet">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" lang="es">
<script type="text/javascript" src="<c:out value='${contextPath}'/>/js/prototype-1.3.1.js"></script>
<script type="text/javascript" src="<c:out value='${contextPath}'/>/js/ajaxtags-1.1.5.js"></script>
<title>ajaxtags-autocomplete</title>
</head>
<body>
<h1>AjaxTags Autocomplete</h1>
<form action="."> Producto:
<input id="producto" type="text" name="producto" class="autocomplete" />
<br />
Tipo
<input id="tipo" name="tipo" type="text" />
<ajax:autocomplete baseUrl="${contextPath}/servlets/ajax/ajaxtags-productoPorNombre"
className="autocomplete" parameters="producto={producto}" source="producto" target="tipo"/>
</form>
</body>
</html>
El resultado es el siguiente:
El efecto visual de mostrar una lista de elementos en el input
text se logra utilizando CSS y javascript. AjaxTags viene
con un archivo .css de ejemplo, que contiene algunas definiciones de
clases útiles. En este ejemplo, hemos usado la clase style
“autocomplete”. El javascript se encarga de escribir en
el HTML un div que tiene la particularidad de mostrarse flotando
sobre el html (justamente, debajo del input text) y crea la
lista de elementos enviados por el servlet.
El código que generó el javascript es como este.
<div style="overflow: visible; top: 102px; left: 71px; min-width: 153px; z-index: 20;"
id="ajaxAutocompletePopup" class="autocomplete">
<ul>
<li id="Frutas">Peras</li>
<li class="selected" id="Verduras">Papas</li>
<li id="Carnes">Pollo</li>
</ul>
</div>
Conclusiones
Recordemos que AJAX no es una tecnología, es una técnica
que reúne tecnologías existentes. Si bien es cierto que
en estas últimas semanas han aparecido IDEs que soportan AJAX
(como JDeveloper y netbeans 5) no significa que con sólo estos
productos podamos hacer lo que acabamos de ver.
Al incorporar AJAX, podremos hacer que nuestras páginas
sean más dinámicas sin sacrificar ancho de banda,
dándole al usuario una sensación de “respuesta
inmediata” en cada interacción que realiza.
Bibliografía
- AjaxTags: http://ajaxtags.sourceforge.net/
- Wikipedia, definición de AJAX:
http://es.wikipedia.org/wiki/AJAX - XML.com, respuestas de xml y http:
http://www.xml.com/pub/a/2005/02/09/xml-http-request.html
Comentarios
Publicar un comentario
Si quieres hacer una pregunta más específica, hazla en los foros que tenemos habilitados en Google Groups
Ah! solo se permiten comentarios de usuarios registrados. Si tienes OpenID, bienvenido! Puedes obtener su OpenID, aquí: http://openid.net/