This is my first Wordpreess Gutenberg block using the <img>
tag. I can't seem to get the image to display. The text renders but it seems the image src doesn't work. When I open up the DOM and inspect elements the <img>
tag is in the DOM tree but there is no src it's just empty. I've tried putting the <Media Upload>
both inline and in the <Panel Body>
but either way the <img src>
is not taking. I'm certain I'm overlooking something but after much trial and error, I cannot seem to connect the <Media Upload>
to the image source properly.
const { registerBlockType } = wp.blocks;
const {
RichText,
InspectorControls,
ColorPalette,
MediaUpload,
MediaUploadCheck,
Button,
RawHTML,
InnerBlocks
} = wp.editor;
const { PanelBody, IconButton } = wp.components;
const ALLOWED_BLOCKS = ['core/button', 'core/html', 'core/image']
registerBlockType('mycustomblock/feature-block', {
//Built-in Attributes
title: 'Feature Block',
description: 'Block Description',
icon: 'align-pull-left',
category: 'design',
//Custom Attributes
attributes: {
title: {
type: 'string',
source: 'html',
selector: 'p'
},
titleColor: {
type: 'string',
default: 'black'
},
bodyColor: {
type: 'string',
default: 'black'
},
image: {
type: 'object',
source:'html',
selector:'feature-icon'
},
body: {
type: 'string',
source: 'html',
selector: 'p'
}
},
//Built-in Functions
edit({attributes, setAttributes}) {
const{
title,
body,
titleColor,
bodyColor,
image,
} = attributes;
//Custom Functions
function onChangeTitle(newTitle) {
setAttributes( { title: newTitle } );
}
function onChangeBody(newBody) {
setAttributes( { body: newBody } );
}
function onTitleColorChange(newColor){
setAttributes( { titleColor: newColor } );
}
function onBodyColorChange(newBodyColor){
setAttributes( { bodyColor: newBodyColor } );
}
function onSelectImage(newImage) {
setAttributes( { image: newImage.sizes.full.url } )
}
return ([
<InspectorControls style={ { marginBottom: '40px' } }>
{/* <PanelBody title={ 'Image Settings' }>
</PanelBody> */}
<PanelBody title={ 'Headline Color' }>
<p><strong>Choose Title Color</strong></p>
<ColorPalette
value={titleColor}
onChange={onTitleColorChange}
/>
</PanelBody>
<PanelBody title={ 'Description Color' }>
<p><strong>Choose Description Color</strong></p>
<ColorPalette
value={bodyColor}
onChange={onBodyColorChange}
/>
</PanelBody>
</InspectorControls>,
<div class="row">
<div class="col-md-4">
<div class="feature-icon-container">
<MediaUpload
onSelect={onSelectImage}
type="image"
value={image}
render={ ( { open } ) =>
<IconButton
onClick={ open }
icon="upload"
className="editor-media-placeholder__button is-button is-default is-default"
>
Select Image
</IconButton>
}
/>
</div>
<div class="feature-description-container">
<RichText
key="editable"
tagName="p"
placeholder="Feature Title"
value= { title }
onChange= { onChangeTitle }
style= { { color: titleColor } }
/>
<RichText
key="editable"
tagName="p"
placeholder="Description"
value= { body }
onChange= { onChangeBody }
/>
</div>
</div>
</div>
]);
},
save({ attributes }) {
const {
title,
body,
titleColor,
bodyColor,
image,
} = attributes;
return(
<div class="row">
<div class="col-md-4">
<div class="feature-image-container">
<img class="feature-icon" src={ { image } } />
</div>
<div class="feature-description-container">
<RichText.Content style={ {color:titleColor } } tagName="p" value={title} />
<RichText.Content style={ {color:bodyColor } } tagName="p" value={body} />
</div>
</div>
</div>
)
}
});
There's a couple of things we need to correct, I'll just point you in the right direction:
image
attribute. You are getting the src
of the image from the MediaUpload
component and set the image
attribute (which is a URL string) via your onSelectImage
function. In the way you declared it now you try to source it from the innerHTML of an html selector.
Therefore the following syntax is sufficient in your attribute declaration:image: {
type: 'string',
default: '',
}
Instead of an empty string you can insert the src url for a default/placeholder image – if you want to.
And just to let you know: Sourcing from an html selector only makes sense if the innerHTML of that selector is editable in the content of the block, like an editable rich text component (i. e. paragraph).
edit
to actually display the image in the backend after it has been selected. Below is just a simple version that needs to be further optimized (as you cannot select another image this way).
Replace your complete <div class="feature-icon-container"></div>
structure in edit
with this:<div class="feature-image-container">
{ image === '' ?
<MediaUpload
onSelect={onSelectImage}
type="image"
value={image}
render={ ( { open } ) =>
<IconButton
onClick={ open }
icon="upload"
className="editor-media-placeholder__button is-button is-default is-default">
Select Image
</IconButton>
}
/>
:
<img class="feature-icon" src={ image } />
</div>
save
. Otherwise your URL string will be wrapped in an object.<img class="feature-icon" src={ image } />
Maybe it will be best to move your MediaUpload
to the controls sidebar, then it will be easier to keep the functionality of changing the selected image. Also if you set a default image it will not be possible to base your conditional on checking for an empty image
string. But I'll leave this up to you.