Drupal 8: Mostrar/Ocultar partes de un formulario con ajax

Cabecera - Drupal 8: Mostrar/Ocultar partes de un formulario con ajax

Drupal 8: Mostrar/Ocultar partes de un formulario con ajax

  • Autor: fjavimartin

  • Fecha de Creación: 08/11/2017

  • Categorías:

    • Drupal,
    • Drupal 8,
    • Ajax,
    • Formularios

El objetivo de este artículo será crear un formulario en Drupal 8 con un fieldset que mostraremos/ocultaremos alternativamente mediante ajax.

Este pequeño formulario nos permitirá ver como podemos almacenar una variable en la variable de estado del formulario para determinar si el fieldset se está mostrando en un determinado momento y mostrarlo/ocultarlo según corresponda.

En las sucesivas llamadas de ajax utilizaremos la clase AjaxRespond() para modificar diferentes partes del formulario de una sola vez.

1. Formulario

Comenzaremos creando nuestro formulario:

<?php 

namespace Drupal\drupal_miseries\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

class HideShowAjaxButton extends FormBase {
  
  protected $values = [
    '0' => [
      'textfield_1' => 'Texto 1',
      'checkbox_1' => TRUE,
    ],
  ];
  
  public function getFormId() {
    return 'HideShowAjaxButtonForm';
  }
  
  public function buildForm(array $form, FormStateInterface $form_state) { 
    
     $form['fieldset_1'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Fieldset container 1'),
      '#attributes' => [
        'id' => 'fieldset-1',
      ],
    ];
    
    $form['fieldset_1']['textfield_1'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Textfield 1'),
      '#description' => $this->t('Example textfield 1'),
      '#default_value' => $this->values['0']['textfield_1'],
     ];
    
    $form['fieldset_1']['checkbox_1'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Checkbox 1'),
      '#default_value' => $this->values['0']['checkbox_1'],
    ];
    
    $form['actions'] = [
      '#type' => 'actions',
    ];
    
    $form['actions']['button_1'] = [
      '#type' => 'button',
      '#name' => 'ajaxbutton1',
      '#value' => $this->t('Show/Hide Fieldset'),
      '#ajax' => [
        'callback' => [$this, 'ajaxButton1'],
      ],
    ];
    
   $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
    ];
    
    $form['status'] = [
      '#markup' => '
        <div id="status"></div>',
    ];
    
    return $form;
  }
  
  public function submitForm(array &$form, FormStateInterface $form_state) { 
    drupal_set_message($form_state->getValue('textfield_1'));
    drupal_set_message($form_state->getValue('checkbox_1'));
  }
}

Como podéis observar, el código del formulario es muy sencillo. Simplemente tenemos un fieldset con un textfield y un checkbox. Sus valores por defecto se fijan mediante un pequeño array de valores fijado al inicio de la clase que hará las veces de una pequeña base de datos.

Será muy importante fijar un valor para la propiedad #name del botón encargado de mostrar/ocultar el fieldset, está propiedad la utilizaremos para determinar que botón se ha pulsado y actualizar el valor que determina si el fieldset se está mostrando o no.

Al final del formulario tendremos una sección que se actualizará mostrando los valores actuales que en cada momento tengan las variables textfield_1 y checkbox _1 cada vez que pulsemos en el botón que muestra/oculta nuestro fieldset.

2. Gestionar Ajax Submit

Ya tenemos nuestro formulario funcionando. Antes de añadir la propiedad #ajax al botón encargado de mostrar/ocultar nuestro formulario añadiremos al principio del método buildform() el código necesario para gestionar la pulsación del botón:

$element = $form_state->getTriggeringElement();
if (empty($element)) {
  $form_state->set('showfieldset1', TRUE);
} else if (!empty($element) && $element['#name'] == 'ajaxbutton1') {
  $showfieldset1 = $form_state->get('showfieldset1');
  $form_state->set('showfieldset1', !$showfieldset1);
}

La función $form_state→getTriggeringElement() nos devolverá el elemento que ha disparado la ejecución del código ajax. Este elemento estará vacío cuando cargamos el formulario momento que aprovecharemos para marcar que el formulario se está mostrando.

Dado que la variable utilizada se comportará como un booleano cuando pulsemos en el botón simplemente actualizaremos el valor de la misma al contrario de su valor actual.

Los métodos setValue y getValue no serán persistentes entre el formulario y los métodos de las llamadas ajax por lo que utilizaremos los métodos get() y set() de la variable de estado del formulario.

Nunca utilizaremos llamadas $this o que no estén accesibles desde fuera de nuestra clase porque el comportamiento del formulario no será el que esperamos.

3. Código Ajax

Ahora modificaremos nuestro botón para incluir la llamada ajax:

$form['actions']['button_1'] = [
      '#type' => 'button',
      '#name' => 'ajaxbutton1',
      '#value' => $this->t('Show/Hide Fieldset'),
      '#ajax' => [
        'callback' => [$this, 'ajaxButton1'],
      ],
    ];

En la propiedad #ajax no utilizaremos ‘wrapper’ para indicar que parte del formulario modificaremos puesto que nuestro método ajax nos permitirá actualizar en una sola llamada varias partes del formulario al mismo tiempo.

public function ajaxButton1 (array &$form, FormStateInterface $form_state) {
    $response = new AjaxResponse();
    
    if ($form_state->get('showfieldset1')) {
      $response->addCommand(new InvokeCommand('#fieldset-1', 'show'));
    } else {
      $response->addCommand(new InvokeCommand('#fieldset-1', 'hide'));
    }
    
    $response->addCommand(new AppendCommand('#status', '<p>Ajaxbutton1 showfieldset1: '. $form_state->get('showfieldset1') .'</p>'));
    $response->addCommand(new AppendCommand('#status', '<p>Textfield 1: '. $form_state->getValue('textfield_1') .'</p>'));
    
    return $response;
  }

Desde las llamadas ajax nunca incluiremos elementos nuevos en el formulario porque nunca se incluirán sus valores en $form_state y no podremos tratar sus valores al hacer submit del formulario o en sucesivas llamadas ajax. Todos los elementos que vayamos a necesitar en un formulario deberán estar siempre creados desde el principio, mostrarse u ocultarse según nuestro interés e ir incluidos en la variable $form que devolvemos en el método buildForm de la clase formulario.

En nuestro método ajax:

  • Mostraremos u ocultaremos el fieldset en función del valor acumulado en la variable ‘showfieldset1’.
  • InvokeCommand() nos permitirá ejecutar métodos jQuery recibiendo como parámetros el selector y el método a ejecutar. En concreto, show/hide incluirá el estilo ‘display: none;’ en el elemento seleccionado.
  • AppendCommand() nos permitirá añadir en la sección de estado un mensaje en el que podremos ver los sucesivos valores que van tomando el textfield y el checkbox.

Ahora solo os queda crear este formulario en un módulo de prueba y ver como funciona. Podréis encontrar el código fuente completo de este ejemplo en la sección de referencias.

4. Resumen

Este pequeño formulario nos servirá para adentrarnos en el mundo de ajax y será el punto de partida para que podamos implementar formularios mejores desde el punto de vista de la usabilidad.

Referencias

http://cgit.drupalcode.org/sandbox-javier.martin-2911003/tree/drupal_miseries/drupal_miseries/src/Form/HideShowAjaxButton.php?id=3292338b14cf0ea25cdc4e6d0c08bfd89429e6e5

https://drupal.stackexchange.com/questions/188730/how-can-i-implement-ajax-form-submission/188752#188752

https://api.drupal.org/api/drupal/core!core.api.php/group/ajax/8.2.x

https://api.drupal.org/api/drupal/developer%21topics%21forms_api_reference.html

https://api.drupal.org/api/drupal/8.2.x

Todos los Derechos Reservados © 2016

Funciona con Drupal