概述

如何为您的插件添加侧边栏。侧边栏是编辑器最右侧的区域。您的插件可以在 InspectorControls(齿轮图标)旁边添加一个可展开的额外图标。

示例侧边栏

注意:本教程涵盖自定义侧边栏,如果您想向侧边栏添加控件,请参阅 编辑器中的块工具栏和设置侧边栏

开始之前

本教程假设您已有一个现有的插件设置,并准备好添加 PHP 和 JavaScript 代码。请参阅 JavaScript 入门 教程,了解 WordPress 插件的介绍以及如何使用 JavaScript 扩展块编辑器。

分步指南

步骤 1:启动侧边栏

第一步是告知编辑器有一个新插件将拥有自己的侧边栏。使用 @wordpress/plugins、@wordpress/editor 和 react 包分别提供的 registerPlugin、PluginSidebar 和 createElement 工具。

将以下代码添加到名为 plugin-sidebar.js 的 JavaScript 文件中,并将其保存在插件目录内:

( function ( wp, React ) {
    var el = React.createElement;
    var registerPlugin = wp.plugins.registerPlugin;
    var PluginSidebar = wp.editor.PluginSidebar;

    registerPlugin( 'my-plugin-sidebar', {
        render: function () {
            return el(
                PluginSidebar,
                {
                    name: 'my-plugin-sidebar',
                    icon: 'admin-post',
                    title: 'My plugin sidebar',
                },
                'Meta field'
            );
        },
    } );
} )( window.wp, window.React );

为了使此代码正常工作,这些工具必须在浏览器中可用,因此您必须将 wp-plugins、wp-editor 和 react 指定为脚本的依赖项。

以下是注册您的脚本并指定依赖项的 PHP 代码:

<?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-editor', 'react' )
    );
}
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' );

安装并激活此插件后,编辑器右上角会出现一个类似图钉的新图标。单击它时,将打开插件的侧边栏:

侧边栏已启动

步骤 2:调整侧边栏样式并添加控件

在侧边栏启动后,下一步是填充必要的组件和基本样式。

为了可视化和编辑元字段值,您将使用输入组件。@wordpress/components 包包含许多可供您重用的组件,特别是 TextControl 旨在创建输入字段:

( function ( wp ) {
    var el = React.createElement;
    var registerPlugin = wp.plugins.registerPlugin;
    var PluginSidebar = wp.editor.PluginSidebar;
    var TextControl = wp.components.TextControl;

    registerPlugin( 'my-plugin-sidebar', {
        render: function () {
            return el(
                PluginSidebar,
                {
                    name: 'my-plugin-sidebar',
                    icon: 'admin-post',
                    title: 'My plugin sidebar',
                },
                el(
                    'div',
                    { className: 'plugin-sidebar-content' },
                    el( TextControl, {
                        label: 'Meta Block Field',
                        value: 'Initial value',
                        onChange: function ( content ) {
                            console.log( 'content changed to ', content );
                        },
                    } )
                )
            );
        },
    } );
} )( window.wp );

使用此新代码更新 plugin-sidebar.js。请注意,它使用了来自 @wordpress/components 包的新工具 wp.components。确保在 PHP 文件中的 wp_register_script 函数中将 wp-components 添加到依赖项中。

代码引入了:

  • 一个 CSS 类 plugin-sidebar-content 应用于 div 元素以针对样式,
  • 使用 TextControl 组件而不是纯文本 'Meta field'。

有了新的 CSS 类,您可以添加一些样式。在插件目录中创建一个名为 plugin-sidebar.css 的新文件,并添加以下内容以提供内边距:

.plugin-sidebar-content {
    padding: 16px;
}

注册脚本并将其与 JavaScript 文件一起通过 enqueue_block_editor_assets 加载。

进行这些更改后,PHP 代码将如下所示:

<?php

/*
Plugin Name: Sidebar example
*/

function sidebar_plugin_register() {
    wp_register_script(
        'plugin-sidebar-js',
        plugins_url( 'plugin-sidebar.js', __FILE__ ),
        array(
            'react',
            'wp-plugins',
            'wp-editor',
            'wp-components'
        )
    );
    wp_register_style(
        'plugin-sidebar-css',
        plugins_url( 'plugin-sidebar.css', __FILE__ )
    );
}
add_action( 'init', 'sidebar_plugin_register' );

function sidebar_plugin_script_enqueue() {
    wp_enqueue_script( 'plugin-sidebar-js' );
    wp_enqueue_style( 'plugin-sidebar-css' );
}
add_action( 'enqueue_block_editor_assets', 'sidebar_plugin_script_enqueue' );

重新加载编辑器并打开侧边栏:

带样式和控件的侧边栏

此代码尚不允许用户存储或检索数据,因此接下来的步骤将专注于如何将其连接到元块字段。

步骤 3:注册元字段

要处理 post_meta 表中的字段,请使用 register_post_meta 函数创建一个名为 sidebar_plugin_meta_block_field 的新字段。

注意:此字段需要向 REST API 可用,因为块编辑器通过该方式访问数据。

在插件的 init 回调函数中添加 PHP 代码:

register_post_meta( 'post', 'sidebar_plugin_meta_block_field', array(
    'show_in_rest' => true,
    'single' => true,
    'type' => 'string',
) );

为了确认,查询块编辑器存储以查看字段已加载。实施后,重新加载编辑器页面并打开浏览器的开发者控制台。在控制台中使用此 JavaScript 代码片段进行确认:

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

该函数将返回一个包含您注册的元字段的对象。

如果代码返回 undefined,请确保您的帖子类型支持 custom-fields。要么在 注册帖子 时,要么使用 add_post_type_support 函数。

步骤 4:初始化输入控件

由于字段在编辑器存储中可用,现在可以将其呈现到 UI。我们将输入控件提取到一个函数中,以便随着功能的添加保持代码整洁。

( function ( wp ) {
    var el = React.createElement;
    var registerPlugin = wp.plugins.registerPlugin;
    var PluginSidebar = wp.editor.PluginSidebar;
    var TextControl = wp.components.TextControl;

    var MetaBlockField = function () {
        return el( TextControl, {
            label: 'Meta Block Field',
            value: 'Initial value',
            onChange: function ( content ) {
                console.log( 'content changed to ', content );
            },
        } );
    };

    registerPlugin( 'my-plugin-sidebar', {
        render: function () {
            return el(
                PluginSidebar,
                {
                    name: 'my-plugin-sidebar',
                    icon: 'admin-post',
                    title: 'My plugin sidebar',
                },
                el(
                    'div',
                    { className: 'plugin-sidebar-content' },
                    el( MetaBlockField )
                )
            );
        },
    } );
} )( window.wp );

我们希望在 MetaBlockField 组件中初始化值为 sidebar_plugin_meta_block_field 的值,并在该值变化时保持更新。

useSelect 函数用于在组件加载时获取数据,并且当数据变化时会进行更新。以下是使用 useSelect 的代码更新:

( function ( wp ) {
    var el = React.createElement;
    var registerPlugin = wp.plugins.registerPlugin;
    var PluginSidebar = wp.editor.PluginSidebar;
    var Text = wp.components.TextControl;
    var useSelect = wp.data.useSelect;

    var MetaBlockField = function () {
        var metaFieldValue = useSelect( function ( select ) {
            return select( 'core/editor' ).getEditedPostAttribute(
                'meta'
            )[ 'sidebar_plugin_meta_block_field' ];
        }, [] );

        return el( Text, {
            label: 'Meta Block Field',
            value: metaFieldValue,
            onChange: function ( content ) {
                console.log( 'content has changed to ', content );
            },
        } );
    };

    registerPlugin( 'my-plugin-sidebar', {
        render: function () {
            return el(
                PluginSidebar,
                {
                    name: 'my-plugin-sidebar',
                    icon: 'admin-post',
                    title: 'My plugin sidebar',
                },
                el(
                    'div',
                    { className: 'plugin-sidebar-content' },
                    el( MetaBlockField )
                )
            );
        },
    } );
} )( window.wp );

wp.data.useSelect 函数来自 @wordpress/data 包,因此需要在 PHP 的 wp_register_script 函数中添加 wp-data 作为依赖项。

注意:getEditedPostAttribute 调用用于检索帖子的最新值,包括尚未保存的用户编辑内容。

通过更新代码、重新加载并打开侧边栏来确认其是否正常工作。输入的内容不再是 Initial value,而是一个空字符串。用户尚无法输入值,但您可以检查如果存储中的值发生变化,组件是否会更新。打开浏览器控制台,执行

wp.data
    .dispatch( 'core/editor' )
    .editPost( { meta: { sidebar_plugin_meta_block_field: 'hello world!' } } );

您可以观察到输入组件中的内容发生变化。

步骤 5:当输入内容变化时更新元字段

最后一步是当输入内容变化时更新元字段。
useDispatch 函数仅接受一个存储名称作为参数,并返回可用于更新存储的方法,在本例中我们将使用 editPost

( function ( wp ) {
    var el = React.createElement;
    var registerPlugin = wp.plugins.registerPlugin;
    var PluginSidebar = wp.editor.PluginSidebar;
    var TextControl = wp.components.TextControl;
    var useSelect = wp.data.useSelect;
    var useDispatch = wp.data.useDispatch;

    var MetaBlockField = function ( props ) {
        var metaFieldValue = useSelect( function ( select ) {
            return select( 'core/editor' ).getEditedPostAttribute(
                'meta'
            )[ 'sidebar_plugin_meta_block_field' ];
        }, [] );

        var editPost = useDispatch( 'core/editor' ).editPost;

        return el( TextControl, {
            label: 'Meta Block Field',
            value: metaFieldValue,
            onChange: function ( content ) {
                editPost( {
                    meta: { sidebar_plugin_meta_block_field: content },
                } );
            },
        } );
    };

    registerPlugin( 'my-plugin-sidebar', {
        render: function () {
            return el(
                PluginSidebar,
                {
                    name: 'my-plugin-sidebar',
                    icon: 'admin-post',
                    title: 'My plugin sidebar',
                },
                el(
                    'div',
                    { className: 'plugin-sidebar-content' },
                    el( MetaBlockField )
                )
            );
        },
    } );
} )( window.wp );

更新后,当用户输入时,输入控件会调用 editPost 并在每次按键时更新编辑器存储。

更新 JavaScript,加载侧边栏,并在输入字段中输入内容。您可以通过在输入控件中输入内容并在浏览器的开发控制台执行 JavaScript 代码片段来确认已保存:

wp.data.select( 'core/editor' ).getEditedPostAttribute( 'meta' )[
    'sidebar_plugin_meta_block_field'
];

显示的消息应该是您在输入中键入的内容。

保存帖子时,可以通过重新加载并确认输入控件已用您键入的最后一个值初始化来确认其是否正确存储在数据库中。

其他资源

有关使用 @wordpress/data 包 的文档。

本指南中使用的函数:

结论

现在您拥有了一个自定义侧边栏,可用于更新 post_meta 内容。

完整的示例可用,请从 block-development-examples 仓库下载 plugin-sidebar 示例。

注意

如果您在编辑器“偏好设置”的“面板”页面(通过右上角的三个点)中启用了自定义字段,则会出现一个与 TextControl 名称相同的字段,在本例中为 sidebar_plugin_meta_block_field,该字段也会出现在编辑器窗口底部的自定义字段面板中。这两个字段可以访问相同的元属性。

Text Control and Custom Field

保存帖子时,TextControl 中的值将首先保存,然后自定义字段中的值将第二次保存,因此最终持久化在数据库中的是后者。因此,如果您更改 TextControl 中的值,最终保存到数据库的仍然是自定义字段中的值。

如果未启用自定义字段,则不存在此问题。

如果您需要启用自定义字段并在侧边栏中拥有帖子元数据,有两种可能的解决方案:

  1. 在元字段的名称前加上下划线,因此上述示例中的名称应为 _sidebar_plugin_meta_block_field。这表明该帖子元数据应被视为私有,因此在帖子的自定义字段部分不可见。使用此解决方案时,除非您向传递给 register_post_meta 的 args 数组添加一个 auth_callback 属性(该属性包含一个最终返回 true 的函数),否则在保存帖子时会生成错误。有关更多信息,请参阅 post_meta 页面中的 args 文档。
  2. 在 TextControl 的 onChange 函数中,将目标值字段文本域并设置该处的值与 TextControl 元字段中的值相同。这样两个地方的值将完全相同,因此您可以确信如果 TextControl 中的值发生变化,它仍然会保存到数据库中。
return el( TextControl, {
  label: 'Meta Block Field',
  value: metaFieldValue,
  onChange: function ( content ) {
    editPost( {
      meta: { sidebar_plugin_meta_block_field: content }
    })
    document.querySelector( {the-value-textarea} ).innerHTML = content;
  },
} );