Eigenen WordPress Block entwickeln – Teil 4: Eingabefelder mit RichText-Komponente hinzufügen

In dieser Artikelreihe erkläre ich, wie du von Grund auf deinen eigenen WordPress Block entwickeln kannst. Im vierten Teil sehen wir uns an, wie wir editierbare Felder mit der RichText-Komponente in unserem WordPress Block hinzufügen und im Frontend ausgeben können.

Zum Start der Serie und ersten Teil geht es hier.

Die RichText-Komponente im Überblick

Im derzeitigen Stand unseres WordPress Blocks wird Titel und Beschreibung statisch ausgegeben:

<h2>Titel des Blocks</h2>
<p>Beschreibung des Blocks.</p>

Diese zwei Felder wollen wir in Teil 4 für den Nutzer editierbar machen, sodass Titel und Beschreibung direkt im Block eingegeben werden können. Eingabe- bzw Textfelder im Block können wir mit der RichText-Komponente des Gutenberg Editors realisieren, welche wir vom Package wp.editor erhalten.

const { RichText } = wp.editor;

Die RichText-Komponente kann im JSX-Code des Return-Statements der Edit-Funktion gerendert werden und gibt dann ein editierbares Feld mit dem HTML-Attribut contenteditable aus.

<RichText
	tagName="p"
	value={ variable }
	className="css-class"
	onChange={ changeFunction }
	placeholder={ __( 'Placeholder Text', 'themecoder-block' ) }
/>

Die Komponente nimmt mehrere Properties entgegen. Zwingend erforderlich ist value für den aktuellen Wert bzw. Inhalt des Textfelds und onChange, mit der eine Funktion übergeben wird, welche bei Änderung des Texts im Eingabefeld für die Speicherung des Inhalts zuständig ist.

Optional können eine Reihe weiterer Properties definiert werden, unter anderem HTML-Tag (p, h2, div), CSS-Klasse und Platzhalter. Eine komplette Liste der Props findest du im Github-Repo.

Block-Attribute für Textfelder hinzufügen

Bevor wir die RichText-Komponente im Block einbauen, legen wir zuerst zwei neue Attribute für Titel und Beschreibung an. Diese werden im Objekt attributes in den Block Settings definiert.

/**
 * Register block
 */
registerBlockType(
	'themecoder-block/image-card',
	{
		title: __( 'Image Card', 'themecoder-block' ),

		category: 'layout',

		attributes: {
			title: {
				type: 'string',
				source: 'html',
				selector: '.tc-title',
			},
			description: {
				type: 'string',
				source: 'html',
				selector: '.tc-description',
			},
		},

		// Edit-Function + Save Function
	}
);

Unsere Textfelder werden direkt im HTML-Markup des Blocks gespeichert. Wir geben daher für die dazugehörigen Attribute den Typ type als string und die Quelle source als html an. Mit selector legen wir fest, wo genau im HTML-Markup unsere Attribute zu finden sind.

Wichtig zu wissen: Grundlagen zu Block-Attributen

1) Block-Attribute sind ein zentraler Bestandteil jedes WordPress Blocks und repräsentieren und strukturieren alle Daten (Inhalt, Optionen, Styling) im Block.

2) Blocks werden nicht einzeln in der WordPress Datenbank gespeichert, sondern landen als HTML-Markup mit allen anderen Blöcken eines Posts als post_content in der Tabelle wp_posts.

3) Blocks werden anhand HTML-Kommentare voneinander abgegrenzt. Beim Aufruf des Gutenberg Editors werden so aus dem ganzen HTML wieder einzelne Blocks generiert.

4) Der Gutenberg Editor extrahiert mit Hilfe der definierten Block-Attribute und deren Selektoren die Variablen wieder aus dem HTML-Code und speichert sie in einem Object-Tree.

5) Falls Attribute keinen Selektor aufweisen, werden sie statt im Markup im HTML-Kommentar des jeweiligen Blocks gespeichert.

Beispiel:

<!-- wp:paragraph {"align":"center"} -->
<p style="text-align:center">Beispiel-Text</p>
<!-- /wp:paragraph -->

Mit obigen HTML-Code wird ein Absatz-Block dargestellt, erkennbar an dem Block-Kommentar <!-- wp:paragraph -->. Es sind ebenfalls zwei Attribute gespeichert. Als Erstes der Inhalt mit dem Selektor p im Markup, als Zweites die Textausrichtung align: center im Kommentar.

RichText-Komponente in Edit-Funktion einbauen

Nach Registrierung der Attribute können wir die RichText-Komponente nun in der Edit-Funktion des Blocks einbauen. Wir ändern dazu den bisherigen Code wie folgt ab:

edit( props ) {
	const {
		attributes,
		className,
		setAttributes,
	} = props;

	function changeTitle( newTitle ) {
		setAttributes( { title: newTitle } );
	}

	function changeDescription( newDescription ) {
		setAttributes( { description: newDescription } );
	}

	return (
		<div className={ className }>
			<div className="tc-columns">

				<div className="tc-image">

				</div>

				<div className="tc-card">

					<RichText
						tagName="h2"
						value={ attributes.title }
						className="tc-title"
						onChange={ changeTitle }
						placeholder={ __( 'Add a title…', 'gt-blocks' ) }
						keepPlaceholderOnFocus
					/>

					<RichText
						tagName="p"
						value={ attributes.description }
						className="tc-description"
						onChange={ changeDescription }
						placeholder={ __( 'Write a description…', 'gt-blocks' ) }
						keepPlaceholderOnFocus
					/>

				</div>

			</div>
		</div>
	);
},

Okay, schauen wir uns die Funktion Zeile für Zeile genauer an.

Am Anfang nutzen wir wieder Object Destructuring zur Extraktion einiger Variablen aus den props unseres Blocks. Für unsere Eingabefelder benötigen wir nun zusätzlich das Objekt attributes und die Funktion setAttributes, mit der Attribute geändert werden können.

Anschließend definieren wir zwei Funktionen changeTitle() und changeDescription(), welche für die Änderung der jeweiligen Block-Attribute title und description zuständig sind, sobald der Nutzer den Inhalt im Eingabefeld manipuliert.

Als letzte Änderung ersetzen wir unseren statischen Titel und Beschreibung mit zwei RichText-Komponenten. Als value übergeben wir unsere Block-Attribute, mit onChange unsere Funktionen. Für den Titel nutzen wir h2 als tagName, für die Beschreibung p.

Der obige Code funktioniert, mit etwas ES6-Magie können wir die Syntax aber noch etwas reduzieren. Statt für jedes Textfeld umständlich eine eigene Funktion anzulegen, nutzen wir eine Arrow-Function direkt in der RichText-Komponente, um setAttributes aufzurufen.

<RichText
	tagName="h2"
	value={ title }
	className="tc-title"
	onChange={ ( value ) => setAttributes( { title: value } ) }
	placeholder={ __( 'Add a title…', 'gt-blocks' ) }
	keepPlaceholderOnFocus
/>

<RichText
	tagName="p"
	value={ description }
	className="tc-description"
	onChange={ ( value ) => setAttributes( { description: value } ) }
	placeholder={ __( 'Write a description…', 'gt-blocks' ) }
	keepPlaceholderOnFocus
/>

Die Funktionen changeTitle() und changeDescription() sind damit nicht mehr notwendig.

Anpassung der Save-Funktion mit RichText.Content

Schlussendlich muss der Inhalt unserer Eingabefelder auch noch im Frontend mit der Save-Funktion ausgegeben werden. Wir nutzen dafür ebenfalls die RichText-Komponente; die Ausgabe erfolgt hier aber mit RichText.Content. Das Attribut wird wie vorhin als value übergeben.

save( { attributes } ) {
	const {
		title,
		description,
	} = attributes;

	return (
		<div>
			<div className="tc-columns">

				<div className="tc-image">

				</div>

				<div className="tc-card">

					<RichText.Content
						tagName="h2"
						className="tc-title"
						value={ title }
					/>

					<RichText.Content
						tagName="p"
						className="tc-description"
						value={ description }
					/>

				</div>

			</div>
		</div>
	);
},

Sehr wichtig ist die Angabe der CSS-Klasse mit className, welcher mit dem selector-Wert des Attributs übereinstimmen muss. Passen die CSS-Klassen nicht zu den definierten Selektoren der Block-Attribute, können die Attribute nicht mehr aus dem HTML-Code herausgefiltert werden.

Invalidation Error im Gutenberg Editor

Nach Änderung der Save-Funktion werdet ihr diesen Fehler im Gutenberg Editor sehen:

Gutenberg Editor: Unerwarteter Inhalt

Der Invalidation Error tritt immer dann auf, wenn das gespeicherte HTML-Markup des Blocks nicht zum definierten Markup in der Save-Funktion passt. Diese Fehlermeldung wirst du bei der Entwicklung von Custom Blocks deshalb häufig sehen, nämlich immer dann wenn du die Save-Funktion modifiziert.

Für den Nutzer sollte dieser Fehler natürlich nie auftauchen, weshalb bei Updates des Block-Markups extra Vorkehrungen getroffen werden müssen, um Blocks ohne Fehler zu aktualisieren. Das ist aber genug Thema für einen eigenständigen Artikel.

Ergebnis und Ausblick auf Teil 5

Und das wars auch schon mit dem vierten Teil. Der Block sieht nahezu gleich aus, Titel und Beschreibung lassen sich nun aber vom Nutzer selbst eingeben. Nach dem Hinzufügen des Blocks werden die Platzhalter angezeigt:

Image Card Block Teil 4Du findest den kompletten Beispiel-Code für unseren Block in diesem Github-Repo: https://github.com/Netzberufler/themecoder-block

Im nächsten Teil werden wir uns mit der Erstellung von Toolbar- und Sidebar-Optionen beschäftigen, um das Styling und Layout des Blocks anpassen zu können.

Überblick über alle Beiträge dieser Serie