lunes, 11 de junio de 2007

Ajax... qué lío

Acabo de hacer algo intencionadamente con Ajax. Quiero decir que no es como el PaginatorHelper, que te lo hace todo. O sea: que me he planteado un pequeño ejercicio con Ajax y me ha salido.

Eso sí, con cierto esfuerzo. Voy a intentar poner aqui las ideas que han ido surgiendo, aunque no creo que ponga código, al menos de momento. Entre otras cosas, porque no está conseguido todo lo necesario para considerarlo completo.

El ejercicio

Consistía en combinar un formulario para añadir entradas en un tabla (citas o frases célebres y su autor) con la propia tabla. Al escribir una nueva frase en el formulario y pulsar el botón de enviar tendría que actualizarse la tabla.

La función de las técnicas Ajax aquí es enviar los datos del formulario al servidor, elicitar la acción add correspondiente en el controlador y recoger el nuevo contenido de la tabla

Suena bien. Hacerlo tiene truco.

Lo primero es planificar un poco la página que se va a generar y ver cuántas zonas (div) necesitamos establecer. En principio, aquí debería llegarnos con una para el formulario y otra para la tabla.

Luego habría que tener vistas para generar el contenido de cada una de estas partes, más una vista que las combine todas. Las vistas de partes serían para actualizar cada div cuando toque, la vista combinada sería para usar la primera vez.

Esta es una parte que no tengo del todo clara cómo organizar. El tutorial que he seguido está basado en la versión 1.1 de CakePHP y algunas cosas no son exactamente iguales.

Por una parte, es posible utilizar Controller::render($vista, 'ajax') para explicitar la vista que queremos mostrar usando el modo "Ajax". Combinando eso con una consulta Controller::ResquestHandler->isAjax () para ver si la petición nos la hace Ajax, podemos hacer que un mismo controlador extraiga los datos necesarios y decida si debe devolver la vista "normal" o la actualización "ajax". No sé si me explico.

Ejemplo. La acción index podría tener una view asociada, llamada también index, que genere tanto el formulario como la tabla de datos (¿me sigues?) y que usaría ante una petición normal. Por otro lado, podríamos escribir una view para actualizar sólo la parte de la tabla y que se usaría para atender una petición Ajax.

Nos quedaría algo así:


function index () {
$data = $this->Paginate ();
$this->set ('frases', $data);
if ($this->RequestHandler->isAjax()) {
$this->render ('tabla');
}
}
No sé si se aprecia bien. El último if nos permite indicar una view específica que genera el contenido que queremos devolver.

Recuerda que tenemos que usar el component RequestHandler y llamar a su método startup en beforeFilter para que funcionen bien estas peticiones Ajax (de otro modo, se empeña en devolverlas con layout y esto es un despiporre).

Las acciones implicadas tienen que saber qué actualizan

Esto me hizo rascarme la cabeza un montón hasta que cai de la burra. Resulta que en mi ejercicio todo funcionaba bien hasta que añadía una entrada a la tabla y trataba de ordenar ésta. Bueno, pues se añadían más entradas, y se generaba un error del layout y no salía la tabla.

Bien. Resulta que el boton de enviar el formulario llamaba a la acción add del controlador. Correcto. Pero la acción add, aparte de añadir el nuevo registro, tiene que volver a pedir los datos para mostrar la tabla. Eso por un lado.

(Nota: lo que no he mirado es si sería más adecuado y económico llamar a la acción index...).

Por otro lado, hay que controlar que la acción add dibuje la vista que genera el contenido de la tabla (en el ejemplo de arriba, es tabla.ctp, en lugar de su add.ctp que dibujaría normalmente), al menos cuando es llamada por Ajax. Si no es llamada por Ajax debería dibujar su vista normal, o hacer algo que le explique al usuario qué pasa o qué puede hacer.

Y, por último...

Más cosillas a las que prestar atención

Al generar enlaces o botones que hagan llamadas Ajax hay que asegurarse de que llaman a la acción adecuada del controlador adecuado. Es posible que tengas que especificar explícitamente la acción y no dejar que sea CakePHP el encargado.

Esto es así porque si dibujamos una vista desde una acción que no es la "propia" (como en el apartado anterior que dibujábamos tabla desde index, pero también desde add, Cake interpreta que esos enlaces se dirigen a la acción "generadora", si no hemos indicado otra.

En mi ejemplo, debo especificar en las opciones del paginador de la tabla, que la acción a la que llaman los enlaces Ajax que ordenan la tabla ha de ser siempre index.

1 comentario:

boriscy dijo...

Lo que hice para no tener este problema fuen en el AppController añadir


function beforeFilter()
{
if($this->RequestHandler->isAjax() || isset($_GET['ajax'])){
$this->layout='ajax';
}
}


//
Obviamente debes llamar el componete RequestHandler en el AppController. La razon por la que use el parametro $_GET es de que el IE6 en Winxp SP1 o menor no no puede detectar el encabezado detectar si una llamada es AJAX aunque si posee ajax.