Bloc post : afficher un résumé d'article ou d'un autre post type - Formation Gutenberg - Capitaine WP

Formation « Créer des blocs avec Gutenberg »

Bloc post : afficher un résumé d’article ou d’un autre post type

Lecture :7 minutes •0

On va analyser plus en détails le bloc Article, afin de voir comment il a été conçu et comment est utilisée l’API Rest WordPress avec Gutenberg, pour récupérer les informations dynamiquement.

Ce bloc article permet d’afficher le résumé d’un article, ou de tout autre type de publication de votre site avec l’image à la Une, le titre, la description, la catégorie et l’auteur.

le bloc article AGB
Le bloc article et ses options

Pour cela j’ai mis en place dans l’inspecteur une recherche et un champ Select permettant de choisir le type de publication souhaité .

Analyse du code

Voyons maintenant le code. index.js est très simple : si il n’y a pas encore d’article défini dans les attributs (via son identifiant), j’affiche un simple message indiquant d’aller en choisir un depuis l’inspecteur, sinon j’affiche le rendu que j’ai mis dans un sous composant <Preview />.

JSX
src/blocks/post/index.js

Du côté de l’inspecteur, dans inspect.js, le code est ici aussi très simple : je vais chercher un composant appelé <SearchPost /> de ma création, c’est là dedans que je vais y mettre la majeure partie de mon code.

Pour alléger un peu l’interface je n’affiche les options « montrer l’image, la catégorie, l’auteur » que lorsqu’un article a été choisi.

Le composant SearchPost

La suite se déroule dans src/components/searchpost/index.js. Comme je le disais dans le précédent cours, j’ai décidé de sortir ce composant du bloc, afin de pouvoir aisément le réutiliser ultérieurement.

Ici j’ai un champ standard, <TextControl />. Lorsque l’utilisateur entrera du texte, une recherche se lancera via l’API Rest.

Throttle et Debounce

On va faire face à un petit souci : en l’état la recherche Ajax va se lancer à chaque nouveau caractère entré dans le champ. Pas top niveau performances.

Pour éviter cela on va utiliser un package npm qui s’appelle throttle/debounce et qui s’installe en ligne de commande via npm install throttle-debounce --save.

La fonction Throttle s’exécute avec un intervalle régulier, afin par exemple de limiter les lancements d’événements lors d’un scroll.

Mais c’est la fonction Debounce qui va nous servir ici. Elle permet de retarder le lancement d’une fonction. Lors de la saisie, cela permet d’attendre un peu, pour voir si l’utilisateur a encore quelque chose à taper, avant de lancer la recherche.

J’ai fixé ce temps à 300ms.

A l’utilisation c’est simple, il suffit d’appeler Debounce en amont de votre fonction :

JSX
src/components/searchpost/index.js

Conseil

N’hésitez pas à utiliser cette librairie pour vos projets, elle est très pratique et énormément utilisée par les développeurs JS.

Sélectionner un type de publication

Ce composant va nous permettre de choisir le type de publication (parmi page, article, media et tout éventuel Custom Post Type). Il est situé dans mon composant <SearchPost/>, après le champ de recherche.

Le souci c’est qu’l n’y a pas de route API qui me permet de lister tous les types de publication du site, j’ai donc du récupérer cette information en PHP et l’envoyer à Javascript via wp_localize_script()  (juste avant d’appeler notre JS Gutenberg) :

PHP
classes/Blocks/Post.php

Notez que j’ai retiré le CPT produits, car j’ai un bloc produit spécifique pour WooCommerce.

En JS, j’aurais donc accès à une variable globale nommée advancedGutenbergBlocksPost qui me fournira la liste des types de publication, je le passe via l’attribut options du <SelectControl />.

Notez également qu’il n’y a pas besoin de générer à la main les balises <option> comme on le faisait classiquement, le composant le fait pour nous, on a juste à transmettre un objet contenant la liste des éléments voulus.

JSX
src/components/searchpost/index.js

Le post type choisi est stocké dans la variable currentType stockée dans le state du composant. Il va nous servir après pour envoyer une requête API vers la bonne collection de données.

Pour rappel le state permet de stocker des données internes au composant. Lorsqu’une donnée du state change, le composant est rendu à nouveau.

Requête Ajax vers l’API

Dans la fonction onSearch j’utilise la promesse fetch().then() afin de faire une requête API Rest.

La fonction fetch() est le remplaçant de la vieille fonction XMLHttpRequest mais à l’époque vous utilisiez surement $.Ajax de jQuery, comme moi !

On va revenir sur l’appel de l’API. Précédemment j’ai stocké le type de publication choisi dans currentType. J’utilise cette notation un peu particulière pour appliquer un nom d’objet dynamiquement :

JS
src/components/searchpost/index.js

J’utilise ensuite le state du composant pour stocker les résultats.

Quand les résultats sont mis à jour, React génèrera à nouveau le rendu HTML de mon composant avec les nouvelles données.

Le résultat contient toutes les informations de l’article (titre, description, date de publication, identifiant…) mais on ne va stocker seulement l’ID dans les attributs.

Transmettre la donnée au parent

J’affiche ensuite ces résultats dans le HTML via un .map() pour itérer dans les résultats. Chaque article trouvé se voit doté d’un onClick qui va transmettre au composant parent l’ID de l’article en question.

D’ailleurs ici j’aurais pu appliquer directement un setAttributes, mais ce n’est pas une bonne idée : ça implique que mon sous composant sensé être indépendant sait quel est l’attribut qu’il doit modifier.

La bonne pratique est la suivante : le composant doit juste pouvoir retourner la valeur au parent, et c’est ce parent qui assigne l’attribut.

Si je reviens dans src/blocks/post/inspect.js :

JSX
src/blocks/post/inspect.js

J’ai défini un attribut onChange (par convention). On voit la même chose dans tous les composants proposés nativement par Gutenberg, par exemple <TextControl />.

Du coup dans le sous-composant <SearchPost />, il suffit de renvoyer la valeur à this.props.onChange, défini juste avant.

JSX
src/components/serachpost/index.js

Au début c’est toujours un peu la galère de savoir qui fait quoi : est-ce que l’on stocke l’attribut depuis où on est, doit-on renvoyer la donnée au parent ? Ne vous inquiétez pas, avec de la pratique, ça va très vite rentrer !

En résumé :

  • Le composant renvoie la valeur finale (ici l’identifiant de l’article) sur la props onChange
  • Dans le parent, lorsque l’on appelle le composant, on définit une fonction sur le onChange pour permettre de capter la valeur et la stocker, dans notre cas en attribut via setAttributes.

Ca aurait également très bien fonctionné de mettre le setAttributes directement dans le sous-composant, mais c’est mieux de fonctionner ainsi.

L’aperçu

De retour dans notre bloc, on a maintenant un ID enregistré dans les attributs, et un ID seulement : je n’ai pas besoin de stocker toutes les informations, car au chargement de l’éditeur dans l’admin, il me suffit de cet ID pour retrouver l’article et afficher ses informations.

Si j’avais stocké ces valeurs, j’aurais certes évité une requête API, mais si l’article a changé entre temps, je n’aurais plus eu les informations à jour.

Il vaut donc mieux conserver cet aspect dynamique.

J’utilise donc là aussi l’API Rest pour récupérer l’article à partir de son identifiant. Mais l’API ne nous renvoie pas tout. Il faudra alors faire plusieurs sous-requêtes pour aller chercher l’auteur, la catégorie et l’image à la Une.

Cependant c’est plutôt rapide, et tant pis s’il faut 4 requêtes au lieu d’une.

JSX
src/blocks/posts/preview.js

J’utilise 2 fonctions React : componentWillMount(), qui se lance lors de la toute première initialisation, et componentDidUpdate(), qui se lance qu’il y a eu une modification d’affichage.

Cette dernière me renvoie les précédentes props et state afin que je puisse les comparer avec les nouvelles : s’il y a eu changement d’ID et seulement dans ce cas, je renvoie une requête API.

Cela permet de rester optimal et de ne pas lancer des requêtes API à tout va au moindre changement.

Le rendu en PHP

Comme c’est un bloc dynamique, je ne créé par son rendu en JS, mais via PHP. Le code se trouve dans classes/Blocks/Post.php pour le code et public/templates/posts.phppour le HTML.

A partir de l’identifiant je vais simplement aller faire une WP_Query et une Loop WordPress afin de récupérer et afficher les informations de l’article en question. Rien de bien fou ici, car on reste complètement dans les codes classiques du CMS !

PHP
classes/Blocks/Post.php

Et voilà le travail !

N’hésitez pas à copier mon bloc et à le tester de votre côté. Insérez des console.log()un peu partout pour voir quand sont lancées les fonctions, quel est l’état des données à ce moment.

Sur le même principe : les blocs produit et le bouton ajouter au panier

Profitez-en pour aller voir le code des deux blocs : Produit et Ajouter au panier qui sont construits sur le même modèle.

L’un permet d’afficher un produit WooCommerce (la différence avec l’article c’est que j’affiche le prix, l’éventuelle promotion et un bouton ajouter au panier) et l’autre permet d’afficher un bouton « ajouter au panier » personnalisable :

Les blocs produit et ajouter au panier
Les blocs produit et ajouter au panier

Vous verrez que les 2 blocs utilisent le même composant <SearchProduct />. Le bouton d’ajout au panier possède une option qui permet de choisir parmi plusieurs Dashicons.

Prenez le temps de voir un peu comment sont faits ces 2 blocs, et les nombreuses similitudes avec le bloc Article. N’hésitez pas à décortiquer et tester le code !


Voilà un cours bien conséquent ! Quand on commence à créer des blocs dynamiques, toujours plus complexes, le code en subit les conséquences.

Mais en restant organisé, par la création de sous-composants, et en dispatchant bien le code, cela simplifie grandement le travail.

On va revoir certains de ces principes dans les prochains cours. N’hésitez pas à me poser des questions si vous bloquez sur un concept.

Dans le cours suivant on va voir le bloc Plugin qui va piocher cette fois sur une API Rest distante. On verra qu’en général le principe reste le même.

0

Questions, réponses et commentaires

Laisser un commentaire