Ajax con Webform

Me ha surgido en varias ocasiones la necesidad de crear un formulario en el que los valores de un campo dependan del valor de otro. Para entendernos, pongamos un formulario en el que el usuario rellene lo que quiere comer, y dependiendo del tipo de dieta elegida veremos unos valores a elegir u otros en un campo.

Por ejemplo:

1. Si quiere comer vegetales las opciones serían:

a) Espinacas

b) Acelga

c) Brocoli

2. Si quiere comer carne:

a) Ternera

b) Cerdo

c) pollo

3. Si quiere comer legumbres:

a) Garbanzos

b) Lentejas

c) Judías blancas

 

En nuestro formulario encontraríamos un campo para seleccionar el tipo de comida (vegetales, carne o legumbres) y en función de este valor rellenaríamos el otro con los valores correspondientes.

 

Cómo hacerlo

A mí me ha tocado hacerlo tanto en Drupal 6 como en Drupal 7. Si comparamos los casos la solución es bien distinta porque en el primero me baso en un archivo JavaScript para cambiar el contenido del campo y en el segundo voy a utilizar las funciones que trae Drupal 7 para esto. Si bien parece que la segunda opción es más sencilla, no lo es.

El manejo de Ajax para los formularios es conceptualmente algo complicado y hay que ser conscientes de lo que hacemos en cada momento porque cualquier error hará que no funcione. Además la información que he encontrado en Drupal.org nos habla del caso en el que creemos el formulario con el Form API de Drupal (es decir, picándolo en código) y no si lo creamos desde el módulo Webform. Aunque el proceso es parecido, el lugar de las variables cambia un poco y tenemos que ajustarlo.

 

Hacerlo en Drupal 6

En primer lugar tenemos que crear una URL en nuestro Drupal que sirva para recuperar los datos con los que queremos poblar el segundo desplegable. Para eso nos valemos de hook_menu. Lo que haremos será decirle que en la ‘mimoduloajaxwebform/js’ URL de nuestro sitio nos muestre los datos de las comidas disponibles que habrá, gracias a la ejecución de la función ‘_mimoduloajaxwebform_get_menu_select_options’:

Ahora vamos a desarrollar esa función:

Vemos que tiene un parámetro, que es el identificador del menú por el que queremos filtrar. Esto lo que consigue es que cuando en la URL que hemos creado añadamos un parámetro al final de la misma, solo nos devuelva los valores que pertenezcan a esa categoría.

Para ser más exactos, si vamos a ‘mimoduloajaxwebform/js/carne’ solo veremos los elementos que pertenecen a esa categoría. Además vemos que llama a una función ‘_mimoduloajaxwebform_get_allmenucomponents_select_options’ que le devuelve sólo los elementos deseados. Esta función es importante porque cumple dos objetivos que ahora veremos. El resultado lo mete en un código de opciones de select list que luego sustituiremos en el elemento correspondiente del formulario mediante AJAX, que siempre suele utilizar una codificación JSON para sus comunicaciones.

En cuanto a la función que he comentado:

 

En primer lugar, nos devuelve los elementos que pertenecen a una categoría cuando le pasamos esta por parámetro. En segundo lugar, si no le pasamos parámetro nos devolverá todas las opciones ordenadas alfabéticamente. Esto todavía no lo hemos utilizado, pero si seguís leyendo lo usaremos más adelante (no quiero explicarlo todavía para no liaros).

Ahora vamos a crear en nuestro módulo un archivo ‘script.js’ que realizará el cambio de las opciones dinámicamente. En primer lugar meteremos todo la lógica en Drupal behaviors, que es cómo funciona Drupal 6 en cuanto a javascript:

 

Lo siguiente es meter en la lógica un evento que se dispare cuando cambiemos el valor del tipo de menú (atención a cómo lo identifico: primero el ID del formulario y luego el ID del select list):

 

Dentro vamos a meter el código que nos va a permitir llamar a la URL que nos mandará las opciones que queremos según el menú escogido. En primer lugar recuperaremos el valor del menú, luego pondremos un mensaje en el selector de comida para informar al usuario de que se están cargando (para que no pierda la paciencia, ya que puede llevar unos segundos) y finalmente llamaremos a la URL para recuperar los valores, finalizando con la llamada a una función ‘mimoduloajaxwebform_actualizaComida‘ que se ejecutará tras recuperar los valores:

 

Ahora tenemos que crear la función, que irá al mismo nivel que la función que se dispara al cambiar el tipo de menú, es decir, dentro del Drupal behaviors. Esta función va a recoger lo que ha devuelto la URL y lo va a convertir de JSON a texto, para luego reemplazarlo dentro del contenido del desplegable de comidas, sustituyendo lo que hubiera previamente:

 

Perfecto. Ahora ya tenemos el archivo javascript creado con el comportamiento dinámico, pero no se está ejecutando en nuestro Drupal 6 porque no lo hemos incluido. Vamos a por ello. En primer lugar nos ayudaremos de hook_init para llamar a una función de theme que vamos a crear mediante hook_theme. En esta función que será llamada al inicio de la carga de Drupal 6, incluiremos nuestro archivo javascript para que podamos hacer uso de él:

 

Ahora ya debería funcionar, pero nos falta una cosa para rizar el rizo (opcional). Si en nuestro desplegable de comidas no metemos todas las comidas disponibles desde el comienzo, cuando rellene los datos un usuario y mande el formulario Drupal no sabrá a qué opción se refiere. Al principio Drupal no carga los valores de ese desplegable, así que no tiene conciencia de que haya valores allí. De ese modo cuando el usuario los carga dinámicamente al rellenar el tipo de menú, cuando le da a enviar Drupal piensa: “No sé a qué valor se refiere, así que voy a guardar la clave solamente”.

Entonces en el caso de que el usuario quisiera “Judía blanca” Drupal no guardará ese dato, si no su clave que en este caso es “legumbre–judia”, que es lo que se verá en los resultados de los envíos cuando los consultemos. Pensaréis: “Vaya chorrada, ¡me da igual!”. En ese caso sí da igual, porque la clave tiene un significado para nosotros, pero… ¿y si las claves que hubieramos elegido fueran numéricas? ¿Sabéis lo que es consultar un listado de envíos y ver en una columna todo números? Implica conocer qué significa cada código. Así que para evitar esto, lo que vamos a hacer es prepoblar los datos de ese select list inicialmente.

Para ello vamos a usar hook_webform_select_options_info, en el que vamos a definir un listado por defecto de opciones que serán rellenadas mediante una función. Esta función ya la hemos creado previamente para recuperar los platos por tipo de comida, pero en esta ocasión, al no pasarle parámetro, nos las devolverá todas ordenadas alfabéticamente:

 

Ahora tenemos que ir a nuestro componente select list del webform donde mostramos los platos y en el listado de listas precargadas veremos una nueva llamada ‘Todas las comidas’. Sencillamente entramos, la seleccionamos y le damos a guardar. Con esto y un borrado de cachés por si acaso, ya deberíamos tener nuestro formulario dinámico funcionando en Drupal 6.

Experto en la Gestión de Proyectos Web basados en Drupal con más de 8 años de experiencia. Desarrollo Full Stack (Front-end, Back-end, Site-Builder).

No Replies to "Ajax con Webform"