Accueil / Blog / Métier / 2019 / Drupal 8 : Dynamiser vos contenus à l'aide des formulaires AJAX

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

Par Simon Mellerin — publié 18/12/2018, édité le 23/01/2019
Utiliser les AjaxCommands de l'API de Drupal 8 pour agir sur le Markup
Drupal 8 : Dynamiser vos contenus à l'aide des formulaires AJAX

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 8 développeur !

ABONNEZ-VOUS À LA NEWSLETTER !
Voir aussi
Mon Top 30 des modules Drupal 8 Mon Top 30 des modules Drupal 8 16/02/2019

Transcription d'une conférence donnée au Drupalcamp Paris 2019

Makina Corpus lance une nouvelle offre pour sécuriser les projets Drupal des ESN Makina Corpus lance une nouvelle offre pour sécuriser les projets Drupal des ESN 18/02/2019

Formations et accompagnement pour sécuriser les projets Drupal

Makina Corpus au Drupalcamp du 15 au 17 février Makina Corpus au Drupalcamp du 15 au 17 février 13/02/2019

Conférences et atelier, rejoignez-nous !

Python : Bien configurer son environnement de développement Python : Bien configurer son environnement de développement 07/12/2015

Comment utiliser les bonnes pratiques de développement Python.

Une usine à sites en Drupal Une usine à sites en Drupal 18/12/2018

Qu'est ce que c'est, et en avez-vous besoin ?