获取 InnerBlocks 的属性并将它们保存到 parent
Getting attributes of InnerBlocks and saving them to parent
我创建了一个 "Tabbed Panels" (tabbed content) 块,它本质上只是一个仅允许块 "Panel" 的 InnerBlocks 组件。当您创建一个面板时,您必须给面板一个标题,然后在面板中使用该标题以及 Tab 按钮。因此,在选项卡式面板的渲染函数中,我需要从 children 面板块中提取标题。
我可以使用几种方法,比如在 tabbed-panels-render.php 函数中使用正则表达式来搜索 children html 的正确属性,但这似乎不是最好的方法。
我认为最简单的解决方案是监听面板块的任何更改并将更改(在本例中为标题和 ID)保存到 parent。我当前的方法基于 this discussion,它使用钩子来监听变化。该部分似乎工作正常,但我需要将输出保存在某处,因此我将它们作为属性保存到选项卡面板块。起初这似乎工作正常,但将 "setAttributes" 方法直接放在编辑功能中会导致问题。如果页面上有太多选项卡式面板块,则 React 会抛出 "too many renders" 错误。
我的 "setAttributes" 函数应该放在哪里,或者是否有更好的方法将数据从 child 传递到 parent?我想过在 child 中使用 useDispatch 钩子,但我需要检查很多事件(标题更改、块重新排序、块被删除等)
这里是相关的js和php文件。有一些自定义元素,但你应该可以解析它。
tabbed-panels.js
import { arraysMatch } from 'Components/utils.js'
const { InnerBlocks } = wp.blockEditor
const { createBlock } = wp.blocks
const { Button } = wp.components
const { useDispatch, useSelect } = wp.data
const { __ } = wp.i18n
export const tabbedPanels = {
name: 'my/tabbed-panels',
args: {
title: __('Tabbed Panels', '_ws'),
description: __('Tabbable panels of content.', '_ws'),
icon: 'table-row-after',
category: 'common',
supports: {
anchor: true
},
attributes: {
headings: {
type: 'array',
default: []
},
uids: {
type: 'array',
default: []
}
},
edit: props => {
const { setAttributes } = props
const { headings, uids } = props.attributes
const { insertBlock } = useDispatch('core/block-editor')
const { panelHeadings, panelUids, blockCount } = useSelect(select => {
const blocks = select('core/block-editor').getBlocks(props.clientId)
return {
panelHeadings: blocks.map(b => b.attributes.heading),
panelUids: blocks.map(b => b.clientId),
blockCount: select('core/block-editor').getBlockOrder(props.clientId).length
}
})
if (!arraysMatch(panelHeadings, headings)) {
setAttributes({ headings: panelHeadings })
}
if (!arraysMatch(panelUids, uids)) {
setAttributes({ uids: panelUids })
}
return (
<div className="block-row">
<InnerBlocks
allowedBlocks={ ['my/panel'] }
templateLock={ false }
renderAppender={ () => (
<Button
isSecondary
onClick={ e => {
insertBlock(createBlock('my/panel'), blockCount, props.clientId)
} }
>
{ __('Add Panel', '_ws') }
</Button>
) }
/>
</div>
)
},
save: props => {
return (
<InnerBlocks.Content />
)
}
}
}
tabbed-panels-render.php
<?php
function block_tabbed_panels($atts, $content) {
$atts['className'] = 'wp-block-ws-tabbed-panels ' . ($atts['className'] ?? '');
$headings = $atts['headings'] ?? '';
$uids = $atts['uids'] ?? '';
ob_start(); ?>
<div class="tabs" role="tablist">
<?php
foreach ($headings as $i=>$heading) : ?>
<button
id="tab-<?= $uids[$i]; ?>"
class="tab"
role="tab"
aria-selected="false"
aria-controls="panel-<?= $uids[$i]; ?>"
tabindex="-1"
>
<?= $heading; ?>
</button>
<?php
endforeach; ?>
</div>
<div class="panels">
<?= $content; ?>
</div>
<?php
return ob_get_clean();
}
panel.js
import ComponentHooks from 'Components/component-hooks.js'
const { InnerBlocks, RichText } = wp.blockEditor
const { __ } = wp.i18n
export const panel = {
name: 'my/panel',
args: {
title: __('Panel', '_ws'),
description: __('Panel with associated tab.', '_ws'),
icon: 'format-aside',
category: 'common',
supports: {
customClassName: false,
html: false,
inserter: false,
reusable: false
},
attributes: {
heading: {
type: 'string'
},
uid: {
type: 'string'
}
},
edit: props => {
const { setAttributes } = props
const { heading } = props.attributes
return (
<>
<ComponentHooks
componentDidMount={ () => setAttributes({ uid: props.clientId }) }
/>
<RichText
label={ __('Tab Name', '_ws') }
placeholder={ __('Tab Name', '_ws') }
tagName="h4"
onChange={ newValue => setAttributes({ heading: newValue }) }
value={ heading }
/>
<InnerBlocks
templateLock={ false }
/>
</>
)
},
save: props => {
return (
<InnerBlocks.Content />
)
}
}
}
panel-render.php
<?php
function block_panel($atts, $content) {
$uid = $atts['uid'] ?? '';
ob_start(); ?>
<div
id="panel-<?= $uid ?>"
class="panel"
role="tabpanel"
aria-labelledby="tab-<?= $uid; ?>"
tabindex="0"
hidden="hidden"
>
<?= $content; ?>
</div>
<?php
return ob_get_clean();
}
您可以从 parent 访问 children 块并获取属性(在您的情况下为选项卡标题)。
componentDidUpdate(previousProps, previousState) {
var myID = this.props.clientId;
var tabs_title = [];
this.myBlock = wp.data.select('core/block-editor').getBlock(myID);
this.myBlock.innerBlocks.map(block => {
tabs_title.push( block.attributes.title );
});
this.props.setAttributes({ 'tabs_title': tabs_title });
}
我创建了一个 "Tabbed Panels" (tabbed content) 块,它本质上只是一个仅允许块 "Panel" 的 InnerBlocks 组件。当您创建一个面板时,您必须给面板一个标题,然后在面板中使用该标题以及 Tab 按钮。因此,在选项卡式面板的渲染函数中,我需要从 children 面板块中提取标题。
我可以使用几种方法,比如在 tabbed-panels-render.php 函数中使用正则表达式来搜索 children html 的正确属性,但这似乎不是最好的方法。
我认为最简单的解决方案是监听面板块的任何更改并将更改(在本例中为标题和 ID)保存到 parent。我当前的方法基于 this discussion,它使用钩子来监听变化。该部分似乎工作正常,但我需要将输出保存在某处,因此我将它们作为属性保存到选项卡面板块。起初这似乎工作正常,但将 "setAttributes" 方法直接放在编辑功能中会导致问题。如果页面上有太多选项卡式面板块,则 React 会抛出 "too many renders" 错误。
我的 "setAttributes" 函数应该放在哪里,或者是否有更好的方法将数据从 child 传递到 parent?我想过在 child 中使用 useDispatch 钩子,但我需要检查很多事件(标题更改、块重新排序、块被删除等)
这里是相关的js和php文件。有一些自定义元素,但你应该可以解析它。
tabbed-panels.js
import { arraysMatch } from 'Components/utils.js'
const { InnerBlocks } = wp.blockEditor
const { createBlock } = wp.blocks
const { Button } = wp.components
const { useDispatch, useSelect } = wp.data
const { __ } = wp.i18n
export const tabbedPanels = {
name: 'my/tabbed-panels',
args: {
title: __('Tabbed Panels', '_ws'),
description: __('Tabbable panels of content.', '_ws'),
icon: 'table-row-after',
category: 'common',
supports: {
anchor: true
},
attributes: {
headings: {
type: 'array',
default: []
},
uids: {
type: 'array',
default: []
}
},
edit: props => {
const { setAttributes } = props
const { headings, uids } = props.attributes
const { insertBlock } = useDispatch('core/block-editor')
const { panelHeadings, panelUids, blockCount } = useSelect(select => {
const blocks = select('core/block-editor').getBlocks(props.clientId)
return {
panelHeadings: blocks.map(b => b.attributes.heading),
panelUids: blocks.map(b => b.clientId),
blockCount: select('core/block-editor').getBlockOrder(props.clientId).length
}
})
if (!arraysMatch(panelHeadings, headings)) {
setAttributes({ headings: panelHeadings })
}
if (!arraysMatch(panelUids, uids)) {
setAttributes({ uids: panelUids })
}
return (
<div className="block-row">
<InnerBlocks
allowedBlocks={ ['my/panel'] }
templateLock={ false }
renderAppender={ () => (
<Button
isSecondary
onClick={ e => {
insertBlock(createBlock('my/panel'), blockCount, props.clientId)
} }
>
{ __('Add Panel', '_ws') }
</Button>
) }
/>
</div>
)
},
save: props => {
return (
<InnerBlocks.Content />
)
}
}
}
tabbed-panels-render.php
<?php
function block_tabbed_panels($atts, $content) {
$atts['className'] = 'wp-block-ws-tabbed-panels ' . ($atts['className'] ?? '');
$headings = $atts['headings'] ?? '';
$uids = $atts['uids'] ?? '';
ob_start(); ?>
<div class="tabs" role="tablist">
<?php
foreach ($headings as $i=>$heading) : ?>
<button
id="tab-<?= $uids[$i]; ?>"
class="tab"
role="tab"
aria-selected="false"
aria-controls="panel-<?= $uids[$i]; ?>"
tabindex="-1"
>
<?= $heading; ?>
</button>
<?php
endforeach; ?>
</div>
<div class="panels">
<?= $content; ?>
</div>
<?php
return ob_get_clean();
}
panel.js
import ComponentHooks from 'Components/component-hooks.js'
const { InnerBlocks, RichText } = wp.blockEditor
const { __ } = wp.i18n
export const panel = {
name: 'my/panel',
args: {
title: __('Panel', '_ws'),
description: __('Panel with associated tab.', '_ws'),
icon: 'format-aside',
category: 'common',
supports: {
customClassName: false,
html: false,
inserter: false,
reusable: false
},
attributes: {
heading: {
type: 'string'
},
uid: {
type: 'string'
}
},
edit: props => {
const { setAttributes } = props
const { heading } = props.attributes
return (
<>
<ComponentHooks
componentDidMount={ () => setAttributes({ uid: props.clientId }) }
/>
<RichText
label={ __('Tab Name', '_ws') }
placeholder={ __('Tab Name', '_ws') }
tagName="h4"
onChange={ newValue => setAttributes({ heading: newValue }) }
value={ heading }
/>
<InnerBlocks
templateLock={ false }
/>
</>
)
},
save: props => {
return (
<InnerBlocks.Content />
)
}
}
}
panel-render.php
<?php
function block_panel($atts, $content) {
$uid = $atts['uid'] ?? '';
ob_start(); ?>
<div
id="panel-<?= $uid ?>"
class="panel"
role="tabpanel"
aria-labelledby="tab-<?= $uid; ?>"
tabindex="0"
hidden="hidden"
>
<?= $content; ?>
</div>
<?php
return ob_get_clean();
}
您可以从 parent 访问 children 块并获取属性(在您的情况下为选项卡标题)。
componentDidUpdate(previousProps, previousState) {
var myID = this.props.clientId;
var tabs_title = [];
this.myBlock = wp.data.select('core/block-editor').getBlock(myID);
this.myBlock.innerBlocks.map(block => {
tabs_title.push( block.attributes.title );
});
this.props.setAttributes({ 'tabs_title': tabs_title });
}