Attivare Javascript al salvataggio in Gutenberg (Block Editor)
Ho un metabox a cui vorrei attivare del Javascript quando un post viene salvato (per ricaricare la pagina in questo caso d'uso).
Nell'editor classico, questo può essere fatto tramite un semplice reindirizzamento agganciato a save_post
(con una priorità alta)
Ma dato che Gutenberg converte il processo di salvataggio per i metabox esistenti in chiamate AJAX individuali ora, deve essere fatto in javascript, quindi come posso:
Ascoltare un evento in cui tutti i processi di salvataggio sono completati e poi attivare il javascript? Se sì, come si chiama questo evento? Esiste una referenza per questi eventi da qualche parte? OPPURE
Attivare javascript all'interno del processo di salvataggio AJAX del metabox, che può poi controllare lo stato del processo di salvataggio della pagina genitore prima di continuare?

Non sono sicuro se esiste un modo migliore, ma sto ascoltando subscribe
invece di aggiungere un event listener al pulsante:
wp.data.subscribe(function () {
var isSavingPost = wp.data.select('core/editor').isSavingPost();
var isAutosavingPost = wp.data.select('core/editor').isAutosavingPost();
if (isSavingPost && !isAutosavingPost) {
// Qui va il tuo codice AJAX ......
}
})
Documentazione ufficiale dei dati dell'Editor di Post: https://wordpress.org/gutenberg/handbook/designers-developers/developers/data/data-core-editor/

sembra molto più pulito, sono solo curioso di sapere da dove viene il metodo subscribe
? fa parte della funzione wp.data
? Non lo vedo menzionato nella documentazione.

Sì, subscribe
è un metodo del modulo wp.data. Apri la console quando modifichi un post con Gutenberg ed esegui wp.data
. Questo elenca tutti i metodi disponibili del modulo dati.

ottimo lavoro nel trovarlo! è un peccato che la documentazione di Gutenberg sia organizzata in modo così oscuro e non abbia abbastanza esempi. Inoltre, l'aspettativa che gli sviluppatori conoscano e/o vogliano imparare i metodi React è davvero eccessiva... Sono sicuro che possa essere un grande risparmio di tempo se li conosci già, ma è una vera perdita di tempo se non li conosci - mi ci sono volute ore solo per capire come accedere a qualcosa di utile nel modello wp.data
. Per me si torna a PHP (e all'editor classico).

Grazie per aver condiviso questo! Come posso intercettare e interrompere l'aggiornamento/pubblicazione del post in base a una condizione.

Sembra che questo metodo attivi anche il codice quando un utente clicca sul pulsante "Sposta nel cestino" (lo stato del post cambia in "trash" e il valore isSavingPost è "true" indipendentemente da ciò). Inoltre, un singolo clic su "Aggiorna" ha attivato il codice di sottoscrizione 3 volte nel mio caso. Alla fine ho optato per ascoltare i clic su .editor-post-publish-button, .editor-post-save-draft e .editor-post-preview.

Questo snippet è ottimo ma verrà attivato più volte, il seguente thread spiega come evitare che ciò accada: https://github.com/WordPress/gutenberg/issues/5975#issuecomment-483488988

Ok, quindi una soluzione molto più "hacky" di quanto volessi, ma alla fine funziona...
Ecco un modo leggermente semplificato e astratto per farlo dal mio codice, nel caso qualcuno avesse bisogno di fare la stessa cosa (sono sicuro che altri plugin lo faranno nel prossimo futuro.)
var reload_check = false; var publish_button_click = false;
jQuery(document).ready(function($) {
add_publish_button_click = setInterval(function() {
$publish_button = jQuery('.edit-post-header__settings .editor-post-publish-button');
if ($publish_button && !publish_button_click) {
publish_button_click = true;
$publish_button.on('click', function() {
var reloader = setInterval(function() {
if (reload_check) {return;} else {reload_check = true;}
postsaving = wp.data.select('core/editor').isSavingPost();
autosaving = wp.data.select('core/editor').isAutosavingPost();
success = wp.data.select('core/editor').didPostSaveRequestSucceed();
console.log('Salvataggio in corso: '+postsaving+' - Autosalvataggio: '+autosaving+' - Successo: '+success);
if (postsaving || autosaving || !success) {classic_reload_check = false; return;}
clearInterval(reloader);
value = document.getElementById('metabox_input_id').value;
if (value == 'trigger_value') {
if (confirm('È necessario ricaricare la pagina. Ricaricare ora?')) {
window.location.href = window.location.href+'&refreshed=1';
}
}
}, 1000);
});
}
}, 500);
});
...basta cambiare metabox_input_id
e trigger_value
in base alle proprie esigenze. :-)

È stato utile, l'unico esempio di riferimento che ho trovato per accedere alla gerarchia dei moduli JavaScript di Gutenberg: https://github.com/front/gutenberg-js

Se qualcuno è ancora interessato, ho trovato un modo semplice per eseguire qualcosa subito DOPO che l'editor a blocchi (Gutenberg) completa la pubblicazione/aggiornamento di un articolo:
const editor = window.wp.data.dispatch('core/editor')
const savePost = editor.savePost
editor.savePost = function (options) {
options = options || {}
return savePost(options)
.then(() => {
// Fai qualcosa dopo che l'articolo è stato effettivamente salvato in modo asincrono.
console.log('L\'articolo è stato salvato.')
if (!options.isAutosave) {
// Questo non è un salvataggio automatico.
}
})
}
In sostanza, lo snippet sopra sovrascrive la funzione nativa savePost()
.
Quindi la si sovrascrive con una propria funzione, si richiama savePost()
al suo interno e si sfrutta la promise restituita semplicemente utilizzando then
.

Per attivare l'azione (in questo caso una richiesta Ajax) DOPO che il salvataggio del post è COMPLETATO, puoi utilizzare un intervallo per attendere finché isSavingPost non restituisce nuovamente false.
let intervalCheckPostIsSaved;
let ajaxRequest;
wp.data.subscribe(function () {
let editor = wp.data.select('core/editor');
if (editor.isSavingPost()
&& !editor.isAutosavingPost()
&& editor.didPostSaveRequestSucceed()) {
if (!intervalCheckPostIsSaved) {
intervalCheckPostIsSaved = setInterval(function () {
if (!wp.data.select('core/editor').isSavingPost()) {
if (ajaxRequest) {
ajaxRequest.abort();
}
ajaxRequest = $.ajax({
url: ajaxurl,
type: 'POST',
data: {},
success: function (data) {
ajaxRequest = null;
}
});
clearInterval(intervalCheckPostIsSaved);
intervalCheckPostIsSaved = null;
}
}, 800);
}
}
});

È necessario raccogliere la funzione unsubscribe dalla sottoscrizione e chiamarla per evitare chiamate multiple.
const unsubscribe = wp.data.subscribe(function () {
let select = wp.data.select('core/editor');
var isSavingPost = select.isSavingPost();
var isAutosavingPost = select.isAutosavingPost();
var didPostSaveRequestSucceed = select.didPostSaveRequestSucceed();
if (isSavingPost && !isAutosavingPost && didPostSaveRequestSucceed) {
console.log("isSavingPost && !isAutosavingPost && didPostSaveRequestSucceed");
unsubscribe();
// il tuo AJAX QUI();
}
});

Sembra funzionare bene, tranne per il fatto che viene ancora attivato PRIMA del salvataggio completo del post. Il mio codice inserito sotto "unsubscribe()" viene eseguito prima che WordPress avvii il salvataggio del post via AJAX. Esiste una soluzione per eseguire il codice solo dopo che WordPress ha completato al 100% il salvataggio/aggiornamento del post?

Posso confermare che viene attivato prima che il post finisca di salvarsi. Ho aggiunto una nuova risposta che penso potrebbe funzionare per attivare il codice solo dopo il completamento del salvataggio del post: https://wordpress.stackexchange.com/a/390543/171971

Ho scritto un articolo sul blog riguardo questo - https://thewpvoyage.com/how-to-detect-when-a-post-is-done-saving-in-wordpress-gutenberg/
Puoi utilizzare il seguente hook:
import { useBlockProps } from '@wordpress/block-editor';
import { useRef, useState, useEffect } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
/**
* Restituisce `true` se il post è stato salvato, `false` altrimenti.
*
* @returns {Boolean}
*/
const useAfterSave = () => {
const [ isPostSaved, setIsPostSaved ] = useState( false );
const isPostSavingInProgress = useRef( false );
const { isSavingPost, isAutosavingPost } = useSelect( ( __select ) => {
return {
isSavingPost: __select( 'core/editor' ).isSavingPost(),
isAutosavingPost: __select( 'core/editor' ).isAutosavingPost(),
}
} );
useEffect( () => {
if ( ( isSavingPost || isAutosavingPost ) && ! isPostSavingInProgress.current ) {
setIsPostSaved( false );
isPostSavingInProgress.current = true;
}
if ( ! ( isSavingPost || isAutosavingPost ) && isPostSavingInProgress.current ) {
// Codice da eseguire dopo che il post è stato salvato.
setIsPostSaved( true );
isPostSavingInProgress.current = false;
}
}, [ isSavingPost, isAutosavingPost ] );
return isPostSaved;
};
/**
* La funzione di modifica di un blocco di esempio.
*
* @return {WPElement} Elemento da renderizzare.
*/
export default function Edit() {
const isAfterSave = useAfterSave();
useEffect( () => {
if ( isAfterSave ) {
// Aggiungi qui il tuo codice che deve essere eseguito dopo il salvataggio del post.
console.log( '...salvataggio completato...' )
}
}, [ isAfterSave ] );
return (
<p { ...useBlockProps() }>
{ __( 'Lista delle cose da fare – ciao dall\'editor!', 'todo-list' ) }
</p>
);
}
