12

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?

6
  • 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
    Nov 13 '18 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
    Nov 13 '18 at 2:37
  • 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
    Nov 13 '18 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
    Nov 13 '18 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
    Nov 13 '18 at 7:14
12

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/

6
  • this looks much cleaner, just curious where does the subscribe method even come from? is it part of the wp.data function? I don't see it mentioned in the docs.
    – majick
    Mar 12 '19 at 13:50
  • Yes, subscribe is a method of the wp.data module. Open the console when editing a post with Gutenberg and run wp.data. This lists all available data module methods.
    – tomyam
    Mar 13 '19 at 8:37
  • 4
    well done on finding this! it's unfortunate the gutenberg docs are so obscurely laid out and don't have enough examples. plus the expectation that developers will know and/or want to learn React methods is really too much... I'm sure it can be quite a timesaver if you know it already, but it's a real timewaster if you don't - took me hours just to work out how to access anything useful in wp.data model. it's back to PHP (and classic editor) for me.
    – majick
    Mar 14 '19 at 9:30
  • Thanks for sharing this! How can I intercept and stop updating/publishing the post based on a condition. Dec 12 '19 at 1:27
  • 1
    It appears this method also triggers the code when a user clicks "Move to Trash" button (the post status changes to "trash" and isSavingPost value is "true" regardless of this). Also, one "Update" click triggered the subscription code 3 times in my case. I ended up listening for click on .editor-post-publish-button, .editor-post-save-draft and .editor-post-preview. Mar 6 '20 at 13:57
3

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. :-)

1
  • This was useful, the only reference example I could find to accessing the gutenberg javascript module hierarchy: github.com/front/gutenberg-js
    – majick
    Nov 13 '18 at 8:18
1

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

            }
        });
2
  • 1
    This seems to work fine, except that it still triggers BEFORE the complete post save. My code placed underneath "unsubscribe() fires before that WP launches the post save by AJAX. Is there a solution to run code after WP has 100% finish with post save/update ?
    – Neoweiter
    Jun 1 '20 at 19:21
  • I can confirm it triggers before the post finishes saving. I added a new answer that I think could work to trigger only after the post finished saving: wordpress.stackexchange.com/a/390543/171971 Jun 15 at 0:34
0

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.