I have created a custom banner image block for Gutenberg, which works great, but I want to know if it is possible to use the page title as the current banner text placeholder until it has been edited?
My Edit function is
return [
el('div', {className:'header-banner'},
el(
element.Fragment,
null,
controls,
el( "div",{
className: 'banner-image',
style: { backgroundImage: 'url('+attributes.mediaURL+')' }
},
attributes.title || isSelected ? el(RichText, {
key: 'editable',
tagName: "h1",
className: "banner-title",
//Can i add the page title in here if it is avaiable??
//placeholder: i18n.__('Write title…'),
value: attributes.title,
onChange: function onChange(value) {
return props.setAttributes({ title: value });
},
inlineToolbar: true
}) : null
)
)
)//header-banner
];
Thanks :)
Gutenberg stores the current editor state using wp.data, which is an abstraction over Redux. To get the title (or 100+ other values), we need to query the core/editor
data store. Gutenberg makes it simple to retrieve post attributes with getEditedPostAttribute.
Once we know where to look, getting the title is simple:
const { select } = wp.data;
const title = select("core/editor").getEditedPostAttribute( 'title' );
That works, but it's not responsive. When the post title changes, title
won't reflect the new value. That's kind of a let down.
To reflect changes to the editor title, we need to listen for changes to the core/editor
data store. There are a few ways to do this.
One solution is to define a simple change handler function and subscribe it to data store updates:
const { select } = wp.data;
function logTitle() {
const title = select("core/editor").getEditedPostAttribute( 'title' );
console.log("Editor Title:", title);
}
subscribe(logTitle);
That will fire when any wp.data
store value is updated -- which happens a lot.
What seems to be the Gutenberg-sanctioned way of including data-store values is to use a higher-order component to include the value directly:
const GetTitle = props => <div>{props.title}</div>;
const selectTitle = withSelect(select => ({
title: select("core/editor").getEditedPostAttribute( 'title' )
}));
const PostTitle = selectTitle(GetTitle);
Then in the block's output, include a <PostTitle />
jsx tag. That's a lot cleaner than nested callbacks or another change handler.
Higher-order components can be difficult to follow. The short explanation is that they wrap a existing component, generate some data, then return a copy of the component with the new data passed as props. This separates logic from presentation and helps with maintainability.
GetTitle
is simple enough, it's just a small component that takes in a props
object with a title key and spits out some html.
withSelect
is a function constructor or decorator. It accepts a function argument, and returns a new function which expects a component. Normally the returned function is invoked immediately (sort of an IIFE) but I stored it in the selectTitle
variable for clarity. The new function generates an object containing the title, this object will be passed as props to any components passed to withSelect
. Through some magic this will be called whenever the data store is updated.
In the end, PostTitle
contains the function result of selectTitle
which is a component pre-populated with the the generated props. This component can then be placed into our markup using a <PostTitle />
tag. Whenever the editor data-store is updated, the higher-level component will reflect the new data.
@joemaller thanks for the helpful response.
Here's an example that demonstrates using withSelect()
to wrap a component defined via the edit
property of the block configuration object passed to registerBlockType()
.
The component is passed the title
via props.
If the user edits the post/page title, the component is re-rendered with the new title because its props will have changed. This enables it to update in "real time".
import { withSelect } from '@wordpress/data'
...
edit: withSelect(
( select ) => {
return {
title: select( 'core/editor' ).getEditedPostAttribute( 'title' ),
}
} )( props => {
return (
<div>{ props.title }</div>
)
} ),
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With