admin管理员组文章数量:1289508
I've got a WP Block with a <ServerSideRender />
component in the backend. It works fine with the attributes. But it does not return the $content
used in the php callback for the frontend.
<ServerSideRender
block="mydomain/my-block"
attributes={ attributes }
/>
And I can't find in the documentation if there is a way to dynamicly render the block on the backend while using the $content.
The $content
is a nested block, so the save()
is used like:
save() {
return <InnerBlocks.Content />;
},
Does anyone know how to accomplice this?
--EDIT--
I've added my current edit()
code. Please note that not all is relevant.
The focus lays on:
- the use of
InnerBlocks
- the use of a preview button. one state is to edit the content and one state it to render a preview with the php callback.
- the use of
ServerSideRender
/**
* WordPress dependencies
*/
const { __ } = wp.i18n;
const {
InnerBlocks,
InspectorControls,
MediaUpload,
MediaUploadCheck,
BlockControls,
} = wp.blockEditor || wp.editor; // Fallback to 'wp.editor' for backwards compatibility
const {
PanelBody,
RangeControl,
Button,
SelectControl,
ToolbarGroup,
ToolbarButton,
ServerSideRender,
} = wpponents;
const { Component, Fragment } = wp.element;
const { withSelect, select } = wp.data;
const { compose } = wppose;
class BlockImageOverlayEdit extends Component {
render() {
const {
clientId,
attributes,
className,
setAttributes,
hasChildBlocks,
} = this.props;
const {
id,
preview,
editorSize,
bgColor,
mediaId,
mediaUrl,
centerContent,
mediaSpanCols,
mediaSpanOffset,
overlaySpanCols,
overlaySpanOffset,
alignX,
alignY,
} = attributes;
if ( id !== clientId ) {
setAttributes( { id: clientId } );
}
const colMaxAmount = 12;
const sizes = [ 'xs', 'sm', 'md', 'lg', 'xl', 'xxl' ];
const removeMedia = () => {
this.props.setAttributes( {
mediaId: 0,
mediaUrl: '',
} );
};
const onSelectMedia = ( media ) => {
setAttributes( {
mediaId: media.id,
mediaUrl: media.url,
} );
};
const onChangeColumns = ( value ) => {
const newMediaSpanOffset = (value + mediaSpanOffset) > colMaxAmount ? colMaxAmount - value : mediaSpanOffset;
setAttributes(
{
mediaSpanCols: value,
mediaSpanOffset: newMediaSpanOffset,
} );
};
const onChangeColumnsOverlay = ( value ) => {
const newOverlaySpanOffset = (value + overlaySpanOffset) > colMaxAmount ? colMaxAmount - value : overlaySpanOffset;
setAttributes(
{
overlaySpanCols: value,
overlaySpanOffset: newOverlaySpanOffset,
} );
};
const MyServerSideRender = () => {
// thought. maybe I can fetch, and prerender the inner blocks here, serialize them and set them as an attribute. Right before this return. (and at save() set it back to null)
return (
<ServerSideRender
block="mydomain/block-wp-block-image-overlay-01"
attributes={ attributes }
/>
);
};
const PreviewButton = () => (
<BlockControls>
<ToolbarGroup>
<ToolbarButton
icon={ preview ? 'hidden' : 'visibility' }
label="Preview"
onClick={ () => setAttributes( { preview: ! preview } ) }
/>
</ToolbarGroup>
</BlockControls>
);
const BreakpointButtons = () => (
<ToolbarGroup>
{ sizes.map( ( size, index ) => {
return (
<ToolbarButton
key={ index }
style={ { padding: 0.5 + 'em' } } //TODO: fix spacing
isPrimary={ editorSize === size }
isSecondary={ editorSize !== size }
// icon={ edit }
label="Edit"
onClick={ () => setAttributes( { editorSize: size } ) }
>{ size }</ToolbarButton>
);
} ) }
</ToolbarGroup>
);
const AlignPanel = () => (
<PanelBody
title={ __( 'Column size', 'mydomain' ) }
initialOpen={ false }
>
<SelectControl
label={ __( 'Horizontal align', 'mydomain' ) }
value={ alignX }
options={ [
{ value: 'start', label: __( 'Image on the right', 'mydomain' ) },
{ value: 'end', label: __( 'Image on the left', 'mydomain' ) },
] }
onChange={ ( value ) => setAttributes( { alignX: value } ) }
/>
<SelectControl
label={ __( 'Vertical align', 'mydomain' ) }
value={ alignY }
options={ [
{ value: 'start', label: __( 'Image on the top', 'mydomain' ) },
{ value: 'end', label: __( 'Image on the bottom', 'mydomain' ) },
] }
onChange={ ( value ) => setAttributes( { alignY: value } ) }
/>
</PanelBody>
);
const ColumnPanel = () => (
<PanelBody
title={ __( 'Column size', 'mydomain' ) }
initialOpen={ false }
>
<BreakpointButtons/>
<RangeControl
label={ __( 'Image columns', 'mydomain' ) }
value={ mediaSpanCols }
onChange={ onChangeColumns }
min={ 1 }
max={ colMaxAmount }
/>
<RangeControl
label={ __( 'Overlay columns', 'mydomain' ) }
value={ overlaySpanCols }
onChange={ onChangeColumnsOverlay }
min={ 1 }
max={ colMaxAmount }
/>
</PanelBody>
);
const Media = () => (
<Fragment>
<MediaUploadCheck>
<MediaUpload
onSelect={ onSelectMedia }
value={ mediaId }
allowedTypes={ [ 'image' ] }
render={ ( { open } ) => (
<Button
className={ mediaId === 0 ? 'editor-post-featured-image__toggle' : 'editor-post-featured-image__preview' }
onClick={ open }
>
{ mediaId === 0 && __( 'Choose an image', 'mydomain' ) }
{ mediaUrl !== undefined &&
<img alt={ '' } src={ mediaUrl }/>
}
</Button>
) }
/>
</MediaUploadCheck>
{
mediaId !== 0 &&
<MediaUploadCheck>
<MediaUpload
title={ __( 'Replace image', 'mydomain' ) }
value={ mediaId }
onSelect={ onSelectMedia }
allowedTypes={ [ 'image' ] }
render={ ( { open } ) => (
<Button
onClick={ open }
isDefault
isLarge>{ __( 'Replace image', 'mydomain' ) }
</Button>
) }
/>
</MediaUploadCheck>
}
{
mediaId !== 0 &&
<MediaUploadCheck>
<Button
onClick={ removeMedia }
isLink
isDestructive>{ __( 'Remove image', 'mydomain' ) }
</Button>
</MediaUploadCheck>
}
</Fragment>
);
const Overlay = () => (
<InnerBlocks
templateLock={ false }
renderAppender={
hasChildBlocks ?
undefined :
() => <InnerBlocks.ButtonBlockAppender/>
}
/>
)
return (
<Fragment>
<InspectorControls>
<AlignPanel/>
<ColumnPanel/>
</InspectorControls>
<PreviewButton/>
{ ! preview &&
<div className={ `${ className }` } style={ { display: 'flex' } }>
<div style={ { width: 50 + '%' } }>
<Media />
</div>
<div style={ { width: 50 + '%' } }>
<Overlay />
</div>
</div> }
{ preview &&
<MyServerSideRender /> }
</Fragment>
);
}
}
export default compose(
withSelect
( (
select
,
ownProps
) => {
const {
clientId
}
= ownProps;
const { getBlockOrder } =
select( 'core/block-editor' ) || select( 'core/editor' ); // Fallback to 'core/editor' for backwards compatibility
return {
hasChildBlocks: getBlockOrder( clientId ).length > 0,
};
} )
)
( BlockImageOverlayEdit );
I've got a WP Block with a <ServerSideRender />
component in the backend. It works fine with the attributes. But it does not return the $content
used in the php callback for the frontend.
<ServerSideRender
block="mydomain/my-block"
attributes={ attributes }
/>
And I can't find in the documentation if there is a way to dynamicly render the block on the backend while using the $content.
The $content
is a nested block, so the save()
is used like:
save() {
return <InnerBlocks.Content />;
},
Does anyone know how to accomplice this?
--EDIT--
I've added my current edit()
code. Please note that not all is relevant.
The focus lays on:
- the use of
InnerBlocks
- the use of a preview button. one state is to edit the content and one state it to render a preview with the php callback.
- the use of
ServerSideRender
/**
* WordPress dependencies
*/
const { __ } = wp.i18n;
const {
InnerBlocks,
InspectorControls,
MediaUpload,
MediaUploadCheck,
BlockControls,
} = wp.blockEditor || wp.editor; // Fallback to 'wp.editor' for backwards compatibility
const {
PanelBody,
RangeControl,
Button,
SelectControl,
ToolbarGroup,
ToolbarButton,
ServerSideRender,
} = wpponents;
const { Component, Fragment } = wp.element;
const { withSelect, select } = wp.data;
const { compose } = wppose;
class BlockImageOverlayEdit extends Component {
render() {
const {
clientId,
attributes,
className,
setAttributes,
hasChildBlocks,
} = this.props;
const {
id,
preview,
editorSize,
bgColor,
mediaId,
mediaUrl,
centerContent,
mediaSpanCols,
mediaSpanOffset,
overlaySpanCols,
overlaySpanOffset,
alignX,
alignY,
} = attributes;
if ( id !== clientId ) {
setAttributes( { id: clientId } );
}
const colMaxAmount = 12;
const sizes = [ 'xs', 'sm', 'md', 'lg', 'xl', 'xxl' ];
const removeMedia = () => {
this.props.setAttributes( {
mediaId: 0,
mediaUrl: '',
} );
};
const onSelectMedia = ( media ) => {
setAttributes( {
mediaId: media.id,
mediaUrl: media.url,
} );
};
const onChangeColumns = ( value ) => {
const newMediaSpanOffset = (value + mediaSpanOffset) > colMaxAmount ? colMaxAmount - value : mediaSpanOffset;
setAttributes(
{
mediaSpanCols: value,
mediaSpanOffset: newMediaSpanOffset,
} );
};
const onChangeColumnsOverlay = ( value ) => {
const newOverlaySpanOffset = (value + overlaySpanOffset) > colMaxAmount ? colMaxAmount - value : overlaySpanOffset;
setAttributes(
{
overlaySpanCols: value,
overlaySpanOffset: newOverlaySpanOffset,
} );
};
const MyServerSideRender = () => {
// thought. maybe I can fetch, and prerender the inner blocks here, serialize them and set them as an attribute. Right before this return. (and at save() set it back to null)
return (
<ServerSideRender
block="mydomain/block-wp-block-image-overlay-01"
attributes={ attributes }
/>
);
};
const PreviewButton = () => (
<BlockControls>
<ToolbarGroup>
<ToolbarButton
icon={ preview ? 'hidden' : 'visibility' }
label="Preview"
onClick={ () => setAttributes( { preview: ! preview } ) }
/>
</ToolbarGroup>
</BlockControls>
);
const BreakpointButtons = () => (
<ToolbarGroup>
{ sizes.map( ( size, index ) => {
return (
<ToolbarButton
key={ index }
style={ { padding: 0.5 + 'em' } } //TODO: fix spacing
isPrimary={ editorSize === size }
isSecondary={ editorSize !== size }
// icon={ edit }
label="Edit"
onClick={ () => setAttributes( { editorSize: size } ) }
>{ size }</ToolbarButton>
);
} ) }
</ToolbarGroup>
);
const AlignPanel = () => (
<PanelBody
title={ __( 'Column size', 'mydomain' ) }
initialOpen={ false }
>
<SelectControl
label={ __( 'Horizontal align', 'mydomain' ) }
value={ alignX }
options={ [
{ value: 'start', label: __( 'Image on the right', 'mydomain' ) },
{ value: 'end', label: __( 'Image on the left', 'mydomain' ) },
] }
onChange={ ( value ) => setAttributes( { alignX: value } ) }
/>
<SelectControl
label={ __( 'Vertical align', 'mydomain' ) }
value={ alignY }
options={ [
{ value: 'start', label: __( 'Image on the top', 'mydomain' ) },
{ value: 'end', label: __( 'Image on the bottom', 'mydomain' ) },
] }
onChange={ ( value ) => setAttributes( { alignY: value } ) }
/>
</PanelBody>
);
const ColumnPanel = () => (
<PanelBody
title={ __( 'Column size', 'mydomain' ) }
initialOpen={ false }
>
<BreakpointButtons/>
<RangeControl
label={ __( 'Image columns', 'mydomain' ) }
value={ mediaSpanCols }
onChange={ onChangeColumns }
min={ 1 }
max={ colMaxAmount }
/>
<RangeControl
label={ __( 'Overlay columns', 'mydomain' ) }
value={ overlaySpanCols }
onChange={ onChangeColumnsOverlay }
min={ 1 }
max={ colMaxAmount }
/>
</PanelBody>
);
const Media = () => (
<Fragment>
<MediaUploadCheck>
<MediaUpload
onSelect={ onSelectMedia }
value={ mediaId }
allowedTypes={ [ 'image' ] }
render={ ( { open } ) => (
<Button
className={ mediaId === 0 ? 'editor-post-featured-image__toggle' : 'editor-post-featured-image__preview' }
onClick={ open }
>
{ mediaId === 0 && __( 'Choose an image', 'mydomain' ) }
{ mediaUrl !== undefined &&
<img alt={ '' } src={ mediaUrl }/>
}
</Button>
) }
/>
</MediaUploadCheck>
{
mediaId !== 0 &&
<MediaUploadCheck>
<MediaUpload
title={ __( 'Replace image', 'mydomain' ) }
value={ mediaId }
onSelect={ onSelectMedia }
allowedTypes={ [ 'image' ] }
render={ ( { open } ) => (
<Button
onClick={ open }
isDefault
isLarge>{ __( 'Replace image', 'mydomain' ) }
</Button>
) }
/>
</MediaUploadCheck>
}
{
mediaId !== 0 &&
<MediaUploadCheck>
<Button
onClick={ removeMedia }
isLink
isDestructive>{ __( 'Remove image', 'mydomain' ) }
</Button>
</MediaUploadCheck>
}
</Fragment>
);
const Overlay = () => (
<InnerBlocks
templateLock={ false }
renderAppender={
hasChildBlocks ?
undefined :
() => <InnerBlocks.ButtonBlockAppender/>
}
/>
)
return (
<Fragment>
<InspectorControls>
<AlignPanel/>
<ColumnPanel/>
</InspectorControls>
<PreviewButton/>
{ ! preview &&
<div className={ `${ className }` } style={ { display: 'flex' } }>
<div style={ { width: 50 + '%' } }>
<Media />
</div>
<div style={ { width: 50 + '%' } }>
<Overlay />
</div>
</div> }
{ preview &&
<MyServerSideRender /> }
</Fragment>
);
}
}
export default compose(
withSelect
( (
select
,
ownProps
) => {
const {
clientId
}
= ownProps;
const { getBlockOrder } =
select( 'core/block-editor' ) || select( 'core/editor' ); // Fallback to 'core/editor' for backwards compatibility
return {
hasChildBlocks: getBlockOrder( clientId ).length > 0,
};
} )
)
( BlockImageOverlayEdit );
Share
Improve this question
edited Mar 31, 2021 at 6:14
Tim
asked Mar 30, 2021 at 6:57
TimTim
1671 silver badge7 bronze badges
3
|
1 Answer
Reset to default 5ServerSideRender
uses the Block Renderer API endpoint (/wp/v2/block-renderer/<name>
) which does not parse inner blocks, hence the $content
in your block render callback would naturally be empty.
However, retrieving the inner blocks is pretty easy and as you've thought, you can set the blocks (or their content) as an attribute that you would pass to ServerSideRender
.
But you don't need to save the attribute (using setAttributes()
), so it will act just as a "dummy" attribute that you would use with ServerSideRender
so that the block render callback would receive the inner blocks.
Working Example
So I'm using
innerContent
as the name for the dummy attribute, but just use any name that you like, and make sure to register the attribute in PHP. E.g.register_block_type( 'mydomain/block-wp-block-image-overlay-01', array( 'render_callback' => 'my_block_render_callback', 'attributes' => array( // the dummy attribute 'innerContent' => array( 'type' => 'string', 'default' => '', ), // your other attributes 'editorSize' => array( 'type' => 'string', 'default' => 'md', ), // ... ), // ... ) );
But in JS, you don't need to actually add the attribute in the
attributes
property of your block type (that's registered usingregisterBlockType()
).Then in your
MyServerSideRender
, retrieve the content of the inner blocks and include the content in theattributes
property of theServerSideRender
element:Note: I'm using POST as the
httpMethod
because that method will allow a bigger attributes object.// ... your code here. const { compose } = wppose; // 1. After the above line, add the following: // These are used to retrieve the inner block content. const { getBlock } = select( 'core/block-editor' ); const { getBlockContent } = wp.blocks; // 2. Then define MyServerSideRender like so: const MyServerSideRender = () => { // Retrieve the inner block content. const innerContent = getBlockContent( getBlock( clientId ) ); // Then append the innerContent to the attributes object below. return ( <ServerSideRender block="mydomain/block-wp-block-image-overlay-01" attributes={ { ...attributes, innerContent } } httpMethod="POST" /> ); };
And then in the block render callback, you would use the dummy (
innerContent
) attribute like so:function my_block_render_callback( $attributes, $content ) { // On the front-end (or non REST API requests), we use $content. if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { return '<h3>MyServerSideRender output</h3>' . $attributes['innerContent']; } return $content; /* Or you could instead just do: return $content ? $content : $attributes['innerContent']; */ }
So try those and let me know how it goes? =)
本文标签: WP BlocksGutenbergltServerSideRendergt not rendering content
版权声明:本文标题:WP Blocks - Gutenberg - <ServerSideRender> not rendering $content 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741467853a2380418.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
ServerSideRender
uses the Block Renderer API endpoint which does not parse inner blocks, hence the$content
would be empty. But why are you usingServerSideRender
? Can we see youredit()
function? – Sally CJ Commented Mar 30, 2021 at 23:05edit()
as a string/html ? Than I could set it as a block attribute which can be send to theServerSideRender
and use it there. Afterwards I could reset the attribute to empty atsave()
. Maybe a hacky workaround : ) Yes, in theory I could do all inedit()
But I now have two columns in the edit, one for an image and one for innerblocks. They calculate an overlay based on 5 attibutes and breakpoints. It would seem overkill to programme it twice. (frontend/backend) – Tim Commented Mar 31, 2021 at 6:02