2011/04/11

Formularios con subidas

Me encanta AJAX, pero eso no quiere decir que tenga sus pegas. La principal y puede que la única, es que no soporta el envío de ficheros mediante POST, pero siempre hay maneras de saltarse las limitaciones con más o menos arte y además, sin tener que preocuparse de interfaces extrañas ya que es equivalente a la usada con jQuery para las llamadas AJAX. Vamos a ello.

El primer paso es saber cómo se hacen las llamadas AJAX con jQuery (documentación aquí!). Hay varias maneras de hacer lo mismo, pero la mayoría de ellas son sinónimos para simplificar la interfaz:

$.ajax() Esta es la principal y la que hace todo. Básicamente, se pasa como parámetro un objeto con los parámetros y los closures que se llamarán al generarse los eventos correspondientes. Un ejemplo de uso básico mediante POST es el siguiente, que llama a "some.php" mediante POST pasando como parámetros a la página "name" y "location" y que cuando todo va bien llama a una función que hace un alert del texto generado por some.php.
$.ajax({
   type: "POST",
   url: "some.php",
   data: "name=John&location=Boston",
   success: function(msg){
     alert( "Data Saved: " + msg );
   }
 });
Aquí va un listado de las propiedades que se le pueden pasar como parámetro

accepts
async    true
beforeSend(jqXHR, settings)
cache    true
complete(jqXHR, textStatus)
contents
contentType    'application/x-www-form-urlencoded'
context
converters    {"* text": window.String, "text html": true, "text json": jQuery.parseJSON, "text xml": jQuery.parseXML}
crossDomain    false
data
dataFilter(data, type)
dataType   Intelligent Guess (xml, json, script, or html)
error(jqXHR, textStatus, errorThrown)
global    true
headers    {}
ifModified    false
isLocal
jsonp
jsonpCallback
mimeType
password
processData    true
scriptCharset
statusCode    {}
success(data, textStatus, jqXHR)
timeout
traditional
type    'GET'
url    página actual
username
xhr
xhrFields
Como siempre, para más detalles la página de jQuery con la documentación.

Aparte de la función $.ajax(), hay varios sinónimos que dan por sentado ciertos parámetros. Los principales son los siguientes y para más detalles mirad aquí
  • $.get( url, [ data ], [ success(data, textStatus, jqXHR) ], [ dataType ] )
  • $.getJSON( url, [ data ], [ success(data, textStatus, jqXHR) ] )
  • $.getScript( url, [ success(data, textStatus) ] )
  • $.load( url, [ data ], [ complete(responseText, textStatus, XMLHttpRequest) ] )
  • $.post( url, [ data ], [ success(data, textStatus, jqXHR) ], [ dataType ] )
Ahora que ya tenemos una visión de conjunto podemos centrarnos en el problema, que es subir datos mediante un formulario. Se os puede dar el caso de que tenéis un maravilloso código que mediante AJAX hace lo que tiene que hacer pero de repente el cliente os pide que además de manejar el formulario tiene que subir un fichero adjunto. Normalmente habría que tirar a la basura todo lo hecho además de que la página tendría que ser dinámica para pasar a ser estática como se hacía hace 10 años... bueno, pues hay varias técnicas para resolverlo, pero lo más común es usar un iframe para realizar las operaciones de forma oculta sin tener que recargar la página prinicpal, pero volvemos a lo mismo, tenemos que reprogramar toda la lógica de negocio del envío y tratamiento de la información del formulario.

Aquí es donde nos ayuda este plugin de jQuery de Jaina Ewen que lo que hace es crear por nosotros la infraestructura del iframe y darnos una interfaz similar a la del método ajax de jQuery de modo que no tengamos que hacer mas que unos cambios mínimos en la página y el código.

Originalmente tendríamos un código tipo
<form id="miform"> ... </form>
Con una llamada AJAX parecida a
// Asigna el evento de envío de formulario
$('#miform').submit(enviaFormulario);

// ...

function enviaFormulario()
{
    // ... lee los valores del formulario en variables valor1, valor2, etc y otros cambios estéticos a la página ...
    $.ajax({
       type: "GET",
       url: "paginaAjax.php",
       data: "campo1=" + valor1 + "&campo2=" + valor2,
       success: function(data) {
         alert( "Datos enviados. Respuesta: " + data );
       }
    });
    return false;
}

Por lo tanto, los cambios serían:

1. Incluir el script. Es de cajón, pero a quién no se le ha olvidado alguna vez?
<script type="text/javascript" src="js/jquery.iframe-post-form.js"></script>
2.  Modificar el <form> para que sea capaz de subir ficheros. También básico, pero fácil de olvidar
<form id="miform" action="paginaAjax.php" method="post" enctype="multipart/form-data">
3.  Modificar el código para el tratamiento de ese formulario. De momento nos sobra el leer los valores de los campos del formulario para contruir la cadena del data. El método es POST sí o sí y el url viene dado por el propio tag del form. Donde antes era "success" ahora es "complete". Y con ésto ya estaría nuestro código funcionando como antes.
$('#miform').iframePostForm
({
    post : function ()
    {
        // Cambios estéticos al enviar el formulario
        // p.e. deshabilitar botones u otras cosas
    },
    complete: function(data)
    {
        alert( "Datos enviados. Respuesta: " + data );
    }
});
Pues ahora ya no hay excusas para que todas nuestras páginas sean dinámicas y además, sin usar flash.

No hay comentarios:

Publicar un comentario