In precedenza abbiamo esplorato la memorizzazione dei dati dell’editor di blocchi di WordPress (Gutenberg) negli attributi di blocco e in post meta, ma sapevi che puoi archiviare e recuperare dalla tabella delle opzioni di WordPress importando apida @wordpress/api.
In questa guida diamo un’occhiata a cosa scriveresti classicamente in PHP come update_optione get_option.
Per implementarlo, dobbiamo sfruttare il ciclo di vita di React, quindi cercheremo di creare un componente React importando Componentda @wordpress/element.
Prerequisiti
- Familiarizzare con la creazione di plugin per WordPress Gutenberg
- Familiarizzare con i blocchi dinamici e il rendering lato server
- Comprendi come creare meta box in Gutenberg
Quest’ultimo requisito è utile per l’interfaccia utente che utilizzeremo in questa guida, tuttavia nelle applicazioni del mondo reale è probabile che tu utilizzi questo metodo in una barra laterale o nella pagina delle opzioni.
Registra le Opzioni in PHP
Prima di poter utilizzare un’opzione in JavaScript, dobbiamo assicurarci di averla registrata in PHP utilizzando register_settinge che l’ show_in_restargomento sia stato impostato su true.
Seguendo la guida al blocco dinamico, apri il file PHP radice del plugin (in questo caso wholesome-plugin.php) e aggiungi il seguente codice in fondo a quel file dopo tutte le altre funzioni:
function wholesomecode_wholesome_plugin_register_settings() {
register_setting(
'wholesomecode_wholesome_plugin_settings',
'wholesomecode_wholesome_plugin_example_text',
[
'default' => '',
'show_in_rest' => true,
'type' => 'string',
]
);
}
add_action( 'init', 'wholesomecode_wholesome_plugin_register_settings' );
Questo codice registra un meta campo chiamato wholesomecode_wholesome_plugin_block_textper il wholesomecode_wholesome_plugin_settingsgruppo di opzioni. Garantisce inoltre che l’API REST possa accedere a questo meta campo con il show_in_restvalore impostato su true.
Crea il componente
Apri il /src/edit.jsfile, modificheremo in qualche modo la struttura di questo file in modo da poter produrre il nostro file Component.
Taglia e incolla l’intero blocco di codice nel /src/edit.jsfile, tratteremo ciò che fa in un momento:
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import {
Panel,
PanelBody,
TextControl,
} from '@wordpress/components';
import { Component } from '@wordpress/element';
import './editor.scss';
class OptionsExample extends Component {
constructor() {
super( ...arguments );
this.state = { exampleText: '' };
}
render() {
const { exampleText } = this.state;
return (<Panel>
<PanelBody
title={ __( 'Example Meta Box', 'wholesome-plugin') }
icon="admin-plugins"
>
<TextControl
help={ __( 'This is an example text field.', 'wholesome-plugin') }
label={ __( 'Example Text', 'wholesome-plugin') }
onChange={ (exampleText) => this.setState( { exampleText }) }
value={ exampleText }
/>
</PanelBody>
</Panel>) }
}
export default function Edit( props) {
return (<div { ...useBlockProps() }>
<OptionsExample { ...props }/>
</div>
);
}
Potresti riconoscere che l’interfaccia utente che abbiamo messo in atto è esattamente la stessa della guida Gutenberg Meta Box, in cui abbiamo utilizzato gli attributi post meta. Potresti anche notare che non stiamo ancora ricevendo o impostando informazioni usando le opzioni, e invece stiamo solo usando il componente state.
Usando lo stato
Il motivo per cui abbiamo creato un componente personalizzato e poi lo abbiamo passato alla nostra Editfunzione è che possiamo sfruttare lo stato. Lo abbiamo fatto perché:
- Creeremo una funzione per caricare le opzioni dall’API e dobbiamo memorizzarla nello stato in modo che i nostri componenti possano leggerla
- Non vogliamo che l’API aggiorni le opzioni non appena il testo cambia nella nostra casella di testo, quindi abbiamo bisogno di una funzione per salvare lo stato nelle opzioni tramite l’API una volta che il post è stato salvato
Usare lo stato è piuttosto semplice. Nel costruttore inizializziamo lo stato in questo modo:
this.state = { exampleText: '' };
E nel componente accediamo in modo simile a come abbiamo avuto accesso agli attributi nella guida precedente:
const { exampleText } = this.state;
La differenza è che, sul nostro onChangemetodo, invece di utilizzare setAttributesutilizziamo this.setState.
Ottenere le opzioni dall’API
Nella parte superiore del documento importa apida @wordpress/api:
import api from '@wordpress/api';
Aggiungi una nuova proprietà in cui viene inizializzato lo stato di isAPILoaded. Avremo bisogno di questo per assicurarci di non tentare di accedere all’API o di eseguire il rendering del componente prima che l’API sia stata caricata.
this.state = {
exampleText: '',
isAPILoaded: false,
};
Ora all’interno del Component che abbiamo creato, aggiungi un blocco di codice sotto il costruttore chiamato componentDidMount. Questo è un metodo del ciclo di vita React, che viene chiamato dopo che un componente è stato aggiunto al DOM.
In quel blocco di codice aggiungi quanto segue:
componentDidMount() {
api.loadPromise.then(() => {
this.settings = new api.models.Settings();
const { isAPILoaded } = this.state;
if (isAPILoaded === false) {
this.settings.fetch().then( (response) => {
this.setState( {
exampleText: response[ 'wholesomecode_wholesome_plugin_example_text' ],
isAPILoaded: true,
} );
} );
}
} );
}
Qui accediamo all’opzione che abbiamo registrato in precedenza con la register_settingfunzione.
Questo blocco di codice esegue le seguenti operazioni:
- Ottiene le impostazioni dall’API delle impostazioni Guttenberg di WordPress.
- Ottiene
isAPILoadeddallo stato - Se l’API non è stata caricata, recupera le Impostazioni dall’API in a
response - Quindi impostiamo lo stato per aggiornare lo stato con l’opzione a cui vogliamo accedere e impostiamo lo
isAPILoadedstato su true
Arresta il rendering del blocco senza impostazioni
Non vogliamo che il nostro blocco venga visualizzato prima che le impostazioni siano state popolate. Per occuparci di questo possiamo importare un PlaceHolder e uno Spinner da $wordpress/components:
import {
Panel,
PanelBody,
Placeholder,
Spinner,
TextControl,
} from '@wordpress/components';
Quindi nel metodo di rendering del componente, assicurati di ottenere isAPILoadeddallo stato e emetti Placeholdere Spinnerse non lo è:
const {
exampleText,
isAPILoaded,
} = this.state;
if (! isAPILoaded) {
return (<Placeholder>
<Spinner />
</Placeholder>
);
}
In questo modo, se le opzioni non sono state caricate, otteniamo un bel segnaposto fino al caricamento del componente:
Segnaposto e Spinner
Entrare in Gutenberg su Save
Ora che stiamo leggendo le opzioni dalla tabella delle opzioni, abbiamo bisogno di un modo per salvare quelle opzioni quando le cambiamo. Per fare ciò andremo al subscribedatastore di WordPress Gutenberg, che indicherà quando qualcosa è cambiato.
Usando questo creeremo un listener per quando il post verrà salvato e salveremo le nostre impostazioni quando ciò accade.
Per fare questo importare subscribee selectda @wordpress/data.
import { select, subscribe } from '@wordpress/data';
Quindi nella parte superiore del componentDidMountblocco di codice, scrivi quanto segue:
subscribe(() => {
const { exampleText } = this.state;
const isSavingPost = select('core/editor').isSavingPost();
const isAutosavingPost = select('core/editor').isAutosavingPost();
if (isAutosavingPost) {
return;
}
if (! isSavingPost) {
return;
}
const settings = new api.models.Settings( {
[ 'wholesomecode_wholesome_plugin_example_text' ]: exampleText,
} );
settings.save();
});
Il codice esegue le seguenti operazioni:
- Verifica se il post viene salvato
- Verifica se il salvataggio è automatico
- Se il post sta salvando e non è un salvataggio automatico, inserisci le nuove impostazioni nell’API delle impostazioni
- Attiva un salvataggio dell’API delle impostazioni.
Un piccolo trucco
Potremmo lasciare il nostro codice in questo modo, ma poiché stiamo mettendo le nostre impostazioni in un blocco e non in una barra laterale o in un altro componente dell’editor, se cambiamo una delle opzioni e nient’altro nell’editor, il pulsante ‘salva’ non lo fa diventare attivo.
Questo perché non stiamo usando setAttributeso altro per alterare il codice effettivo del blocco.
Possiamo aggirare questo problema, semplicemente modificando un’altra parte del post o aggiungendo un piccolo hack nel TextControlcodice:
onChange={ (exampleText) => { this.setState( { exampleText } ); setAttributes( { exampleText }) } }
Ricordandoci di mettere questa riga di codice all’inizio del metodo render da estrarre setAttributesda props(perché stiamo usando un Component a cui accediamo a prop leggermente diversi con this.
const { setAttributes } = this.props;
Ora, quando cambiamo il nostro attributo, un attributo "falso" cambierà, facendo pensare all’editor che ora possiamo salvare il post.
È un po’ complicato, ma per questo caso d’uso fa ciò di cui abbiamo bisogno.
L’intero Editcodice
Ecco tutto il codice necessario per il Editmetodo:
io ` import {} da ‘ @wordpress /i18n’; importa API da ‘ @wordpress /api’; importa {useBlockProps} da ‘ @wordpress /block-editor’; import {Pannello, PanelBody, Placeholder, Spinner, TextControl, } da ‘ @wordpress /components’; importa {seleziona, iscriviti} da ‘ @wordpress /data’; importa {Componente} da ‘ @wordpress /elemento’;
importa ‘./editor.scss’;
class OptionsExample estende Component { constructor() { super( …arguments );
this.state = {
exampleText: '',
isAPILoaded: false,
};
}
componentDidMount() {
subscribe( () => {
const { exampleText } = this.state;
const isSavingPost = select('core/editor').isSavingPost();
const isAutosavingPost = select('core/editor').isAutosavingPost();
if (isAutosavingPost) {
return;
}
if (! isSavingPost) {
return;
}
const settings = new api.models.Settings( {
[ 'wholesomecode_wholesome_plugin_example_text' ]: exampleText,
} );
settings.save();
});
api.loadPromise.then( () => {
this.settings = new api.models.Settings();
const { isAPILoaded } = this.state;
if (isAPILoaded === false) {
this.settings.fetch().then( (response) => {
this.setState( {
exampleText: response[ 'wholesomecode_wholesome_plugin_example_text' ],
isAPILoaded: true,
} );
} );
}
} );
}
render() {
const {
exampleText,
isAPILoaded,
} = this.state;
const { setAttributes } = this.props;
if (! isAPILoaded) {
return (<Placeholder>
<Spinner />
</Placeholder>
);
}
return (<Panel>
<PanelBody
title={ __( 'Example Meta Box', 'wholesomecode') }
icon="admin-plugins"
>
<TextControl
help={ __( 'This is an example text field.', 'wholesome-plugin') }
label={ __( 'Example Text', 'wholesome-plugin') }
onChange={ (exampleText) => { this.setState( { exampleText } ); setAttributes( { exampleText }) } }
value={ exampleText }
/>
</PanelBody>
</Panel>) }
}
funzione di esportazione predefinita Modifica( props) { return (
); }
### Remove the Attributes
Option up `src/index.js` and remove the attributes block that we placed there in the previous guides. We are not storing any attributes, the data will be pushed into and retrieved from the options table.
Render the Output
Because we have saved our attribute as settings in the WordPress options table, we could output this anywhere in WordPress using `get_option`:
get_option( ‘wholesomecode_wholesome_plugin_block_text’, " );
Continuing from the [Dynamic Block guide](https://wholesomecode.ltd/guides/php-render-block-wordpress-gutenberg/), let's see how we can access this attribute on the server side in PHP.
With this in mind, let’s update our `register_block_type` to output the option:
register_block_type( ‘wholesomecode/wholesome-plugin’, array( ‘editor_script’ => ‘wholesomecode-wholesome-plugin-block-editor’, ‘editor_style’ => ‘wholesomecode-wholesome-plugin-block-editor’, ‘render_callback’ = > funzione($attributi, $contenuto) { $example_text = get_option( ‘wholesomecode_wholesome_plugin_example_text’); return "
$testo_esempio
"; }, ‘style’ => ‘wholesomecode-wholesome-plugin-block’,) );
Note that we no longer need to register the `attributes` here, because we are only accessing the post meta field via the `get_post_meta` function.
5.
Using the Block
--------------------
Putting it all together, let’s see the block in action:
:
importa {} da ‘ @wordpress /i18n’; importa API da ‘ @wordpress /api’; importa {useBlockProps} da ‘ @wordpress /block-editor’; import {Pannello, PanelBody, PanelRow, Placeholder, SelectControl, Spinner, TextControl, ToggleControl, } da ‘ @wordpress /components’; importa {seleziona, iscriviti} da ‘ @wordpress /data’; importa {Componente} da ‘ @wordpress /elemento’;
importa ‘./editor.scss’;
class OptionsExample estende Component { constructor() { super( …arguments );
this.state = {
exampleSelect: '',
exampleText: '',
exampleText2: '',
exampleText3: '',
exampleToggle: false,
isAPILoaded: false,
};
}
componentDidMount() {
subscribe( () => {
const {
exampleSelect,
exampleText,
exampleText2,
exampleText3,
exampleToggle,
} = this.state;
const isSavingPost = select('core/editor').isSavingPost();
const isAutosavingPost = select('core/editor').isAutosavingPost();
if (isAutosavingPost) {
return;
}
if (! isSavingPost) {
return;
}
const settings = new api.models.Settings( {
[ 'wholesomecode_wholesome_plugin_example_select' ]: exampleSelect,
[ 'wholesomecode_wholesome_plugin_example_text' ]: exampleText,
[ 'wholesomecode_wholesome_plugin_example_text_2' ]: exampleText2,
[ 'wholesomecode_wholesome_plugin_example_text_3' ]: exampleText3,
[ 'wholesomecode_wholesome_plugin_example_toggle' ]: exampleToggle,
} );
settings.save();
});
api.loadPromise.then( () => {
this.settings = new api.models.Settings();
const { isAPILoaded } = this.state;
if (isAPILoaded === false) {
this.settings.fetch().then( (response) => {
this.setState( {
exampleSelect: response[ 'wholesomecode_wholesome_plugin_example_select' ],
exampleText: response[ 'wholesomecode_wholesome_plugin_example_text' ],
exampleText2: response[ 'wholesomecode_wholesome_plugin_example_text_2' ],
exampleText3: response[ 'wholesomecode_wholesome_plugin_example_text_3' ],
exampleToggle: Boolean( response[ 'wholesomecode_wholesome_plugin_example_toggle' ] ),
isAPILoaded: true,
} );
} );
}
} );
}
render() {
const {
exampleSelect,
exampleText,
exampleText2,
exampleText3,
exampleToggle,
isAPILoaded,
} = this.state;
if (! isAPILoaded) {
return (<Placeholder>
<Spinner />
</Placeholder>
);
}
return (<Panel>
<PanelBody
title={ __( 'Example Meta Box', 'wholesome-plugin') }
icon="admin-plugins"
>
<SelectControl
help={ __( 'An example dropdown field.', 'wholesome-plugin') }
label={ __( 'Example Select', 'wholesome-plugin') }
onChange={ (exampleSelect) => this.setState( { exampleSelect }) }
options={ [
{
label: __( 'Please Select...', 'wholesome-plugin' ),
value: '',
},
{
label: __( 'Option 1', 'wholesome-plugin' ),
value: 'option-1',
},
{
label: __( 'Option 2', 'wholesome-plugin' ),
value: 'option-2',
},
] }
value={ exampleSelect }
/>
<TextControl
help={ __( 'This is an example text field.', 'wholesome-plugin') }
label={ __( 'Example Text', 'wholesome-plugin') }
onChange={ (exampleText) => this.setState( { exampleText }) }
value={ exampleText }
/>
<PanelRow>
<TextControl
help={ __( 'Use PanelRow to place controls inline.', 'wholesome-plugin') }
label={ __( 'Example Text 2', 'wholesome-plugin') }
onChange={ (exampleText2) => this.setState( { exampleText2 }) }
value={ exampleText2 }
/>
<TextControl
help={ __( 'This control is inline.', 'wholesome-plugin') }
label={ __( 'Example Text 3', 'wholesome-plugin') }
onChange={ (exampleText3) => this.setState( { exampleText3 }) }
value={ exampleText3 }
/>
</PanelRow>
<ToggleControl
checked={ exampleToggle }
help={ __( 'An example toggle.', 'wholesome-plugin') }
label={ __( 'Example Toggle', 'wholesome-plugin') }
onChange={ (exampleToggle) => this.setState( { exampleToggle }) }
/>
</PanelBody>
</Panel>) }
}
funzione di esportazione predefinita Modifica( props) { return (
); } `
Ecco il risultato:
Opzioni extra
- Dai un’occhiata alla creazione di blocchi figlio nidificati con il
InnerBlockscomponente - Dai un’occhiata all’utilizzo dei meta-campi post nei blocchi di Gutenberg
- Dai un’occhiata alla creazione di Meta Box personalizzate in Gutenberg