Wordpress Gutenberg PluginDocumentSettingPanel 无法使用控件?

Wordpress Gutenberg PluginDocumentSettingPanel not working with controls?

我想将自定义元字段添加到 gutenberg 文档面板并使用了 this doc. For the custom meta field I used this tutorial。 我在尝试将它们放在一起时遇到的问题。

到目前为止,这是我的代码:

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

编辑:感谢 Ivan,我解决了这个问题 :)


我的边栏一开始看起来还不错:

但是当我尝试更改输入值时,它不会更新(但 wp.data 中的存储会更新)。我也删不掉它保持在它的初始值。当我删除我设置初始值的部分时,它会像它应该的那样工作,但因为我需要初始值,所以这对我来说不是一个选项;)

这是当我在输入末尾添加 "x" 时来自控制台的示例日志(如上所述,输入中的文本本身不会更改)

有谁知道如何使输入字段正常工作?

首先,请确保您有 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.

其次,您不需要 wp.plugins 的间隔函数。它最初未定义的原因是 WordPress 不知道您需要首先加载 wp-plugins。来自 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

这同样适用于所有其他模块(阅读脚本,如 'wp-plugins')。 在注册你的 js 插件脚本时,你必须添加 'wp-plugins' 脚本作为依赖:

<?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' );

上面的PHP取自official WP documentation.

我还建议您通读这个 awesome tutorial from Css Tricks. It goes in depth about setting up an ESNext environment with only the @wordpress/scripts 包。它遍历了依赖关系,添加了元字段等等:)希望这对您有所帮助!

----------------初始答案到此结束----------------

编辑: 测试作者的代码后,我发现了几个问题。首先,缺少 TextControl 的结束标记。其次,我从 wp-data 包中添加了高阶组件,然后我将其用于 "enhance" TextControl,这样它就不会直接操作或读取数据,而是将该逻辑抽象到它的 higher order components .代码如下所示:

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

由于meta字段是用下划线作为名字的第一个符号注册的,WordPress不允许你保存它,因为它把它当作一个私有值,所以你需要在注册时添加额外的代码领域:

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

同样,所有这些都在 Css tricks tutorial 中进行了解释。

我有同样的问题 - 现场和数据库中的值没有更新 - 经过一些研究,我发现原因是你应该将 'custom-fields' 添加到'supports' 数组在你对 register_post_type() 的调用中,像这样:

register_post_type(
    'my_post_type_slug',
    array(
        ...
       'supports' => array( 'title', 'editor', 'custom-fields' ),
        ...
    )
);

加载块编辑器后,您可以通过在 JavaScript 控制台中调用 wp.data.select( 'core/editor' ).getCurrentPost().meta 来测试它是否有效。如果您的 post 类型不添加对 'custom-fields' 的支持,此调用将 return undefined;如果是,它将 return 一个空数组(或者更确切地说,一个包含数据库中已经存在的元数据的数组)。在注册您的 post meta:

的注释中提到了这种行为 in the Gutenberg docs

To make sure the field has been loaded, query the block editor internal data structures, also known as stores. Open your browser’s console, and execute this piece of code:

wp.data.select( 'core/editor' ).getCurrentPost().meta;

Before adding the register_post_meta function to the plugin, this code returns a void array, because WordPress hasn’t been told to load any meta field yet. After registering the field, the same code will return an object containing the registered meta field you registered.

我最近在进行类似的实现,并且还研究了很多示例。在上述文章和 this great series by one of the Automattic devs 之间,我使用较新的 useSelectuseDispatch 自定义挂钩获得了上述示例的工作版本。它确实非常相似,但利用 React 16.8 中的自定义挂钩以获得更简洁的开发体验:

(另外,使用 @wordpress/scripts,因此导入是从 npm 包而不是直接从 wp 对象导入的,但两者都可以。)

import { __ } from '@wordpress/i18n';
import { useSelect, useDispatch } from '@wordpress/data';
import { PluginDocumentSettingPanel } from '@wordpress/edit-post';
import { TextControl } from '@wordpress/components';

const TextController = (props) => {
  const meta = useSelect(
    (select) =>
      select('core/editor').getEditedPostAttribute('meta')['_myprefix_text_metafield']
  );
  const { editPost } = useDispatch('core/editor');
  return (
    <TextControl
      label={__("Text Meta", "textdomain")}
      value={meta}
      onChange={(value) => editPost({ meta: { _myprefix_text_metafield: value } })}
    />
  );
};

const PluginDocumentSettingPanelDemo = () => {
  return (
    <PluginDocumentSettingPanel
      name="custom-panel"
      title="Custom Panel"
      className="custom-panel"
    >
      <TextController />
    </PluginDocumentSettingPanel>
  );
};

export default PluginDocumentSettingPanelDemo;

希望这能帮助其他人搜索。