admin管理员组

文章数量:1122846

So I have a metabox which I want to trigger some Javascript when a post is saved (to refresh the page in this use case.)

In Classic Editor, this can be done via a simple redirect hooked to save_post (with a high priority)

But since Gutenberg converts the saving process for existing metaboxes into individual AJAX calls now, it needs to be javascript, so how do I either:

  • Listen for an event where all the saving processes are complete and then trigger the javascript? If so what is this event called? Is there a reference to these events anywhere yet? OR

  • Trigger javascript inside the metabox saving AJAX process, which can then check the state of the parent page saving process before continuing?

So I have a metabox which I want to trigger some Javascript when a post is saved (to refresh the page in this use case.)

In Classic Editor, this can be done via a simple redirect hooked to save_post (with a high priority)

But since Gutenberg converts the saving process for existing metaboxes into individual AJAX calls now, it needs to be javascript, so how do I either:

  • Listen for an event where all the saving processes are complete and then trigger the javascript? If so what is this event called? Is there a reference to these events anywhere yet? OR

  • Trigger javascript inside the metabox saving AJAX process, which can then check the state of the parent page saving process before continuing?

Share Improve this question edited Nov 13, 2018 at 14:06 fuxia 107k38 gold badges255 silver badges459 bronze badges asked Nov 13, 2018 at 1:01 majickmajick 5,1412 gold badges18 silver badges30 bronze badges 7
  • 1 Making Gutenberg reload just your metabox is also a potential solution, as would implementing the metabox UI in JS and relying on wp.data data stores – Tom J Nowell Commented Nov 13, 2018 at 2:07
  • @TomJNowell I have found this reference I can use to check some states, but not sure how to access them: wordpress.org/gutenberg/handbook/data/data-core-editor – majick Commented Nov 13, 2018 at 2:37
  • 1 so far I have: eg. wp.data.select('core/editor').isSavingPost() ... this kind of access is not documented anywhere I can see... and it seems also unreliable as it returns false after the post is first saved (before that it is undefined) whether the editor is still saving or not. facepalm – majick Commented Nov 13, 2018 at 5:50
  • You can raise an issue on the gutenberg repo for support too, it's on topic here but you might get more knowledgable people answering there. Also the JS WP hooks system might be a possibility but that's just a guess – Tom J Nowell Commented Nov 13, 2018 at 7:02
  • Crazily, something simple like this is already asked for and not supported: github.com/WordPress/gutenberg/issues/10044 ... hence I am trying to find a way to do it myself. – majick Commented Nov 13, 2018 at 7:14
 |  Show 2 more comments

6 Answers 6

Reset to default 16

Not sure if there is a better way, but I am listening to subscribe rather than adding an event listener to the button:

wp.data.subscribe(function () {
  var isSavingPost = wp.data.select('core/editor').isSavingPost();
  var isAutosavingPost = wp.data.select('core/editor').isAutosavingPost();

  if (isSavingPost && !isAutosavingPost) {
    // Here goes your AJAX code ......

  }
})

Official docs of the Post Editor data: https://wordpress.org/gutenberg/handbook/designers-developers/developers/data/data-core-editor/

Okay, so way way more hacky solution than I wanted, but got it working...

Here is a slightly simplified and abstracted way of doing it from my code, in case anyone ever needs to do the same (as I'm sure more plugins will in the near future.)

    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('Saving: '+postsaving+' - Autosaving: '+autosaving+' - Success: '+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('Page reload required. Refresh the page now?')) {
                                window.location.href = window.location.href+'&refreshed=1';
                            }
                        }
                    }, 1000);
                });
            }
        }, 500);
    });

...just need to change metabox_input_id and trigger_value to match as needed. :-)

In order to trigger the action (in this case an Ajax request) AFTER the post save is COMPLETE, you can use an interval to wait until the isSavingPost returns false again.

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

If someone is still interested I came up with a simple way to actually execute something right AFTER the block editor (Gutenberg) finishes a post publish/update:

const editor = window.wp.data.dispatch('core/editor')
const savePost = editor.savePost

editor.savePost = function (options) {
    options = options || {}

    return savePost(options)
        .then(() => {
            // Do something after the post was actually asynchronous saved.
            console.log('The post was saved.')

            if (!options.isAutosave) {
               // This is not an autosave.
            }
        })
}

Basically the snippet above overrides the native savePost() function.
So you override it with your own function, call savePost() again inside it and take advantage of the promise returned just by using then.

You need collect unsubscribe function from subscribe and call to avoid multiples time call.

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


                // your AJAX HERE();

            }
        });

I wrote a blog post about this - https://thewpvoyage.com/how-to-detect-when-a-post-is-done-saving-in-wordpress-gutenberg/

You can use the following hook:

import { useBlockProps } from '@wordpress/block-editor';
import { useRef, useState, useEffect } from '@wordpress/element';
import { useSelect } from '@wordpress/data';

/**
 * Returns `true` if the post is done saving, `false` otherwise.
 *
 * @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 ) {
            // Code to run after post is done saving.
            setIsPostSaved( true );
            isPostSavingInProgress.current = false;
        }
    }, [ isSavingPost, isAutosavingPost ] );

    return isPostSaved;
};

/**
 * The edit function of an example block.
 *
 * @return {WPElement} Element to render.
 */
export default function Edit() {
    const isAfterSave = useAfterSave();

    useEffect( () => {
        if ( isAfterSave ) {
            // Add your code here that must run after the post is saved.
            console.log( '...done saving...' )
        }
    }, [ isAfterSave ] );

    return (
        <p { ...useBlockProps() }>
            { __( 'Todo List – hello from the editor!', 'todo-list' ) }
        </p>
    );
}

本文标签: metaboxTrigger Javascript on Gutenberg (Block Editor) Save