I want to add a custom meta field to the gutenberg document panel and used this doc. For the custom meta field I used this tutorial. The problem I have occurs when trying to put them together.
Here is my code so far:
const { __ } = wp.i18n;
const { registerBlockType } = wp.blocks;
const { InspectorControls } = wp.editor;
const { registerPlugin } = wp.plugins
const { PluginDocumentSettingPanel } = wp.editPost
const { PanelBody, PanelRow, TextControl } = wp.components
const PluginDocumentSettingPanelDemo = () => (
<PluginDocumentSettingPanel
name="custom-panel"
title="Custom Panel"
className="custom-panel"
>
<TextControl
value={wp.data.select('core/editor').getEditedPostAttribute('meta')['_myprefix_text_metafield']}
label={ "Text Meta" }
onChange={(value) => wp.data.dispatch('core/editor').editPost({meta: {_myprefix_text_metafield: value}})}
/>
</PluginDocumentSettingPanel>
)
registerPlugin('plugin-document-setting-panel-demo', {
render: PluginDocumentSettingPanelDemo
})
Edit: Thanks to Ivan I solved this side issue :)
My Sidebar looks okay at first:
But when I try to change the inputs value it isn't updated (but the storage in wp.data is). I can't delete it, too. It stays at it's initial value. When I remove the part where I set the initial value it works like it should be but since I need the initial value this isn't an option for me ;)
Here an example log from the console when I add an "x" to the end of the input (as mentioned above the text in the input itself doesn't change)
Does anyone know how to make the input field working properly?
First of all, make sure you have https://wordpress.org/plugins/gutenberg/ plugin installed, because PluginDocumentSettingPanel is not fully implemented in core WP yet. It should be for 5.3 version, as per these tweets.
Second, you don't need the interval function for the wp.plugins. The reason it is undefined at first is that WordPress doesn't know that you need the wp-plugins loaded first. From the WordPress documentation
If you wanted to use the PlainText component from the editor module, first you would specify wp-editor as a dependency when you enqueue your script
The same applies for all other modules (read scripts, like 'wp-plugins'). You have to add the 'wp-plugins' script as a dependency, when registering your js plugin script:
<?php
/*
Plugin Name: Sidebar plugin
*/
function sidebar_plugin_register() {
wp_register_script(
'plugin-sidebar-js',
plugins_url( 'plugin-sidebar.js', __FILE__ ),
array( 'wp-plugins', 'wp-edit-post', 'wp-element' ) // <== the dependencies array is important!
);
}
add_action( 'init', 'sidebar_plugin_register' );
function sidebar_plugin_script_enqueue() {
wp_enqueue_script( 'plugin-sidebar-js' );
}
add_action( 'enqueue_block_editor_assets', 'sidebar_plugin_script_enqueue' );
The PHP above is taken from the official WP documentation.
I would also suggest reading thoroughly this awesome tutorial from Css Tricks. It goes in depth about setting up an ESNext environment with only the @wordpress/scripts package. It goes over the dependencies, adding meta fields and much more :) I hope this helps!
--------------- Initial answer ends here ---------------
Edit: After testing the code from the author, I found out a couple of issues. First of all, there was a missing closing tag for the TextControl. Second, I added Higher order components from the wp-data package, which I then used to "enhance" the TextControl, so that it doesn't manipulate or read data directly, but abstract that logic into it's higher order components. The code looks like so:
const { __ } = wp.i18n;
const { registerPlugin } = wp.plugins;
const { PluginDocumentSettingPanel } = wp.editPost;
const { TextControl } = wp.components;
const { withSelect, withDispatch, dispatch, select } = wp.data;
// All the necessary code is pulled from the wp global variable,
// so you don't have to install anything
// import { withSelect, withDispatch, dispatch, select } from "@wordpress/data";
// !!! You should install all the packages locally,
// so your editor could access the files so you could
// look up the functions and classes directly.
// It will not add to the final bundle if you
// run it through wp-scripts. If not, you can
// still use the wp global variable, like you have done so far.
let TextController = props => (
<TextControl
value={props.text_metafield}
label={__("Text Meta", "textdomain")}
onChange={(value) => props.onMetaFieldChange(value)}
/>
);
TextController = withSelect(
(select) => {
return {
text_metafield: select('core/editor').getEditedPostAttribute('meta')['_myprefix_text_metafield']
}
}
)(TextController);
TextController = withDispatch(
(dispatch) => {
return {
onMetaFieldChange: (value) => {
dispatch('core/editor').editPost({ meta: { _myprefix_text_metafield: value } })
}
}
}
)(TextController);
const PluginDocumentSettingPanelDemo = () => {
// Check if a value has been set
// This is for editing a post, because you don't want to override it everytime
if (!select('core/editor').getEditedPostAttribute('meta')['_myprefix_text_metafield']) {
// Set initial value
dispatch('core/editor').editPost({ meta: { _myprefix_text_metafield: 'Your custom value' } });
}
return (
<PluginDocumentSettingPanel
name="custom-panel"
title="Custom Panel"
className="custom-panel"
>
<TextController />
</PluginDocumentSettingPanel>
)
};
registerPlugin('plugin-document-setting-panel-demo', {
render: PluginDocumentSettingPanelDemo
})
Since the meta field is registered with an underscore as a first symbol in the name, WordPress will not allow you to save it, because it treats it as a private value, so you need to add extra code, when registering the field:
function myprefix_register_meta()
{
register_post_meta('post', '_myprefix_text_metafield', array(
'show_in_rest' => true,
'type' => 'string',
'single' => true,
'sanitize_callback' => 'sanitize_text_field',
'auth_callback' => function () {
return current_user_can('edit_posts');
}
));
}
add_action('init', 'myprefix_register_meta');
Again, all of this is explained in the Css tricks tutorial.