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

Toulouse Du 22 au 24 mai 2024

Voir la formation

Actualités en lien

Image
Migration Drupal 7 à Drupal 10
04/04/2024

Migration d'un site Drupal 7 en Drupal 10

Trucs, astuces et "bouts" de code pour migrer votre site web de Drupal 7 à Drupal 10. Compte-rendu d'une conférence donnée au Drupalcamp Rennes 2024.

Voir l'article
Image
Formation Migration Drupal 10
03/04/2024

Du nouveau dans notre gamme de forma­tions Drupal

Maîtri­sez le CMS Drupal de bout en bout avec notre panel complet de forma­tions couvrant la migra­tion (notre petite dernière), l’ad­mi­nis­tra­tion, le déve­lop­pe­ment et l’in­té­gra­tion Drupal. Pour deve­nir expert, plon­gez dans l’uni­vers Drupal !

Voir l'article
Image
Encart article DrupalCamp 2024
06/03/2024

Makina Corpus, parte­naire du Drupal­Camp 2024

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

Voir l'article

Inscription à la newsletter

Nous vous avons convaincus