Makina Blog

Le blog Makina-corpus

Drupal 8 : Dynamiser vos contenus à l'aide des formulaires AJAX


Utiliser les AjaxCommands de l'API de Drupal 8 pour agir sur le Markup

On voit souvent des articles et tutoriels sur la Form API et l'AJAX API de Drupal 8 pour agir sur le formulaire en lui-même (validation à la volé [en], ajout conditionnel de champs [en]…). Il est plus rare de trouver des ressources sur les AjaxCommands de l'API Drupal pour modifier le Markup d'un élément lambda. C'est l'objet de cet article.

L'exemple que nous allons prendre n'a pas vraiment d'intérêt dans la vraie vie, mais a l'avantage de bien illustrer les possibilités de la commande `ReplaceCommand`.

Nous allons développer un module définissant une page contenant un formulaire. Ce formulaire permettra d'aller chercher le contenu d'un nœud à partir de son ID et de l'afficher.

Développement du formulaire

Dans un module Custom, on crée un nouveau formulaire Drupal en instanciant une classe étendant la classe FormBase dans le fichier src/Form/MonFormulaire.php

On pourra s'aider dans la console Drupal 8 pour générer le module et le formulaire :

drupal generate:module
drupal generate:form

Arborescence du module

A la différence d'un formulaire classique, dans la fonction buildForm, on remplace l'élément de type submit par un élément de type button. Cela a pour effet d'éviter de passer par la fonction submitForm au moment où l'on cliquera sur le bouton, tout en conservant le passage par la fonction validateForm.

Sur cet élément de type button, on rajoute un attribut '#ajax' contenant le nom du callback à appeler (ici '::loadNode').

On déclare ensuite notre fonction de callback loadNode.

<?php

namespace Drupal\monmodule\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Ajax\ChangedCommand;
use Drupal\node\Entity\Node;

/**
 * Class MonFormulaire.
 */
class MonFormulaire extends FormBase {


  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'mon_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['node_id'] = [
      '#type' => 'number',
      '#title' => $this->t('Node Id'),
    ];
    $form['submit'] = [
      '#type' => 'button',
      '#value' => $this->t('Submit'),
      '#ajax' => [
        'callback' => '::loadNode',
      ],
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    parent::validateForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    // On a rien de plus à faire ici.
  }
  
  /**
   * Notre callback.
   */
  public function loadNode(array &$form, FormStateInterface $form_state) {

    // On récupère le RenderArray du noeud demandé par l'utilisateur.
    $node_id = $form_state->getValue('node_id');
    $node = Node::load($node_id);

    $view_builder = \Drupal::entityTypeManager()->getViewBuilder('node');
    $render_array = $view_builder->view($node, 'full');

    // On rajoute un élément div autour du RenderArray de manière à pouvoir le recibler
    // si la callback est appelée à nouveau.
    $render_array['#prefix'] = '<div id="div-cible">';
    $render_array['#suffix'] = '</div>';

    // On crée notre AjaxResponse.
    $response = new AjaxResponse();
    
    // Puis on ajoute une ReplaceCommand à notre AjaxResponse.
    // Le premier argument du constructeur d'une ReplaceCommand est le sélecteur CSS de
    // l'élément à remplacer, le second est le RenderArray que l'on souhaite retourner.
    $response->addCommand(new ReplaceCommand('#div-cible', $render_array));

    // Pour l'accessibilité de notre site, on ajoute une ChangedCommand pour notifier
    // les lecteurs d'écran d'un changement sur la page.
    response->addCommand(new ChangedCommand('#div-cible'));

    return $response;
  }

}


Définition de notre page personnalisée

On crée maintenant une page personnalisée dans laquelle on affichera notre formulaire.

On commence par définir sa route dans monmodule.routing.yml

monmodule.ma_page:
  path: 'ma-page'
  defaults:
    _controller:  '\Drupal\monmodule\Controller\MaPageController::maPage'
    _title: 'Ma Page'
  requirements:
    _permission: 'access content'

Le Controller associé dans src/Controller/MaPageController.php :

<?php

namespace Drupal\monmodule\Controller;

use Drupal\Core\Controller\ControllerBase;

class MaPageController extends ControllerBase {

  /**
   * Display MaPage.
   *
   * @return array
   */
  public function maPage() {
    // Building type project selection form.
    $monFormulaire =  \Drupal::formBuilder()->getForm('Drupal\monmodule\Form\MonFormulaire');

    return [
      '#title' => \Drupal::config('system.site')->get('name'),
      '#theme' => 'ma_page',
      '#mon_formumlaire' => $monFormulaire,
    ];
  }

}

On déclare le thème dans un hook_theme dans monmodule.module :

<?php

/**
 * Implements hook_theme().
 */
function monmodule_theme() {
  return [
    'ma_page' => [
      'variables' => [
        'mon_formumlaire' => NULL,
      ],
    ],
  ];
}

Et le template dans templates/ma-page.html.twig :

<h1>Ma Page</h1>

{{ mon_formulaire }}

<div id="div-cible">
  {# C'est ce div qui sera remplacé par notre AjaxCommand #}
</div>

Et voilà !

Maintenant, n'oubliez pas d'installer votre module, de vider le cache de Drupal et allez visiter notre nouvelle page.

Rentrez l'ID d'un nœud que vous avez déjà contribué et validez le formulaire, vous devriez voir apparaître votre contenu sous le formulaire.

Aperçu de notre nouvelle page

Note: Pour simplifier je n'ai pas mentionné l'étape de validation du formulaire. Dans notre exemple, il faudrait bien sûr vérifier qu'il existe bien un nœud correspondant à l'ID rentré par l'utilisateur dans la fonction validateForm.

Pour aller plus loin

Ici, nous nous sommes contentés de remplacer un élément div. Mais il est bien sûr possible d'ajouter autant de commandes que l'on souhaite à l'objet AjaxResponse.

On peut alors imaginer remplacer d'autre éléments du DOM en ajoutant d'autres ReplaceCommand. On peut également effectuer d'autres actions en utilisant d'autres types d'AjaxCommand comme :

  • SettingsCommand pour envoyer des DrupalSettings au Javascript
  • AddCssCommand pour ajouter du style
  • Et bien d'autres à découvrir dans la documentation de l'API AJAX de Drupal 8

Maintenant à vous de jouer !

Vous rencontrez des problèmes ? Vous souhaitez approfondir le sujet ?

Demandez-nous des informations sur notre formation Drupal 9 développeur !

Formations associées

Formations Drupal

Formation Drupal Développeur

À distance (FOAD) Du 2 au 4 avril 2024

Voir la formation

Formations Drupal

Formation Drupal Administrateur

Paris Du 29 au 31 janvier 2024

Voir la formation

Actualités en lien

Image
Encart article DrupalCamp 2024
06/03/2024

Makina Corpus, parte­naire de la Drupal­Camp 2024

Nous sommes fiers d’an­non­cer que Makina Corpus est le spon­sor de la Drupal­Camp à Rennes. Notre expert vous y propose une confé­rence « migrer de Drupal 7 à Drupal 10 ».

Voir l'article
Image
DrupalCon Lille Sponsor
20/10/2023

DrupalCon Lille 2023 : quand Drupal prend (enfin ?) le virage de l'UX

Présentation rapide de sujets ergonomiques discutés lors de la première journée de la DrupalCon Lille 2023

Voir l'article
Image
DrupalCon 23 Encart
14/09/2023

Makina Corpus sponsorise la DrupalCon 2023 à Lille

La DrupalCon 2023 revient en France du 17 au 20 octobre et notre équipe y participe ! Pour cette nouvelle édition de la DruplaCon Fr, Makina Corpus s'engage auprès de Drupal et sponsorise l'événement 2023 à Lille.

Voir l'article

Inscription à la newsletter

Nous vous avons convaincus