Drupal 8 - Formulaire - « ajouter un élément » et « supprimer un élément »

Posté le Lundi 9 octobre 2017 - 17:19
Dernière mise à jour le Vendredi 26 janvier 2018 - 18:35

Voici comment dans un formulaire custom D8, avoir un ensemble de champ "multiples" dans un formulaire de config par exemple.

drupal8-formulaire-add-more.jpg

Dans mon exemple je n'ai que le champ « titre », mais c'est simplement pour alléger le snippet.

Le code en question :

<?php

namespace Drupal\monmodule\Form;

use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\file\Entity\File;

/**
 * Class PopoteConfigForm.
 */
class HomepageConfigForm extends ConfigFormBase {

  protected function getEditableConfigNames() {
    return [
      'monmodule.homepage',
    ];
  }

  public function getFormId() {
    return 'homepage_config_form';
  }

  public function buildForm(array $form, FormStateInterface $form_state) {
    $config = $this->config('monmodule.homepage');
    $pomoted_items = $config->get('promoted', []);
    $number_promoted = $form_state->getValue('number_promoted', count($pomoted_items));
    $removed = $form_state->getValue('removed_promoted', []);

    $form['number_promoted'] = [
      '#type' => 'value',
      '#value' => $number_promoted,
    ];
    $form['removed'] = [
      '#type' => 'value',
      '#value' => $removed,
    ];
    $form['promoted'] = [
      '#type' => 'fieldset',
      '#title' => t("À la une"),
      '#tree' => TRUE,
    ];
    for ($i = 1; $i <= $number_promoted; $i++) {
      $item = array_shift($pomoted_items);
      if (in_array($i, $removed)) {continue;}
      $form['promoted'][$i] = [
        '#type' => 'fieldset',
        '#title' => t("À la une #@i", ['@i' => $i]),
      ];
      $form['promoted'][$i]['title'] = [
        '#type' => 'textfield',
        '#title' => $this->t("Titre"),
        '#default_value' => $item['title'],
      ];
      $form['promoted'][$i]['remove_' . $i] = [
        '#type' => 'submit',
        '#value' => t("Supprimer l'élément #@i", ['@i' => $i]),
        '#submit' => ['::removeItem'],
        '#attributes' => [
          'class' => ['button--danger'],
          'data-toRemove' => $i,
        ]
      ];
    }
    $form['promoted']['add_item'] = [
      '#type' => 'submit',
      '#value' => t('Ajouter un autre élément'),
      '#submit' => ['::addPromotedItem'],
    ];

    return parent::buildForm($form, $form_state);
  }

  public function addPromotedItem(array &$form, FormStateInterface $form_state) {
    $form_state->setValue('number_promoted', $form_state->getValue('number_promoted') + 1);
    $form_state->setRebuild();
  }

  public function removeItem(array &$form, FormStateInterface $form_state) {
    $removed = $form_state->getValue('removed_promoted', []);
    $removed[] = $form_state->getTriggeringElement()['#attributes']['data-toRemove'];
    $form_state->setValue('removed_promoted', $removed);
    $form_state->setRebuild();
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    parent::submitForm($form, $form_state);
    $parsed = [];
    $promoted = $form_state->getValue('promoted');
    foreach ($promoted as $promoted_item) {
      if(is_array($promoted_item)) {
        $parsed[] = $promoted_item;
      }
    }
    $this->config('monmodule.homepage')->set('promoted', $parsed)->save();
  }
}

 

 

Aller plus loin ?

 

Commentaires

Hello,

Petite problématique pour la suppression.
En effet, dans ton form, tu nommes ton champ `removed` alors qu'il devrait s'appeler `removed_promoted`.
La conséquence est qu'on ne peut pas supprimer plus d'un élément car, dans ta méthode `removeItem`, tu récupères la valeur de `removed_promoted` qui est vide car elle n'existe pas dans ton formulaire.

Retour d'expếrience : j'ai eu un gros souci de serialization lors de l'ajout d'un élément dans mon form (Serialization of ' Closure' is not allowed). Le problème vient de la mise en cache du formulaire et s'est résolu avec un $form_state->disableCache(); dans le buildForm().
Voilà, si ça peut servir...

Ajouter un commentaire

Ne sera pas publié

CAPTCHA Désolé, pour ça, mais c'est le seul moyen pour éviter le spam...