Tantos proyectos y tan poco tiempo, entre la universidad y el trabajo me queda muy poco tiempo para trabajar con Caleio. Más si tenemos en cuenta que esta semana pasaba re-escribí mi CMS y rediseñé la página. Y por si esto no fuera poco estoy escribiendo un programa bastante importante en C# (no pregunten porque no voy a responder).
El punto es que quería escribir un artículo sobre Ajax. Ultimamente estuve usandolo mucho en el trabajo y con Caleio y me crucé con un problema interesante. La definición de Ajax en inglés es: Asynchronous JavaScript + XML
según quien acuño el termino. Notese el XML del finál. Importante ese XML ya que todo el mundo parece ignorarlo… Investigando un poco más en dicha página aparece esta ‘lista’ de tecnologías que se incluyen en el concepto de Ajax:
- standards-based presentation using XHTML and CSS;
- dynamic display and interaction using the Document Object Model;
- data interchange and manipulation using XML and XSLT;
- asynchronous data retrieval using XMLHttpRequest;
and JavaScript binding everything together.
Por alguna extraña razón NADIE que yo haya visto hasta ahora usa XML para pasar la información entre el servidor y el cliente. Es más, este problema surgió porque yo no sabía muy bien como hacerlo y buscaba un framework ya escrito para usarlo en Caleio, pero no encontré ninguno! Absolutamente ninguno, y eso que miré en varios. Esto fue antes de navidades así que capaz que la situación cambió pero en cualquier caso el 90% o más de la gente que usa Ajax no usa XML para pasar la información. El método que usa todo el mundo es el de hacer un REQUEST al servidor incluyendo la información en las variables GET o en el caso de lo más osados en las variables POST, a su vez el servidor responde con cachos de HTML que son mostrados en la página por medio de la propiedad innerHTML.
Esto hace que el código quede muy feo, que sea poco portable y limita el uso de la aplicación. Pero lo más importante es que viola totalmente la regla de separación de contenido y presentación. Tanto XHTML y CSS para después hacer esa guarrería :P
Al final conseguí un ‘framework’ bastante ligerito que recibía XML pero no lo enviaba, así que con un poco de manipulación conseguí lo que necesitaba. Es un script muy básico y en realidad no es un framework, lo único que hace es crear el objecto xmlhttprequest y luego enviar y recibir XML. Lo pueden encontrar en la sección de Code » tw-sack.js.
Así que ahí va un pequeño y corto tutorial de como hacerlo bien usando ese script. El ejemplo que usaré será una página que invierta el texto escrito en un input. El enfásis esta en como funcióna Ajax y no tanto en que se puede hacer en el servidor, es muy fácil modificar este ejemplo para que haga cosas mucho más complejas.
HTML + CSS
En primer lugar creamos una simple página HTML y le añadimos unos estilos con CSS. Para nuestro ejemplo solo necesitamos un input, un boton y un div donde mostrar el texto invertido.
<html>
<head>
<title>Ejemplo Ajax</title>
</head>
<body>
<form action="#" method="post" onsubmit="return sendText()">
<label for="texto">Texto:</label>
<input type="text" id="texto" name="texto" /><br />
<label for="submit"> </label>
<input type="submit" id="submit" name="submit" value="Enviar" />
</form>
<p id="areaResultado"> </p>
</body>
</html>
Falta el CSS y la inclusión de los archivos Javascript, pero se pueden dar una idea, nada complicado. Se podría no usar el form y llamar a la función sendText() desde el onClick() del botón, pero hecho así es más facil hacer la aplicacion compatible con los usuarios que no tengan activado Javascript ya que el formulario se ejecutará y ahí podemos hacer que el servidor se haga cargo como normalmente lo hace.
Javascript de ida
Así que el usuario ha apretado el boton y quiere saber como queda su texto al vesre. Creamos una función que saque el valor del campo, lo meta en el XML y lo mande.
function sendText()
{
texto = document.getElementById('texto').value;
xml = xml = '<?xml version="1.0" encoding="UTF-8" ?>'+
"<request>"+
"<texto>""+texto+"</texto>""+
"</request>";
ajax.object = document.getElementById('areaResultado');
ajax.onCompletion = recieveText;
ajax.runAJAX(xml);
}
Acá es donde hay una parte complicada, en la ante-última linea, donde pone ajax.onCompletion = recieveText. Esta parte es necesaria por la forma en que funcióna xmlhttprequest y Javascript. Cuando el script llega a la parte de enviar el XML no espera el resultado, sino que sigue la ejecución, asi que si fueramos a hacer:
// crear xml
ajax.runAjax(xml)
alert(ajax.reponse);
Esto fallaría, porque el proceso de conseguir el resultado es largo y el Javascript no espera. Así que el truco es decirle al script que función queremos que ejecute después de haber conseguido el resultado, en este caso la función es recieveText(). Lo de ajax.object es para ‘guardar’ el objeto donde queremos luego insertar o modificar algo. Esto sirve más cuando a la función la llamamos con un ‘this‘ y navegamos por los nodos cercanos que posiblemente no se puedan o no querramos agarrar con un getElementById().
Pero primero el PHP
Antes de recibir el resultado hay que leer el texto con PHP y darlo vuelta. Cuando por primera vez llegué a esta parte me quede frito, cómo cuernos leo yo el XML en PHP? Y la solución es mucho más simple de lo que parece, yo ya estaba pensando en que iba a tener usar sockets de alguna manera extraña y seguramente depravada, pero no, resulta que la variable $HTTP_RAW_POST_DATA contiene, como bien indica su nombre, todo el contenido POST de manera ‘cruda’, es decir, sin procesar. Asi que leer el valor de texto es cuestion de parsear la variable como XML así:
<?php
$xml = $HTTP_RAW_POST_DATA;
$p = new xmlParser;
$p->read($xml);
$texto = $p->getTagValue('texto');
header('Content-Type: text/xml');
echo
'<'.'?xml version="1.0" encoding="utf-8" ?'.'>'.
'<response>'.
'<texto>'.strrev($texto);.'</texto>'.
'</response>';
?>
Las lineas 4,5 y 6 hacen uso de una pequeña clase que escribí para parsear XML, es muy básica y la pueden encontrar acá. En cualquier caso, lo único que tienen que saber es que lee el XML y le saca el texto a la primera etiqueta <texto>. Usa principalmente xml_parse_into_struct que convierte el XML en un array, y de ahí en adelante es tan fácil como hacer un loop para buscar etiquetas, atributos y valores. Luego mandamos el header de text/xml, porque sino después xmlhttprequest no crea reponseXML, con lo cual no obtenemos las ventajas del DOM. Y finalmente escupimos el resultado usando strrev() para invertir la cadena.
Vuelta al Javascript
Antes de enviar el XML con Javascript había especificado que función quería que se ejecutase una vez que hubiese terminado de recibir la respuesta del servidor. Así que llegamos al paso final, leer el XML, sacar el valor de la cadena invertida y meterlo en #areaResultado.
function recieveText()
{
xml = ajax.responseXML;
texto = xml.getElementsByTagName('texto')[0].childNodes[0].data;
ajax.object.innerHTML = texto;
}
Otra simple función, si es que esto del Ajax esta tirao. Primero agarramos el XML que nos devolvío el servidor, como ajax esta declarado como global podemos acceder desde cualquier función. Luego navegar por el árbol de XML es igual que hacerlo por el de HTML, solo que hay que añadir algunas cosillas más para llegar a la información, y finalmente asignamos el texto al innerHTML de #areaResultado que se asignó a ajax.object antes de enviar el XML. Era por mostrar esa funciónalidad nada más, tranquilamente podriamos esquivar ese paso y hacer el getElementById() en esta función.
<voz-del-flaco-de-bricolaje>
ya veis chavales, aqui tenemos nuestro propio sistemita Ajax, recordemos los pasos que hemos seguido:
</voz>
- Escribimos el HTML y añadimos la acción que llamará al Javascript
- Escribimos la primera función Javascript que envía el XML
- Escribimos el script PHP que procesa el XML y devuelve más XML
- Y finalmente escribimos la segunda función Javascript que nos permite leer el resultado y mostrarlo en el HTML
Es verdaderamente fácil una vez que uno se acostumbrar, pero para ayudar construí una demo con todos los archivos de este ejemplo. Que disfruten, y al próximo que vea que recibe HTML y lo mete asi nomás con innerHTML…
