Hero

Como crear botón "Agregar otro campo" con FAPI en Drupal 7

Abril 24, 2013

enzo
Drupal

Cuando se trabaja con un sistema de administración de contenido es muy normal tener que almacenar varios datos de una mis propiedad. Imaginemos que tenemos un tipo de contenido llamado Envase el cual se puede fabricar de varios materiales Plástico, Vidrio, Metal, etc.

Esta labor puede ser muy fácil utilizando el Field API indicando que el campo aceptara valores ilimitados con lo cual el sistema generara el Javascript necesario para permitir la acción de agregar campos dinámicamente.

Sin embargo imaginemos que por alguna razón en este escenario no es posible utilizar el Field API y se requiere hacer por medio de instrucción FAPI, con esta restricción esta simple tarea se puede complicar mas de la cuenta, pero en este y otros escenarios es un necesidad real.

A continuación les presentare una solución a este problema.

  1. Modificar el formulario de edición.

Lo primero que haremos sera modificar el formulario de edición de todos los tipos de contenido, esto lo lograremos haciendo uso del hook hook_form_alter como se muestra a continuación.

function MIMODULO_form_alter(&$form, &$form_state, $form_id) {


 if ($form['type']['#value'] . '_node_form' == $form_id) {

Se podría ser mas especifico y utilizar el valor almacenado en $form[‘type’][‘#value’] para que solo se aplique a un algún tipo de contenido en especial.

  1. Especificar contenedor de los campos dinámicos.

Para que Drupal genere las acciones de Ajax que generan los campos es necesario un contenedor de HTML donde se actualizaran los campos de forma dinámica, como se muestra en el siguiente listado de código.

$form['material_wrapper'] = array(
 '#type' => 'fieldset',
 '#title' => t('Materiales'),
 '#weight' => 5,
 '#collapsible' => TRUE,
 '#collapsed' => FALSE,
 );

 $form['material_wrapper']['materiales'] = array(
 '#type' => 'container',
 '#tree' => TRUE,
 '#prefix' => '<div id="materiales">',
 '#suffix' => '</div>',
 );

El primer elemento es solo un fieldset de carácter decorativo para guiar al usuario, el segundo elemento utiliza la directivas #suffix y #preffix para generar el contenedor de los elementos.

  1. Variable para almacenar materiales.

Antes de comenzar a generar los campos para recibir la información es necesario definir donde almacenar cuantos materiales tenemos en el formulario en medio de cada llamada al Ajax, como se presenta en el siguiente listado.

$form_state['storage']['materials'] = isset($form_state['storage']['materials']) ? $form_state['storage']['materials'] : 0;

Como se puede observar hacemos uso del $form_state el cual conservara los valores almacenados entre cada llamado Ajax y al inicio se colocara a cero.

  1. Generación de campos dinamicos.

Luego de obtener la cantidad de campos para materiales que el usuario ha solicitado crear mediante Ajax se debe generar como se puede apreciar a continuación.

 if ($form_state['storage']['materials']) {
   for ($i = 1; $i <= $form_state['storage']['materials']; $i++) {
     $form['material_wrapper']['materiales'][$i] = array(
       '#title' => t('Material #') . " " . $i,
       '#type' => 'textfield',
       '#attributes' => array('style' => array('display:block; margin-bottom:5px;')),
     );
   }
 }

Con el código anterior se generaran tantos campos de texto como el usuario haya solicitado. El proceso para leer los datos almacenados y colocarlos como valores por defecto se deja de tarea al lector.

  1. Generar llamados Ajax.

En esta sección es donde ocurre toda la magia que integra el código implementado anteriormente.

Para generar los llamado en Ajax debemos implementar un botón submit como el que se muestra a continuación.

$form['material_wrapper']['add_material'] = array(
 '#type' => 'button',
 '#value' => t('Add a Material'),
 '#href' => '',
 '#ajax' => array(
   'callback' => 'MIMODULO_ajax_add_materials',
   'wrapper' => 'materiales',
   ),
 );

 $form_state['storage']['materials']++;

A simple vista es un simple botón de un formulario cualquiera, pero la propiedad callback indica a Drupal que se debe hacer un llamado vía Ajax a la función MIMODULO_ajax_add_meterials esta función determina que elementos en el formulario serán actualizados y el lugar donde se desplegaran estos cambios serán determinados por el wrapper materials, el cual es el div generador con las etiquetas $suffix y #preffix en el punto 2 de este tutorial. La clave esta en que la función Ajax solo modifique elementos del formulario que ya estén previamente contenidos dentro del wrapper que en nuestro caso es el div con clase de CSS materials.

Cada vez que se presiona el botón Add a Material se ejecuta un llamado a las funciones que generan el formulario, pero con los valores almacenados en el $form_state, entonces es correcto decir que el formulario se genera en cada llamado a Ajax, pero aunque es ejecutado no se remplaza en su totalidad, eso es función del callback como se muestra a continuación.

  1. Definir que elementos del $FORM se actualizaran vía Ajax.

Si el punto anterior les parecio magico este mucho mas, veamos la definción de la funcion de callback.

function MIMODULO_ajax_add_materials($form, $form_state) {
 return $form['material_wrapper']['materiales'];
}

Como se menciono anteriormente esta función determina que elementos del formulario re ejecutado se van a mostrar, en el ejemplo anterior se decidió que la parte del formulario que se va a actualizar seria $form[‘assets_wrapper’][‘assets’] la cual contendrá el campo extra que se genero luego de re ejecutar el formulario donde la variable $form_state[‘storage’][‘materials’] estaría incrementada en 1.

Luego de implementado este código tendríamos un formulario parecido al siguiente.

El proceso de almacenar en el nodo los valores introducidos por el usuario se dejas como reto para el lector.

drupal fapi ajax

Espero que haya sido de su agrado.

Recibe consejos y oportunidades de trabajo 100% remotas y en dólares de weKnow Inc.