In eerdere artikelen op dit blog hebben we de ontwikkeling van WordPress blokken vanuit verschillende invalshoeken bekeken. We hebben de ontwikkeling van zowel statische als dynamische blokken onderzocht en de functionaliteit van core-blokken uitgebreid. Maar de aanpak die we tot nu toe hebben gevolgd, stelde ons eigenlijk alleen in staat om standaardblokken te maken die niet in realtime reageerden op gebruikersinteracties. Met andere woorden, deze blokken waren niet-interactief.

In dit artikel zullen we een nieuwe benadering van blokontwikkeling verkennen, waarmee we interactieve blokken kunnen maken dankzij een nieuwe, krachtige WordPress API: de WordPress Interactivity API. Deze API is geïntroduceerd in WordPress 6.5 en stelt je in staat om blokken te maken die in realtime reageren op gebruikersinteracties, waardoor je rijke gebruikerservaringen kunt creëren en je sites dynamisch, responsief en visueel aantrekkelijk kunt maken.

Er is veel om over te praten, maar laten we voordat we beginnen eerst eens kijken naar de essentiële vereisten!

Wat je nodig hebt voordat je begint met de Interactivity API

Omdat de Interactivity API is gebaseerd op React, heb je op zijn minst basiskennis nodig van server-side JavaScript en React, evenals bouwgereedschappen zoals npm en npx. Je hebt ook een grondige kennis nodig van de ontwikkeling van WordPress en de Gutenberg block editor.

Zodra je de nodige vaardigheden hebt verworven, heb je een lokale ontwikkelomgeving nodig waarmee je snel en gemakkelijk een WordPress site kunt starten. Wij raden DevKinsta aan, onze lokale ontwikkelsuite die speciaal is ontworpen voor WordPress. Met DevKinsta kun je in een paar klikken een nieuwe lokale WordPress site opzetten en deze tot in detail aanpassen.

Wanneer je een nieuw WordPress project aanmaakt in DevKinsta, kun je de volgende opties instellen:

  • Top Level domein: Standaard .local
  • PHP versie
  • Naam database
  • HTTPS inschakelen
  • WordPress details
  • WordPress automatisch bijwerken
  • Multisite

Daarnaast kun je een bestaande MyKinsta website importeren vanuit een backup.

Een lokale website configureren in DevKinsta
Een lokale website configureren in DevKinsta

Wat is de Interactivity API?

De Interactivity API is een WordPress-native API waarmee je interactiviteit kunt toevoegen aan Gutenberg blokken en dus aan berichten en pagina’s op een WordPress site. Het is een lichtgewicht, moderne oplossing met een declaratieve benadering voor het beheren van gebruikersinteracties.

Het maken van een interactief blok vanaf nul vereist geavanceerde PHP en server-side JavaScript ontwikkelvaardigheden. Het is echter niet nodig om bij elk nieuw project het wiel opnieuw uit te vinden, want WordPress biedt een template voor het maken van interactieve blokken:

npx @wordpress/create-block --template @wordpress/create-block-interactive-template

Dit template bevat alles wat je nodig hebt om een interactief blok te maken, inclusief twee werkende voorbeelden die je kunt gebruiken als referentie voor je eerste project: een knop om het huidige thema om te schakelen en een knop om een paragraaf uit te vouwen/inklappen.

Om te beginnen open je je favoriete opdrachtprompt, navigeer je naar de map Plugins van je lokale WordPress-installatie en typ je het volgende:

npx @wordpress/create-block your-interactive-block --template @wordpress/create-block-interactive-template

Wacht even tot de installatie is voltooid en open dan je projectmap met de code-editor van je voorkeur. Wij raden aan om Visual Studio Code te gebruiken, maar je kunt elke editor gebruiken waar jij je het prettigst bij voelt.

Het interactieve blok in Visual Studio Code
Het interactieve blokproject dat wordt geleverd door de @wordpress/create-block-interactive-template

Navigeer vanaf de commandoregel naar de map van de nieuwe plugin en start de ontwikkelserver met het volgende commando:

npm start

Vanaf nu zullen alle wijzigingen die je aanbrengt in je blok in realtime zichtbaar zijn in je WordPress installatie.

Navigeer vervolgens in je WordPress admin naar het Plugins scherm en activeer de Interactivity API plugin die je zojuist hebt gemaakt. Maak een nieuw bericht of pagina aan, zoek dan naar Your Interactive Block in de block inserter en voeg het toe aan je inhoud. Sla het bericht op en bekijk het voorbeeld op de voorkant. Je ziet een geel blok met twee knoppen. De eerste knop verandert de achtergrondkleur van het blok en de tweede knop toont of verbergt de inhoud van de alinea.

Een voorbeeld van een interactief blok
Een voorbeeld van een interactief blok dat door de @wordpress/create-block-interactive-template

Nu je een plugin hebt om naar te verwijzen voor de onderwerpen die in dit artikel zijn behandeld, kunnen we verder gaan en interactieve blokken verder uitdiepen.

De structuur van interactieve blokken

De structuur van interactieve blokken is hetzelfde als die van traditionele blokken. Je hebt nog steeds een package.json, een block.json, een edit.js bestand en een style.scss bestand nodig. Daarnaast heb je een render.php bestand nodig voor rendering aan de serverkant en een view.js bestand voor interactiviteit aan de frond-end.

Laten we eens kijken naar de specifieke bouwstenen van een interactief blok door de individuele bestanden van het startproject te behandelen.

package.json

Het package.json bestand wordt gebruikt in Node projecten om je project te identificeren, scripts te beheren en dependencies te beheren en te installeren tijdens de ontwikkeling.

Het volgende is de package.json voor het interactieve blok dat wordt geleverd door de create-block-interactive-template:

{
	"name": "your-interactive-block",
	"version": "0.1.0",
	"description": "An interactive block with the Interactivity API.",
	"author": "The WordPress Contributors",
	"license": "GPL-2.0-or-later",
	"main": "build/index.js",
	"scripts": {
		"build": "wp-scripts build --experimental-modules",
		"format": "wp-scripts format",
		"lint:css": "wp-scripts lint-style",
		"lint:js": "wp-scripts lint-js",
		"packages-update": "wp-scripts packages-update",
		"plugin-zip": "wp-scripts plugin-zip",
		"start": "wp-scripts start --experimental-modules"
	},
	"dependencies": {
		"@wordpress/interactivity": "latest"
	},
	"files": [
		"[^.]*"
	],
	"devDependencies": {
		"@wordpress/scripts": "^30.24.0"
	}
}

Vooral de secties scripts en dependencies zijn hier belangrijk.

  • build: Compileert de broncode naar JavaScript voor productie. De optie --experimental-modules maakt ondersteuning voor WordPress scriptmodules mogelijk.
  • start: Start de ontwikkelserver. Merk op dat de optie --experimental-modules opnieuw wordt opgegeven.
  • dependencies: Inclusief runtime dependencies met het nieuwste pakket van de Interactivity API.

block.json

Het bestand block.json is het manifest voor je Gutenberg blok. Het specificeert metadata, media, scripts en stijlen om te laden. Standaard genereert de create-block-interactive-template het volgende block.json:

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "create-block/your-interactive-block",
	"version": "0.1.0",
	"title": "Your Interactive Block",
	"category": "widgets",
	"icon": "media-interactive",
	"description": "An interactive block with the Interactivity API.",
	"example": {},
	"supports": {
		"interactivity": true
	},
	"textdomain": "your-interactive-block",
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css",
	"render": "file:./render.php",
	"viewScriptModule": "file:./view.js"
}

De volgende velden zijn essentieel voor een interactief blok:

  • apiVersion: 3 is de nieuwste versie van de Block API en ondersteunt de nieuwste blokfuncties, zoals Script Modules.
  • supports: Specificeert blokondersteuning. "interactivity": true voegt ondersteuning voor de Interactivity API toe.
  • render: Specificeert het PHP-bestand dat verantwoordelijk is voor de rendering in de frontend. In dit bestand voeg je de directives toe die een blok interactief maken.
  • viewScriptModule: Geeft het JavaScript-bestand op dat de interactiviteitslogica bevat. Dit bestand wordt alleen geladen op de front-end en alleen als de pagina het interactieve blok bevat.

render.php

Op render.php bouw je de opmaak van een dynamisch blok. Om je blok interactief te maken, moet je attributen toevoegen die de DOM-elementen van je blok interactief maken.

Het bestand render.php in het startproject ziet er als volgt uit:

<?php
/**
 * PHP file to use when rendering the block type on the server to show on the front end.
 *
 * The following variables are exposed to the file:
 *     $attributes (array): The block attributes.
 *     $content (string): The block default content.
 *     $block (WP_Block): The block instance.
 *
 * @see https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-metadata.md#render
 */

// Generates a unique id for aria-controls.
$unique_id = wp_unique_id( 'p-' );

// Adds the global state.
wp_interactivity_state(
	'create-block',
	array(
		'isDark'    => false,
		'darkText'  => esc_html__( 'Switch to Light', 'your-interactive-block' ),
		'lightText' => esc_html__( 'Switch to Dark', 'your-interactive-block' ),
		'themeText'	=> esc_html__( 'Switch to Dark', 'your-interactive-block' ),
	)
);
?>

<div
	<?php echo get_block_wrapper_attributes(); ?>
	data-wp-interactive="create-block"
	<?php echo wp_interactivity_data_wp_context( array( 'isOpen' => false ) ); ?>
	data-wp-watch="callbacks.logIsOpen"
	data-wp-class--dark-theme="state.isDark"
>
	<button
		data-wp-on--click="actions.toggleTheme"
		data-wp-text="state.themeText"
	></button>

	<button
		data-wp-on--click="actions.toggleOpen"
		data-wp-bind--aria-expanded="context.isOpen"
		aria-controls="<?php echo esc_attr( $unique_id ); ?>"
	>
		<?php esc_html_e( 'Toggle', 'your-interactive-block' ); ?>
	</button>

	<p
		id="<?php echo esc_attr( $unique_id ); ?>"
		data-wp-bind--hidden="!context.isOpen"
	>
		<?php
			esc_html_e( 'Your Interactive Block - hello from an interactive block!', 'your-interactive-block' );
		?>
	</p>
</div>

Dit is wat deze code doet:

  • wp_interactivity_state: Verkrijgt en/of stelt de initiële globale status in van een Interactivity API store.
  • data-wp-interactive: Schakelt de Interactivity API in op het DOM element en zijn children. De waarde moet de unieke namespace van je plugin of blok zijn.
  • wp_interactivity_data_wp_context(): Genereert de data-wp-context directive, die een lokale status geeft aan een specifiek HTML knooppunt en zijn kinderen.
  • data-wp-watch: Voert een callback uit wanneer een node wordt gemaakt en elke keer als de status of context verandert.
  • data-wp-class--dark-theme: Voegt de dark-theme klasse toe aan het HTML element of verwijdert deze.
  • data-wp-on--click: Voert code synchroon uit bij een klikgebeurtenis.
  • data-wp-text: Stelt de binnentekst van het HTML-element in.
  • data-wp-bind--aria-expanded en data-wp-bind--hidden: Zet HTML-attributen (aria-expanded en hidden) op de overeenkomstige elementen op basis van een booleaanse of tekenreekswaarde.

view.js

Dit bestand definieert de Store die de logica en gegevens bevat die nodig zijn voor het gedrag van het blok, inclusief status, acties en callbacks.

Het volgende is het bestand view.js dat is gegenereerd door het startproject:

/**
 * WordPress dependencies
 */
import { store, getContext } from '@wordpress/interactivity';

const { state } = store( 'create-block', {
	state: {
		get themeText() {
			return state.isDark ? state.darkText : state.lightText;
		},
	},
	actions: {
		toggleOpen() {
			const context = getContext();
			context.isOpen = ! context.isOpen;
		},
		toggleTheme() {
			state.isDark = ! state.isDark;
		},
	},
	callbacks: {
		logIsOpen: () => {
			const { isOpen } = getContext();
			// Log the value of `isOpen` each time it changes.
			console.log( `Is open: ${ isOpen }` );
		},
	},
} );
  • store: De hoofdfunctie die wordt gebruikt om de globale toestand en logica van het blok aan te maken en te registreren.
  • getContext: Een functie die binnen acties en callbacks wordt gebruikt om toegang te krijgen tot de lokale status (de context) van het DOM-element dat de gebeurtenis heeft getriggerd.
  • state: Definieert de globale reactieve gegevens van het blok.
  • actions: Bevat de functies die de logica definiëren en de toestand wijzigen.
  • callbacks: Bevat de functies die automatisch worden uitgevoerd als reactie op specifieke gebeurtenissen of toestandsveranderingen.

Het is veel om te verwerken, maar maak je geen zorgen! Alles zal duidelijker worden als je de volgende secties hebt gelezen.

Laten we nu de belangrijkste concepten van de Interactivity API bekijken: directives, store, state, actions en callbacks.

Interactivity API directives

Net als andere frontend bibliotheken zoals Alpine.js en Vue.js, gebruikt de Interactivity API speciale HTML attributen waarmee je kunt reageren op gebeurtenissen op de pagina, de staat van de applicatie kunt bijwerken, de DOM kunt manipuleren, CSS stijlen kunt toepassen, gebruikersinvoer kunt verwerken en nog veel meer.

Deze attributen worden directives genoemd en stellen je in staat om je opmaak te verbinden met de onderliggende JavaScript-logica.

Hieronder staat een lijst van de directives die je het meest zult gebruiken.

Functie Richtlijn Beschrijving
Activation/Namespace data-wp-interactive Activeert de API voor het element en zijn children. De waarde moet worden ingesteld op de unieke identifier van je plugin.
Local state data-wp-context Geeft een lokale status (“context”) voor het huidige element en al zijn kinderen. Het accepteert een JSON object. Merk op dat het aanbevolen wordt om wp_interactivity_data_wp_context() te gebruiken om het in te stellen in PHP (meestal render.php).
Attribute Binding data-wp-bind--[attribute] Stelt een HTML-attribuut in (bijvoorbeeld disabled, value) op basis van een reactieve status of contextwaarde (een booleaanse of tekenreekswaarde).
Text Modification data-wp-text Stelt de interne tekstinhoud van het element in. Dit accepteert alleen tekenreeksen.
CSS Class Toggling data-wp-class--[classname] Voegt een CSS klasse toe of verwijdert deze, afhankelijk van een booleaanse waarde.
Inline styling data-wp-style--[css-property] Voegt een inline stijlklasse toe of verwijdert deze afhankelijk van een booleaanse waarde.
Event Handling data-wp-on--[event] Voert code uit als reactie op standaard DOM-gebeurtenissen zoals click of mouseover.
Initial Execution data-wp-init Voert eenmalig een callback-functie uit, alleen wanneer de node wordt gemaakt.
State Watching data-wp-watch Voert een callback uit wanneer de node wordt gemaakt en opnieuw wanneer de state of context verandert.
List Iteration data-wp-each Rendert een lijst met elementen.

Kijk voor een volledige lijst met directives in de Interactivity API dev notes en API referentie.

Globale state, lokale context en afgeleide state

Voordat je de Interactivity API gaat gebruiken, is het essentieel dat je vertrouwd raakt met de fundamentele concepten van statebeheer in frontend ontwikkeling. Degenen die regelmatig ontwikkelen met React, Vue of Angular zullen al bekend zijn met deze concepten. Voor degenen die nieuw zijn met deze technologieën, kan het nuttig zijn om wat algemene definities te geven.

Globale state

Global state verwijst naar de verzameling gegevens die toegankelijk zijn vanuit bijna alle componenten van een applicatie. In het geval van de Interactivity API, bijvoorbeeld, beïnvloedt de globale state alle interactieve blokken op de pagina, waardoor ze synchroon blijven. Als een gebruiker bijvoorbeeld een product aan zijn mandje toevoegt, wordt dit gereflecteerd in het winkelwagenblok.

Als je de Interactivity API gebruikt, moet je de beginwaarden van de globale state op de server instellen met de functie wp_interactivity_state(). In het hierboven beschreven startproject wordt deze functie als volgt gebruikt in het bestand render.php:

// Adds the global state.
wp_interactivity_state(
	'create-block',
	array(
		'isDark'    => false,
		'darkText'  => esc_html__( 'Switch to Light', 'your-interactive-block' ),
		'lightText' => esc_html__( 'Switch to Dark', 'your-interactive-block' ),
		'themeText'	=> esc_html__( 'Switch to Dark', 'your-interactive-block' ),
	)
);

Deze functie accepteert twee argumenten:

  • Een unieke identifier voor de namespace van de winkel. In dit geval create-block.
  • Een array met gegevens die worden samengevoegd met de bestaande store namespace, als die bestaat.

De initiële globale statewaarden worden dan gebruikt om de pagina te renderen. Je hebt direct toegang tot de globale statewaarden door state te gebruiken in de attribuutwaarden van de directive, zoals in de volgende code:

<button
	data-wp-on--click="actions.toggleTheme"
	data-wp-text="state.themeText"
></button>

De functie store() biedt het belangrijkste toegangspunt tot de globale status vanuit JavaScript, beperkt tot de geselecteerde namespace. Als we teruggaan naar de code van het startproject, wordt de functie store() als volgt gebruikt in het bestand view.js:

import { store, getContext } from '@wordpress/interactivity';

const { state } = store( 'create-block', {
	state: { ... },
	actions: { ... },
	callbacks: { ... },
} );

Om toegang te krijgen tot de globale state kun je de property state gebruiken:

actions: {
	toggleTheme() {
		state.isDark = ! state.isDark;
	},
},

Lokale context

Lokale context is data die alleen toegankelijk is voor een specifiek component en zijn directe children. Een WordPress interactief blok biedt een onafhankelijke state voor het blok en zijn geneste elementen.

Wanneer je de Interactivity API gebruikt, kun je de Lokale context benaderen met de getContext() functie. Wanneer de gebruiker op de knop Toggle klikt, wordt de actie toggleOpen() geactiveerd en wordt er toegang verkregen tot de Lokale context van het component:

actions: {
	toggleOpen() {
		const context = getContext();
		context.isOpen = ! context.isOpen;
	},
},
  • getContext(): Haalt het lokale state-object van het blok op. De properties van dit object worden gedefinieerd in de component markup (render.php) met de functie wp_interactivity_data_wp_context().
  • context.isOpen = ! context.isOpen;: Verandert de waarde van de property isOpen in de lokale context van de component.

Afgeleide staat

Afgeleide toestand verwijst naar gegevens die dynamisch worden berekend uit bestaande globale of lokale states.

Kijk bijvoorbeeld eens naar de code in het bestand view.js, specifiek in deze sectie:

const { state } = store( 'create-block', {
	state: {
		get themeText() {
			return state.isDark ? state.darkText : state.lightText;
		},
	},
	...
}

Dit blok definieert de themeText Afgeleide state binnen de Globale toestand gedefinieerd in de create-block namespace.

  • get themeText() is geen vaste waarde, maar eerder een functie die wordt uitgevoerd elke keer dat je de property themeText probeert te lezen. Het moet niet worden aangeroepen als een normale functie omdat de Interactivity API het behandelt als een state property en automatisch de waarde ervan herberekent wanneer de waarden van andere state properties veranderen. In de bovenstaande code wordt de waarde van de property themeText telkens herberekend als de waarde van de property  isDark verandert. Als state.isDark true is, dan neemt themeText de waarde van state.darkText aan; anders neemt het de waarde van state.lightText aan.

Voor een uitgebreider overzicht van de concepten die in deze sectie worden beschreven, zie Begrip van globale state, lokale context en afgeleide state.

Actions en callbacks

Actions en callbacks bepalen de reactie op gebruikersinteractie en stateveranderingen.

De sectie actions van een interactief blok bevat functies die worden uitgevoerd als reactie op door de gebruiker gegenereerde gebeurtenissen. Deze functies dienen voornamelijk om de lokale of globale state van de component te wijzigen. Neem de volgende code uit het bestand view.js:

actions: {
	toggleOpen() {
		const context = getContext();
		context.isOpen = ! context.isOpen;
	},
	...
},
  • In dit deel van de code gebruikt de functie toggleOpen() getContext() om toegang te krijgen tot de Lokale context van het blok dat de actie heeft geactiveerd om de waarde van de property isOpen te wijzigen.

Op dezelfde manier kun je toegang krijgen tot de Globale state:

actions: {
	...,
	toggleTheme() {
		state.isDark = ! state.isDark;
	},
},
  • De functie toggleTheme() heeft direct toegang tot het globale object state en verandert de waarde van de property isDark.

Acties worden geactiveerd via de directive data-wp-on--[event]. In het bestand render.php staat bijvoorbeeld de volgende knop:

<button
	data-wp-on--click="actions.toggleOpen"
	data-wp-bind--aria-expanded="context.isOpen"
	aria-controls="<?php echo esc_attr( $unique_id ); ?>"
>
  • In deze HTML-code activeert het data-wp-on--click attribuut de toggleOpen actie wanneer de gebruiker op de omschakelknop klikt.

De sectie callbacks bevat functies die automatisch worden uitgevoerd als de gegevens waarvan ze afhankelijk zijn veranderen. Hun doel is om neveneffecten te produceren als reactie op een stateverandering.

In het basisproject dat is gegenereerd door create-block-interactive-template vind je de volgende callback:

callbacks: {
	logIsOpen: () => {
		const { isOpen } = getContext();
		// Log the value of `isOpen` each time it changes.
		console.log( `Is open: ${ isOpen }` );
	},
},
  • De logIsOpen functie gebruikt de isOpen variabele, die beschikbaar is in de Lokale context.
  • De callback haalt de waarde van isOpen op met getContext().
  • Elke keer als de waarde van isOpen verandert, gooit de functie een bericht naar de browserconsole.
Een bericht in de console informeert de gebruiker over de verandering in de Lokale context.
Een bericht in de console informeert de gebruiker over de verandering in de Lokale context.

Zo bouw je een interactief blok

Nu we de theorie hebben behandeld, is het tijd om de handen uit de mouwen te steken met code! In het tweede deel van deze handleiding leer je hoe je een interactief blok kunt maken waarmee gebruikers producten kunnen toevoegen aan een winkelmandje, met hoeveelheden en totalen die automatisch worden bijgewerkt. Dit is een voorbeeld ter demonstratie, maar we hopen dat het een duidelijk inzicht geeft in het gebruik van status, acties en callbacks.

Het interactieve blok in de editor
Het interactieve blok in de editor

We maken een blok met de naam Interactive counter met behulp van de create-block-interactive-template. Om te beginnen open je je opdrachtprompt en typ je het volgende:

npx @wordpress/create-block interactive-counter --template @wordpress/create-block-interactive-template

Navigeer vervolgens naar je nieuwe projectmap en voer de eerste build uit.

cd interactive-counter && npm run build

Open het project nu in je code editor. Zoek in de map /src naar het bestand block.json. Het zou er ongeveer zo uit moeten zien:

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "create-block/interactive-counter",
	"version": "0.1.0",
	"title": "Interactive Counter",
	"category": "widgets",
	"icon": "media-interactive",
	"description": "An interactive block with the Interactivity API.",
	"supports": {
		"interactivity": true
	},
	"textdomain": "interactive-counter",
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css",
	"render": "file:./render.php",
	"viewScriptModule": "file:./view.js"
}

Pas het gerust aan, maar zorg ervoor dat je de essentiële velden zoals hierboven beschreven niet wijzigt.

Het bestand edit.js

De volgende stap is het maken van het blok dat in de editor zal verschijnen. Hiervoor moet je het bestand /src/edit.js bewerken. Open het bestand en wijzig het als volgt:

import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import './editor.scss';

export default function Edit({ attributes, setAttributes }) {
	const blockProps = useBlockProps();
	const products = [
		{ id: 'product1', name: __('Product 1', 'interactive-counter'), price: 10.00 },
		{ id: 'product2', name: __('Product 2', 'interactive-counter'), price: 15.00 },
		{ id: 'product3', name: __('Product 3', 'interactive-counter'), price: 20.00 },
	];

	return (
		<div {...blockProps}>
			<h3>{__('Shopping Cart', 'interactive-counter')}</h3>
			<ul>
				{products.map((product) => (
					<li key={product.id} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px' }}>
						<span style={{ flex: 1 }}>{product.name} - ${product.price.toFixed(2)}</span>
						<div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}>
							<button disabled>-</button>
							<span>0</span>
							<button disabled>+</button>
						</div>
						<span style={{ flex: 1, textAlign: 'right' }}>
							{__('Subtotal:', 'interactive-counter')} $0.00
						</span>
					</li>
				))}
			</ul>
			<div style={{ borderTop: '1px solid #ccc', paddingTop: '15px' }}>
				<p style={{ display: 'flex', justifyContent: 'space-between' }}>
					<strong>{__('Subtotal:', 'interactive-counter')}</strong>
					<span>$0.00</span>
				</p>
				<p style={{ display: 'flex', justifyContent: 'space-between' }}>
					<strong>{__('Tax (22%):', 'interactive-counter')}</strong>
					<span>$0.00</span>
				</p>
				<p style={{ display: 'flex', justifyContent: 'space-between' }}>
					<strong>{__('Total:', 'interactive-counter')}</strong>
					<span>$0.00</span>
				</p>
			</div>
			<p>{__('Quantities and totals will be interactive in the frontend.', 'interactive-counter')}</p>
		</div>
	);
}

Deze code genereert een custom blok in de backend. Het blok zal alleen interactief zijn in het front-end. Meer details over het bestand /src/edit.js vind je in onze Gutenberg blok ontwikkelingsgidsen.

Het render.php bestand

Het volgende bestand dat je moet bewerken is /src/render.php. Open het bestand en vervang de bestaande code door het volgende:

<?php
/**
 * Render callback for the interactive-counter block.
 */

$products = [
	['id' => 'product1', 'name' => __('Product 1', 'interactive-counter'), 'price' => 10.00],
	['id' => 'product2', 'name' => __('Product 2', 'interactive-counter'), 'price' => 15.00],
	['id' => 'product3', 'name' => __('Product 3', 'interactive-counter'), 'price' => 20.00],
];

// Initialize global state
wp_interactivity_state('interactive-counter', [
	'products' => array_map(function ($product) {
		return [
			'id' => $product['id'],
			'name' => $product['name'],
			'price' => $product['price'],
			'quantity' => 0,
			'subtotal' => '0.00',
		];
	}, $products),
	'vatRate' => 0.22,
]);

Dit is wat deze code doet:

  • Ten eerste maakt het een array van producten met een vaste codering. Elk product heeft een ID, een naam en een prijs.
  • Vervolgens wordt de Global state geïnitialiseerd met wp_interactivity_state. De eerste parameter is de winkelnaam, die moet overeenkomen met die in view.js.
  • Daarna wordt de vorige matrix van producten gekoppeld aan een nieuwe matrix products, waarbij hoeveelheid en subtotaal worden toegevoegd aan de eigenschappen van de oorspronkelijke matrix. Deze nieuwe matrix biedt de gegevensstructuur die je zult gebruiken in view.js.
  • vatRate stelt de standaardwaarde voor de belastingberekening in.

Voeg vervolgens het volgende toe aan de bovenstaande code:

<div <?php echo get_block_wrapper_attributes(); ?> data-wp-interactive="interactive-counter" data-wp-init="callbacks.init">
	<h3><?php echo esc_html__('Cart', 'interactive-counter'); ?></h3>
	<ul>
		<?php foreach ($products as $index => $product) : ?>
			<li data-wp-context='{
				"productId": "<?php echo esc_attr($product['id']); ?>",
				"quantity": 0,
				"subtotal": "0.00"
			}' 
			data-wp-bind--data-wp-context.quantity="state.products[<?php echo $index; ?>].quantity" 
			data-wp-bind--data-wp-context.subtotal="state.products[<?php echo $index; ?>].subtotal">
				<span class="product-name"><?php echo esc_html($product['name']); ?> - $<?php echo esc_html(number_format($product['price'], 2)); ?></span>
				<div class="quantity-controls">
					<button data-wp-on--click="actions.decrement">-</button>
					<span data-wp-text="context.quantity">0</span>
					<button data-wp-on--click="actions.increment">+</button>
				</div>
				<span class="product-subtotal">
					<?php echo esc_html__('Subtotal:', 'interactive-counter'); ?>
					$<span data-wp-text="context.subtotal">0.00</span>
				</span>
			</li>
		<?php endforeach; ?>
	</ul>
	<div class="totals">
		<p>
			<strong><?php echo esc_html__('Subtotal:', 'interactive-counter'); ?></strong>
			$ <span data-wp-text="state.subtotal">0.00</span>
		</p>
		<p>
			<strong><?php echo esc_html__('Tax (22%):', 'interactive-counter'); ?></strong>
			$ <span data-wp-text="state.vat">0.00</span>
		</p>
		<p>
			<strong><?php echo esc_html__('Total:', 'interactive-counter'); ?></strong>
			$ <span data-wp-text="state.total">0.00</span>
		</p>
	</div>
</div>

Dit is wat deze code doet:

  • De get_block_wrapper_attributes() functie in de div container is een WordPress functie die de standaard attributen van een blok genereert. In dit geval genereert het het klasse attribuut "wp-block-create-block-interactive-counter".
  • Het data-wp-interactive attribuut maakt dit blok interactief.
  • Het data-wp-init attribuut activeert de init callback gedefinieerd in view.js.
  • De foreach loop genereert een lijstitem voor elk product in de products array.
  • data-wp-context definieert de Lokale context voor het blok.
  • data-wp-bind bindt de waarde van data-wp-context.quantity aan de globale property state.products[$index].quantity.
  • Hetzelfde gebeurt in de regel hieronder met het subtotaal.
  • De volgende twee knoppen activeren de acties decrement en increment dankzij het attribuut data-wp-on--click.
  • Het data-wp-text attribuut in de span werkt de inhoud van het element bij op basis van de huidige waarde van context.quantity.

De rest van de code spreekt voor zich, dus laten we doorgaan naar het volgende bestand.

Het bestand view.js

Dit bestand bevat de logica voor je interactieve blok.

import { store, getContext } from '@wordpress/interactivity';

store('interactive-counter', {
	state: {
		get subtotal() {
			const { products } = store('interactive-counter').state;
			return products
				.reduce((sum, product) => sum + product.price * (product.quantity || 0), 0)
				.toFixed(2);
		},
		get vat() {
			const { subtotal, vatRate } = store('interactive-counter').state;
			return (subtotal * vatRate).toFixed(2);
		},
		get total() {
			const { subtotal, vat } = store('interactive-counter').state;
			return (parseFloat(subtotal) + parseFloat(vat)).toFixed(2);
		},
	},
	actions: {
		increment: () => {
			const context = getContext();
			const { products } = store('interactive-counter').state;
			const product = products.find(p => p.id === context.productId);
			if (product) {
				product.quantity = (product.quantity || 0) + 1;
				product.subtotal = (product.price * product.quantity).toFixed(2);
				context.quantity = product.quantity;
				context.subtotal = product.subtotal;
				console.log(`Incremented ${context.productId}:`, { quantity: product.quantity, subtotal: product.subtotal, context });
			} else {
				console.warn('Product not found:', context.productId);
			}
		},
		decrement: () => {
			const context = getContext();
			const { products } = store('interactive-counter').state;
			const product = products.find(p => p.id === context.productId);
			if (product && (product.quantity || 0) > 0) {
				product.quantity -= 1;
				product.subtotal = (product.price * product.quantity).toFixed(2);
				context.quantity = product.quantity;
				context.subtotal = product.subtotal;
				console.log(`Decremented ${context.productId}:`, { quantity: product.quantity, subtotal: product.subtotal, context });
			} else {
				console.warn('Cannot decrement:', context.productId, product?.quantity);
			}
		},
	},
	callbacks: {
		init: () => {
			const { products } = store('interactive-counter').state;
			products.forEach((product, index) => {
				product.quantity = 0;
				product.subtotal = '0.00';
				console.log(`Initialized product ${index}:`, { id: product.id, quantity: product.quantity, subtotal: product.subtotal });
			});
		},
	},
});

Dit bestand definieert de opslagruimte voor de interactive-counter namespace. Het beheert status, acties en callbacks:

store('interactive-counter', {
	state: { ... },
	actions: { ... },
	callbacks: { ... },
});

Laten we het eens van dichterbij bekijken.

  • state: Definieert drie berekende state properties (getters): subtotal, vat, en total. Deze functies halen waarden op uit de globale state en berekenen de terug te geven waarden.
  • actions: Definieert twee functies die worden uitgevoerd op gebeurtenissen: increment en decrement. Deze functies halen de array products op uit de globale state, halen het huidige product op uit de lokale context op basis van context.productId, werken de waarden van de properties van het huidige product bij (quantity en subtotal) en synchroniseren de lokale context met de nieuwe waarden.
  • callbacks: Definieert een init callback voor initialisatie.

De volgende afbeelding toont het interactieve blok in de frontend.

Een interactieve teller gebouwd met de Interactivity API
Een interactieve teller gebouwd met de Interactivity API

Samenvatting

In dit artikel hebben we de belangrijkste functies van de WordPress Interactivity API geïntroduceerd. We hebben ons verdiept in belangrijke concepten zoals Global state, Local context, directives, acties en callbacks. Je hebt geleerd hoe je een interactief blok vanaf nul kunt maken met behulp van de @wordpress/create-block-interactive-template en we hebben dit in de praktijk gebracht door een echt blok te maken dat reageert op invoer van de gebruiker.

We hopen dat we je de nodige hulpmiddelen en kennis hebben gegeven om fantastische, dynamische en interactieve WordPress websites te maken met behulp van de WordPress Interactivity API.

Veel plezier met coderen!

Carlo Daniele Kinsta

Carlo is een gepassioneerd liefhebber van webdesign en front-end development. Hij werkt al meer dan 10 jaar met WordPress, ook in samenwerking met Italiaanse en Europese universiteiten en onderwijsinstellingen. Hij heeft tientallen artikelen en gidsen over WordPress geschreven, gepubliceerd op zowel Italiaanse als internationale websites en in gedrukte tijdschriften. Je kunt Carlo vinden op X en LinkedIn.