Il arrive que l’on souhaite afficher du contenu de façon asynchrone, que ce contenu soit déclenché par d’autres éléments de la page ou par une action de l’utilisateur.
Drupal propose une solution simple et performante pour répondre à ce besoin : les AJAX Callbacks, et plus précisément, les AJAX Commands.
Dans cet article, nous allons explorer ce concept, en mettant l'accent sur la création d’une logique d’AJAX Commands générique et réutilisable pour faciliter son intégration dans différents projets.
Fonctionnement des AJAX Callback Commands
En termes simples, les AJAX Callback Commands sont des objets qui implémentent l'interface CommandInterface, fournie par le core de Drupal. Ils incluent une méthode render()
, qui génère un render array envoyé au client. Ce dernier peut alors interpréter ce tableau et exécuter la commande correspondante.
Voici 2 liens pour aborder la notion des AJAX Callback Commands :
- Demystifying Ajax Callback Commands
- Core AJAX Callback Commands (liste des commandes inclues dans Drupal core)
- Le client (JavaScript) envoie une requête à un Controller.
- Le Controller crée une AjaxResponse et y attache une commande AJAX.
- Le Controller renvoie l’AjaxResponse au client.
- Le client (JavaScript) interprète la réponse AJAX et exécute la commande incluse.
Et voilà, tout simplement !
Implémentations
Comme mentionné précédemment, pour mettre en place cette logique, nous aurons besoin de plusieurs éléments :
- Un script JavaScript chargé de déclencher la requête, d’interpréter le résultat et d’exécuter la commande AJAX incluse.
- Un Controller qui construira la commande AJAX et la réponse AJAX, en attachant la commande à la réponse.
- Une commande AJAX (que l'on peut créer ou réutiliser une commande existante).
Une commande AJAX se compose de deux parties :- Une partie PHP qui est un objet implémentant l'interface
Drupal\Core\Ajax\CommandInterface
. - Une partie JavaScript attachée à
Drupal.AjaxCommands.prototype
, qui définit l'action concrète réalisée par la commande.
- Une partie PHP qui est un objet implémentant l'interface
Script JS
const settings = {
url: [mon_url],
};
Drupal.ajax(settings).execute();
Vous trouverez ci-dessous la liste des settings disponible :
/**
* Settings for an Ajax object.
*
* @typedef {object} Drupal.Ajax~elementSettings
*
* @prop {string} url
* Target of the Ajax request.
* @prop {?string} [event]
* Event bound to settings.element which will trigger the Ajax request.
* @prop {boolean} [keypress=true]
* Triggers a request on keypress events.
* @prop {?string} selector
* jQuery selector targeting the element to bind events to or used with
* {@link Drupal.AjaxCommands}.
* @prop {string} [effect='none']
* Name of the jQuery method to use for displaying new Ajax content.
* @prop {string|number} [speed='none']
* Speed with which to apply the effect.
* @prop {string} [method]
* Name of the jQuery method used to insert new content in the targeted
* element.
* @prop {object} [progress]
* Settings for the display of a user-friendly loader.
* @prop {string} [progress.type='throbber']
* Type of progress element, core provides `'bar'`, `'throbber'` and
* `'fullscreen'`.
* @prop {string} [progress.message=Drupal.t('Please wait...')]
* Custom message to be used with the bar indicator.
* @prop {object} [submit]
* Extra data to be sent with the Ajax request.
* @prop {boolean} [submit.js=true]
* Allows the PHP side to know this comes from an Ajax request.
* @prop {object} [dialog]
* Options for {@link Drupal.dialog}.
* @prop {string} [dialogType]
* One of `'modal'` or `'dialog'`.
* @prop {string} [prevent]
* List of events on which to stop default action and stop propagation.
*/
Controller
Pour le Controller, nous aurons besoin du Controller et du routing
<?php
namespace Drupal\custom_code\Controller;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;
/**
* Returns responses for custom-code routes.
*/
class CustomCodeController extends ControllerBase {
/**
* Builds the response.
*/
public function build(Request $request) {
$ajaxResponse = new AjaxResponse();
$ajaxCommand = new TheAjaxCommand();
$ajaxResponse->addCommand($ajaxCommand);
return $ajaxResponse;
}
}
Et le .routing.yml
custom_code.example:
path: '/custom-code/example'
defaults:
_title: 'Example'
_controller: '\Drupal\custom_code\Controller\CustomCodeController::build'
requirements:
_permission: 'access content'
Commande AJAX
Pour en savoir plus sur l’implémentation d’une commande AJAX, je vous recommande de regarder le document Demystifying Ajax Callback Commands.
Module contrib Drupal - Ajax Callback Field Formatter
Nous avons développé un module contrib qui permet d’appeler un contrôleur utilisant les "AJAX Commands" directement depuis le contenu.
Lien vers le module : https://www.drupal.org/project/ajax_callback_field_formatter
Présentation
Étant donné que cette logique peut être appliquée de façon générique, nous avons donc développé une solution appropriée.
Pour concevoir cette solution, nous avons établi les principes suivants :
- La logique doit être attachée à un champ spécifique d’une entité.
- Ce champ doit pouvoir être facilement identifié par le script JavaScript.
- Un callback doit être associé à ce champ pour déclencher l’action souhaitée.
- Il serait utile d’introduire une sorte de "bibliothèque" de callbacks.
- Il doit être possible de modifier le callback utilisé sans nécessiter de nouvelle mise en production.
Le champ de type "Lien" s’adapte bien à ce contexte, à condition d’ajuster son fonctionnement de la manière suivantes :
- Partie URL : Elle permet de spécifier l’URL du contrôleur, rendant ainsi possible la modification du callback sans mise en production.
- Partie texte : Elle sert de valeur par défaut, affichée durant l’exécution du callback ou en cas d’échec de celui-ci.
Contenu
Avec le contexte posé, nous avons pu élaborer la conception suivante :
- Nous avons choisi d’utiliser le type de champ "Lien", modifié par un Field Formatter, afin de :
- Ajouter les deux attributs suivants au champ :
data-execute-ajax-callback-field-formatter-url
: Contient l'URL du callback (contrôleur).- d
data-execute-ajax-callback-field-formatter-id
: L'identifiant permettant d’identifier l'élément.
- Remplacer l'affichage du lien par un affichage en texte (en remplaçant la balise
<a>
par<span>
). - Attacher un script JavaScript.
- Ajouter les deux attributs suivants au champ :
- Un script JavaScript qui récupère les attributs ajoutés par le Field Formatter et exécute la requête.
Et voilà, c’est tout.
Pour la partie Controller, étant donné qu’elle doit obligatoirement être spécifique, il incombe aux utilisateurs du module de l’implémenter dans un module personnalisé qui dépend du module Ajax Callback Field Formatter.
Fonctionnement
Le fonctionnement est simple :
- Les deux attributs ajoutés par le Field Formatter sont utilisés de la manière suivante :
data-execute-ajax-callback-field-formatter-url
: Contient l'URL récupérée à partir du champ "Lien".data-execute-ajax-callback-field-formatter-id
: L'identifiant construit selon le modèle suivant : "[entity_type]--[entity_id]-[field_name]--[delta]", où :- entity_type : Le type d'entité auquel le champ est rattaché.
- entity_id : L'identifiant de l'entité à laquelle le champ est lié.
- field_name : Le nom machine du champ.
- delta : Le delta de l'élément.
Notre élément passe donc de
<div class="field field--name-field-lien-ajax field--type-link field--label-above">
<div class="field__label">Lien Ajax</div>
<div class="field__item"><a href="/custom-code/example">This is a test</a></div>
</div>
à
<div class="field field--name-field-lien-ajax field--type-link field--label-above">
<div class="field__label">Lien Ajax</div>
<div class="field__item">
<span data-execute-ajax-callback-field-formatter-url="/custom-code/example" data-execute-ajax-callback-field-formatter-id="node--4--field-lien-ajax--0">This is a test</span>
</div>
</div>
- Le script JavaScript :
- Sélectionne tous les éléments à partir du sélecteur
[data-execute-ajax-callback-field-formatter-id]
. - Pour chaque élément :
- Il récupère l'URL du Controller
- Il y ajoute le sélecteur qui sera utilisé dans la AjaxResponse en tant que paramètre.
- Il effectue la requête AJAX.
- Sélectionne tous les éléments à partir du sélecteur
Ce n’est pas plus complexe que cela !
Il ne vous reste plus qu’à implémenter votre contrôleur.
Conclusion
Au final, l'utilisation des AJAX Callback Commands n’a rien de compliqué, et il est assez simple de mettre en place une logique générique pour les exploiter.
Nous espérons que ce billet de blog vous aura été utile et que le module contrib que nous vous proposons saura répondre à vos besoins !