Oct 072011
 

Post to Twitter Post to Facebook


Logo de AJAX

En posts anteriores vimos cómo crear formularios básicos y avanzados usando la propiedad #states.Es usual necesitar en un formulario que cierto campo varíe en función del valor de otro , casos típicos son cuando tenemos dos select uno de países y ciudades o universidades y estudios ofertados etc…En estos casos no podemos usar la propiedad #states ya que esta muestra u oculta partes del formulario pero no puede cargar por ejemplo una lista de opciones en un select dependiendo del valor de otro, en estos casos necesitamos usar Ajax.

En este post veremos cómo incorporar Ajax en nuestros formularios de una forma simple gracias a la API Form de Drupal.El proceso de aplicar Ajax en nuestros formularios es relativamente simple :

  1. Crea o modifica un elemento del formulario usa la propiedad #ajax, esto activará la llamada Ajax cuando el usuario modifique o pinche sobre el elemento.
  2. La propiedad #ajax['wappper'] indica el ID del elemento HTML que será modificado cuando se realice la llamada Ajax.
  3. Crear la función callback indicada en #ajax['callback'] que será la encargada de modificar el contenido de la etiqueta indicada en #ajax['wrapper'].

Práctica

Veamos un ejemplo práctico para entenderlo mejor.Para ello nos basaremos en el módulo que realizamos en el post anterior y añadiremos un nuevo enlace al menu navigation que nos lleve a nuestro nuevo formulario Ajax , para ellos modificamos nuestro hook_menu () :


/**
 * Implements hook_menu()
 */
function form_example_dynamic_menu(){
 $items['form_example_dynamic'] = array(
 'title' => 'Ejemplo formulario dinámico',
 'page callback' => 'drupal_get_form',
 'page arguments' => array('form_example_dynamic_form'),
 'access callback' => TRUE,
 'type' => MENU_NORMAL_ITEM
 );
 //Enlace al formulario AJAX
 $items['form_example_ajax'] = array(
 'title' => 'Ejemplo formulario con Ajax',
 'page callback' => 'drupal_get_form',
 'page arguments' => array('form_example_ajax_form'),
 'access callback' => TRUE,
 'type' => MENU_NORMAL_ITEM
 );
 return $items;
}

A continuación definimos nuestro formulario Ajax definiendo bien la propiedad #ajax :


/**
 * Define un formulario usando Ajax :
 * -Muestra dos listas select donde una es dependiente de la otra,
 *     modelos depende de marcas.
 * -Cuando cambie la marca del primer select se deben actualizar los
 *     modelos del segundo select con los modelos de la marca selectionada.
 */
function form_example_ajax_form($form, &$form_state){
 //Obtenemos la lista de marcas disponibles
 $options_marcas = _automobile_get_marcas_dropdown_options();
 //Verificamos si el select de marcas ya tiene una opcion seleccionada,
 //sino establecemos una por defecto.
 $selected = isset($form_state['values']['marcas_dropdown']) ? $form_state['values']['marcas_dropdown'] : key($options_marcas);
 //Definimos el primer select : Marcas
 $form['marcas_dropdown'] = array(
 '#type' => 'select',
 '#title' => 'Marcas',
 '#options' => $options_marcas,
 '#default_value' => $selected,
 //Incorporamos la propiedad de #ajax para qu ante un cambio de marca
 //se actualicen los modelos asociados a dicha marca.
 '#ajax' => array(
 'callback' => 'automobile_modelos_dependientes_callback',
 //Id del elemento que vamos a reemplazar.
 'wrapper' => 'dropdown_modelos_replace',
 ),
 );
 //Select de modelos
 $form['modelos_dropdown'] = array(
 '#type' => 'select',
 '#title' => 'Modelos',
 '#options' => _automobile_get_modelos_dropdown_options($selected),
 '#default_value' => isset($form_state['values']['modelos_dropdown']) ? $form_state['values']['modelos_dropdown'] : '' ,
 //Encapsulamos el select dentro del div 'dropdown_modelos_replace' para poder reemplazarlo
 //usando ajax.
 '#prefix' => '
<div id="dropdown_modelos_replace">',
 '#suffix' => '</div>
',
 );
 //Submit
 $form['submit'] = array(
 '#type' => 'submit',
 '#value' => t('Enviar'),
 );
 return $form;
}

Por último implementamos las funciones auxiliares necesarias para que nuestro ejemplo funcione correctamente :


/**
 * Función auxiliar que nos devuelve las marcas para el select de marcas.
 * Normalmente deberia obtener su información de la base de datos.
 *
 * @return array of options.
 */
function _automobile_get_marcas_dropdown_options(){

 return drupal_map_assoc(array(t('Honda'), t('Toyota'), t('Ford'), t('Volkswagen')));

}
/**
 * Solo seleciona el select de modelos para ser retornado para ser re-renderizado.
 * Será recargado con las nuevas opciones según la marca seleccionada.
 *
 * @return renderable array (select de modelos).
 */
function automobile_modelos_dependientes_callback($form, $form_sate){

 return $form['modelos_dropdown'];

}

/**
 * Función auxiliar que nos devuelve los modelos para el select de modelos.
 * Normalmente deberia obtener su información de la base de datos.
 *
 * @param key. Determina el conjunto de modelos a devolver.
 *
 * @return array of options.
 */
function _automobile_get_modelos_dropdown_options($key = ''){

 //Definimos un array anidados con las opciones posibles
 //En realidad lo deberiamos obtener de BD.
 $options = array(

 t('Honda') => drupal_map_assoc(array(t('Accord'), t('Civic'), t('CRX'), t('Pilot'))),
 t('Toyota') => drupal_map_assoc(array(t('Camry'), t('Yaris'), t('Tundra'), t('Tacoma'))),
 t('Ford') => drupal_map_assoc(array(t('F-150'), t('Explorer'), t('Escape'), t('Edge'))),
 t('Volkswagen') => drupal_map_assoc(array(t('GTI'), t('Passat'), t('Jeta'), t('Polo'))),

 );

 if(isset($options[$key])){
 return $options[$key];
 }else{
 return array();
 }

}

Y ya podemos probar nuestro formulario y comprobar que realmente el select de modelos se modifica dependiendo de la marca seleccionada.Note que no hemos tenido que usar nada de javascript para realizar la llamada Ajax , la API de Drupal lo hace por nosotros.

Proceso interno de la llamada Ajax
  1. Se muestra el formulario al usuario.
  2. En el formulario, un div con ID ‘dropdown_modelos_replace‘ envuelve a $form['modelos_dropdown'], para ello usamos las propiedades #prefix y #suffix.
  3. Cuando un usuario realiza un cambio en $form['marcas_dropdown'] internamente se realiza una llamada al servidor que causa la reconstrucción del formulario.
  4. El formulario es reconstruido y los valores para los modelos son restablecidos en base de $form['marcas_dropdown']
  5. La función automobile_modelos_dependientes_callback. Selecciona el trozo del formulario que será reemplazado en la página(normalmente la misma que la indicada en #ajax['wrapper']).
  6. La porción retornada es renderizada y devuelta a la página y el div con ID ‘dropdown_modelos_replace‘ es reemplazado.

  13 Responses to “Ajax en formularios con Drupal 7”

  1. Es posible llenar los elementos desde una taxonomia? De ser asi como lo haria?

    • Hola, que hay de la respuesta a esta pregunta, como hago para llenar el array no quemado, sino traer los datos de una taxonomia o tipo de contenido.

      Gracias

  2. Alguien por favor esponda a la pregunta anterior, ya sea desde una taxonomia o tipo de contenido,

    Tks.

  3. Tengo la misma inquietud, se podría llenar los elementos desde un tipo de contenido o de una taxonomía ????

    • Si se puede. Para ello, deberias hacer una consulta a la tabla {term_data} para listar los terminos que corresponden a la taxonomia. Llenas un array, y listo…

  4. Interesante la explicación, pero me gustaría saber como realizar esta acción si el formulario no se ha creado manualmente, o sea, si al tipo de contenido artículo que trae por defecto drupal 7 se adicionara dos fields, uno que dependiera del otro, como podría hacer para actualizar el dependiente, muchas gracias

    • con el módulo field-conditional-state para la versión 7 te resuelve lo de mostrar un elemento o no dependiendo del valor seleccionado en otro elemento del form. Ya si quieres que uno se actualice según el contenido de otro, este post está muy bueno, pro también puedes usar Hierarchical Select. lo que tendrías que tratar esos campos como taxonomías

  5. Buenas tardes, excelente tu web, ahora mismo tengo un problema, he usado tal cual tu codigo pero no me funciona en mi web, no realiza el evento ajax, no se que pueda ser.
    Lo estoy ejecutando dentro de un bloque que tambn cree por un modulo.
    Aquí lo puedes ver a la derecha de la página
    http://usadosgarantizados.com/test/
    Te agradeceria enormemente me digas que puedo estar haciendo mal.

  6. Mi codigo es

    /**
    * Implements hook_block_info().
    */
    function busquedavehiculo_block_info() {
    $blocks['busquedavehiculo'] = array(
    ‘info’ => t(‘Busqueda Vehiculo’), //The name that will appear in the block list.
    ‘cache’ => DRUPAL_CACHE_PER_ROLE, //Default
    );
    return $blocks;
    }

    function busquedavehiculo_block_view($delta = ”) {
    switch($delta){
    case ‘busquedavehiculo’:
    $block['subject'] = t(‘Current posts’);
    $block['content'] = busquedavehiculoajax_form();
    break;
    }

    return $block;
    }

    function busquedavehiculoajax_form($form, &$form_state){
    //Obtenemos la lista de marcas disponibles
    $options_marcas = _automobile_get_marcas_dropdown_options();
    //Verificamos si el select de marcas ya tiene una opcion seleccionada,
    //sino establecemos una por defecto.
    $selected = isset($form_state['values']['marcas_dropdown']) ? $form_state['values']['marcas_dropdown'] : key($options_marcas);
    //Definimos el primer select : Marcas
    $form['marcas_dropdown'] = array(
    ‘#type’ => ‘select’,
    ‘#title’ => ‘Marcas’,
    ‘#options’ => $options_marcas,
    ‘#default_value’ => $selected,
    //Incorporamos la propiedad de #ajax para qu ante un cambio de marca
    //se actualicen los modelos asociados a dicha marca.
    ‘#ajax’ => array(
    ‘callback’ => ‘automobile_modelos_dependientes_callback’,
    //Id del elemento que vamos a reemplazar.
    ‘wrapper’ => ‘dropdown_modelos_replace’,
    ),
    );
    //Select de modelos
    $form['modelos_dropdown'] = array(
    ‘#type’ => ‘select’,
    ‘#title’ => ‘Modelos’,
    ‘#options’ => _automobile_get_modelos_dropdown_options($selected),
    ‘#default_value’ => isset($form_state['values']['modelos_dropdown']) ? $form_state['values']['modelos_dropdown'] : ” ,
    //Encapsulamos el select dentro del div ‘dropdown_modelos_replace’ para poder reemplazarlo
    //usando ajax.
    ‘#prefix’ => ‘
    ‘,
    ‘#suffix’ => ‘
    ‘,
    );
    //Submit
    $form['submit'] = array(
    ‘#type’ => ‘submit’,
    ‘#value’ => t(‘Enviar’),
    );
    return $form;
    }
    /**
    * Función auxiliar que nos devuelve las marcas para el select de marcas.
    * Normalmente deberia obtener su información de la base de datos.
    *
    * @return array of options.
    */
    function _automobile_get_marcas_dropdown_options(){

    return drupal_map_assoc(array(t(‘bmw’), t(‘motorrad’), t(‘mini’)));

    }
    /**
    * Solo seleciona el select de modelos para ser retornado para ser re-renderizado.
    * Será recargado con las nuevas opciones según la marca seleccionada.
    *
    * @return renderable array (select de modelos).
    */
    function automobile_modelos_dependientes_callback($form, $form_sate){

    return $form['modelos_dropdown'];

    }

    /**
    * Función auxiliar que nos devuelve los modelos para el select de modelos.
    * Normalmente deberia obtener su información de la base de datos.
    *
    * @param key. Determina el conjunto de modelos a devolver.
    *
    * @return array of options.
    */
    function _automobile_get_modelos_dropdown_options($key = ”){

    //Definimos un array anidados con las opciones posibles
    //En realidad lo deberiamos obtener de BD.
    $options = array(

    t(‘bmw’) => drupal_map_assoc(array(t(‘bmw1′), t(‘bmw2′), t(‘bmw3′), t(‘bmw4′))),
    t(‘motorrad’) => drupal_map_assoc(array(t(‘motorrad1′), t(‘motorrad2′), t(‘motorrad3′), t(‘motorrad4′))),
    t(‘mini’) => drupal_map_assoc(array(t(‘mini1′), t(‘mini2′), t(‘mini3′), t(‘mini4′))),
    );

    if(isset($options[$key])){
    return $options[$key];
    }else{
    return array();
    }

    }

  7. Hola estoy usando tu programación para hacer un field compuesto pero no me esta saliendo si me pudieses ayudar o dar una guía te lo agradeceria.

    CODIGO

  8. Lo maximo tu Pagina aprendi mucho siguiendo tus guias me gustaria que lo hicieras con tipos de contenidos que se crean en drupal con las bases de datos que te muestre los usuarios del sistema en un dropbox.

    Gracias

  9. Hermano Muchas Felicidades. Espero en Dios puedas continuar como vas para ayudarnos a programar nuestros propios modulos.

    Saludos desde Republica Dominicana.

Leave a Reply to Jhonatan Fernandez Cancel reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>