0
votes

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> 
        ) 
    }
});
1

1 Answers

0
votes

There's a couple of things we need to correct, I'll just point you in the right direction:

  1. Small misconception in how you "source" your 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).

  1. You need some logic in 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>
  1. Change the double curly braces to single curly braces in 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.