Formation « Créer des blocs avec Gutenberg »

Champs administrables et attributs

Lecture : 6 minutes • 0

On va maintenant voir comment offrir un peu d'interactivité à nos blocs en ajoutant notamment une zone de texte.

Jusque là nous avons des blocs statiques qui ne se révèleront pas super utiles. On va donc maintenant commencer à ajouter des zones de textes éditables pour rendre le tout un peu plus vivant.

Pour les bénéficiaires du cours premium, on va prendre l’exemple src/3-edit/index.js.

Dans ce cours l’objectif est d’ajouter un champ de texte simple qui va nous permettre de modifier le contenu de notre bloc. On va l’appeler content.

Voici le rendu attendu lorsque j’édite mon champ :

Et lorsqu’on le laisse tranquille :

Voici maintenant le code JS de ce bloc :

JS
src/3-edit/index.js
import './style.scss'
import './editor.scss'

const { __ } = wp.i18n
const { registerBlockType } = wp.blocks

registerBlockType(
	'capitainewp/edit',
	{
		title: __( "Bloc éditable"),
      	description: __("Un bloc avec un champ d'édition"),
		icon: 'edit',
		category: 'common',
		keywords: [
			__( 'texte administrable' ),
		],

		attributes: { // C'est ici que l'on défini les champs dynamiques
			content: {
				type: 'string',
				source: 'text',
				selector: '.content'
			},
    },

		edit: props => {

			// La fonction qui met à jour la valeur
			const onChangeContent = event => {
				props.setAttributes( { content: event.target.value } )
			}

			return (
				<div className={ props.className }>
					{ props.isSelected ? ( // N'afficher le champ seulement si le bloc est actif
						<input
							type='text'
							value={ props.attributes.content }
							onChange={ onChangeContent }
							placeholder={ __('Ecrivez un texte !') }
						/>
					) : ( // Sinon
						<p className="content">{ props.attributes.content }</p>
					) }
				</div>
			)
		},

		save: props => {
			return (
				<div>
					<p className="content">{ props.attributes.content }</p>
				</div>
			)
		},
	}
)

On va regarder ce code un peu plus dans les détails juste après mais on peut déjà remarquer qu’il y a eu pas mal de changement dans edit, et un nouveau paramètre a fait son apparition : attributes.

Rappel : ce qu'enregistre Gutenberg en base

Avant tout, il faut rappeler que Gutenberg enregistre en base le HTML du bloc (avec tous les autres blocs), c’est donc tout ce qui est dans save qui sera conservé à l’enregistrement de la page.  On peut d’ailleurs voir ce HTML dans le menu supérieur droit, puis Éditeur de code.

Le code source HTML généré par vos blocs
Le code source HTML généré par vos blocs

Je vous invite à revoir le cours dédié au cycle de vie de Gutenberg si jamais vous avez besoin de vous rafraichir la mémoire.

On a donc ici un problème : dans la base, la seule chose qu’il nous reste c’est un HTML statique. Il va donc nous falloir un moyen de dire à Gutenberg comment il doit récupérer les données modifiables, comme le contenu de notre champ content.

C’est ici que l’on fait appel aux attributs !

Les attributs

Toute donnée modifiable par l’utilisateur doit être déclarée dans les attributs. C’est comme ça que Gutenberg sait quelle donnée du HTML est administrable.

Pour l’instant on n’a qu’une seule donnée à enregistrer : content. C’est un texte que l’on retrouve dans le HTML de save, dans la balise qui a la classe .content.

JS
attributes: {
  content: {
    type: 'string',
    source: 'text',
    selector: '.content'
  },
},

On a donc 3 informations à indiquer :

  1. le type : une chaine de charactères « string » (mais ça pourrait être un entier)
  2. la source : dans quel type d’élément je vais trouver ce contenu (ici dans la balise, mais ça peut être dans un attribut HTML par exemple)
  3. le sélecteur css qui va permettre de cibler l’élément à trouver

En résumé, Gutenberg va chercher une chaine de caractères (1) dans le contenu d’une balise (2) dont la classe est nommée .content (3).

J’ai bien sûr adapté le HTML dans la partie save pour refléter l’apparition de cette classe :

JS
save: props => {
	return (
		<div>
			<p className="content">{ props.attributes.content }</p>
		</div>
	)
},

Gutenberg va donc désormais pouvoir lire le HTML et retrouver les données, où comme on les appelle ici les attributs de notre bloc.

D’autres types d’attributs

On va voir qu’il existe plusieurs types d’attributs disponibles dans Gutenberg. On devrait à peu près tous les voir au cours de la formation. Si vous voulez déjà vous familiariser avec les autres, je vous invite à jeter un oeil à la documentation Gutenberg sur les attributs.

Un mot sur les classes CSS en JSX

Avez-vous remarqué que j’ai employé le mot-clé className et pas class dans mon HTML ? C’est normal, c’est une contrainte du langage JSX de React.

En fait quand le code est interprété ça reste du Javascript, et s’il voit le mot-clé class il va croire que c’est une classe JS (du point de vue programmation orientée objet).

C’est pour cela que React propose en remplacement className (avec un N majuscule).

Attention

Ne l'oubliez pas ! Quand on n'a pas l'habitude on a vite fait d'oublier. Et au début la moitié de mes erreurs provenait de là ! Alors notez le en rouge quelque part pour ne pas vous faire piéger !

En tous cas Emmet connait JSX et vous fera la bonne implémentation, si vous tapez p.content puis tabulation il autocomplètera alors correctement en <p className="content></p>.

Le changement de HTML dans save casse le bloc

Autre chose dont j’ai déjà fait mention dans le cycle de vie de Gutenberg mais qui est assez important pour en faire un petit rappel : quand vous changez le HTML d’un bloc, l’éditeur renverra une erreur lorsque vous rechargerez la page : le HTML reçu n’étant pas celui attendu.

Une erreur dans le contenu du bloc
Une erreur de bloc Gutenberg

C’est logique, mais du coup vous risquez de voir cette erreur souvent pendant le développement de vos blocs. Il suffit alors de supprimer et créer à nouveau le bloc.

Astuce : pour débugguer facilement, cliquez sur Comparer la conversion afin de voir le contenu attendu face au contenu réellement reçu.

Comparatif des versions attendues et reçues
On voit en rouge ce qui change entre les 2 HTML

La méthode edit

On vient de voir l’ajout des attributs et la légère modification du HTML contenu dans save, mais le plus gros changement intervient du coup dans la méthode edit, car il faut intégrer notre champ de formulaire. Je remet le code :

JS
edit: props => {

  // La fonction qui met à jour la valeur
  const onChangeContent = event => {
    props.setAttributes( { content: event.target.value } )
  }

  return (
    <div className={ props.className }>
    	{ props.isSelected ? ( // N'afficher le champ seulement si le bloc est actif
    		<input
    			type='text'
    			value={ props.attributes.content } // Valeur de l'attribut
    			onChange={ onChangeContent } // Évenement JS pour enregistrer le nouvel attribut au changement de valeur
    			placeholder={ __('Ecrivez un texte !') } 
			/>
		) : ( // Sinon
  			<p className="content">{ props.attributes.content }</p>
		) }
 	</div>
  )
},

On note l’apparition d’une fonction onChangeContent() avant le return, c’est celle qui va permettre de transmettre le contenu modifié à Gutenberg.

On remarque aussi qu’il y a pas mal de nouveautés dans la fonction return, on va commencer par là.

props.isSelected

Notre but est que lorsque l’on ne touche pas au bloc, il a une apparence normale (la même qu’on aura en front), et quand on édite ce bloc, le champ texte apparait.

Pour cela on va utiliser la propriété props.isSelected. Pour rappel les props sont les données envoyées par le parent du composant (ici Gutenberg).

Et props.isSelected est une valeur booléenne (vrai/faux) qui indique si le bloc est actuellement actif ou non.

  • Si oui : j’affiche le champ texte,
  • si non : j’affiche simplement la valeur dans un paragraphe

props.attributes

Notre valeur content a été définie comme un attribut de notre bloc, Gutenberg va donc nous la mettre à disposition via props.attributes.content. Et on le met entre accolades { ... } pour l’afficher dans du HTML, comme on le ferait dans n’importe quel langage de templating.

Information

En fait la valeur des attributs n'est pas stocké dans notre composant bloc, mais en amont dans Gutenberg, qui centralise toutes les datas. C'est pour cela que l'on utilise props à chaque fois : la donné est transmise par le parent via les props.

Le champ input

C’est un simple champ input basique qui est utilisé ici. On verra plus tard que WordPress propose des composants déjà tout fait pour des champs de formulaires ou boutons, qui nous seront fort utiles.

Dans les attributs on commence déjà par attribuer la valeur { props.attributes.content } dans l’attribut value du champ de texte (sinon la valeur ne sera pas réaffichée la prochaine fois que vous lancerez l’éditeur).

On assigne ensuite à l’événement onChange la fonction onChangeContent() qui sera lancée lorsque le contenu du champ sera modifié.

D’ailleurs notez qu’on revient ici aux bases du JS : on applique la fonction directement dans la balise, contrairement à jQuery qui externalisait tout et opérait à grand renfort de sélecteurs $('input').change().

La fonction onChangeContent()

Cette fonction va nous permettre de réattribuer la nouvelle valeur de notre contenu lorsqu’on la change. On utilise la fonction props.setAttributes fournie par Gutenberg en amont pour mettre à jour l’attribut « officiellement ».

Notre fonction récupère l’événement qui l’a lancée, et on obtient la valeur par event.target.value, qui ça aussi, est du JS absolument classique !

D’ailleurs on a bien la notation de fonction en fat arrow comme on a pu le voir sur les bases ESNext. J’aurais très bien pu écrire la version « classique », mais je vous conseille vivement d’opter pour la nouvelle syntaxe.

À éviter

En réalité je vous ai mené sur une fausse piste : WordPress propose un champ Input adapté aux blocs qui s'appelle Textcontrol. On le verra en temps voulu, lors de l'exemple du champ URL.

JS
// Notation moderne
const onChangeContent = event => {
    props.setAttributes( { content: event.target.value } )
}

// équivalent JS classique
function onChangeContent( event ) {
    props.setAttributes( { content: event.target.value } )
}

Et voilà ! vous avez votre premier bloc administrable. Un petit pas pour le développeur, un grand pas pour l’édition de contenu en temps réel !

Dans le prochain cours on va voir un composant bien plus sympa que notre champ de texte de base : le champ RichText qui nous permettra pleins de super choses.

0

Questions, réponses et commentaires

    Laisser un commentaire