How can I correctly retrieve attachment metafields in a WordPress block when using MediaUpload?

How can I correctly retrieve attachment metafields in a WordPress block when using MediaUpload?

I’m building a custom Gutenberg block that displays a gallery of images. Each image has a custom attachment meta field called _oembed_url, which stores an external video URL (for example, YouTube or Vimeo). This field is added via the Attachment_fields_to_edit and Attachment_fields_to_save filters in PHP.

Since the onSelect callback from MediaUpload or MediaPlaceholder does not contain attachment meta fields, I use wp.data.select(‘core’).getEntityRecord(‘postType’, ‘attachment’, image.id) to retrieve the missing meta value and include it in the images attribute of my block.

Here is the relevant part of my code:

<>
    <InspectorControls>
        <PanelBody title={__('Settings', 'my-domain' )}>
        </PanelBody>
    </InspectorControls>
    {images.length > 0 && (
    <BlockControls>
        <ToolbarGroup>
            <MediaUploadCheck>
                <MediaUpload 
                    allowedTypes={['image']}
                    multiple={true}
                    gallery={true}
                    value={images.map((image) => image.id)}
                    onSelect={setImages}
                    render={({ open }) => (
                    <ToolbarButton onClick={open}>
                        {__('Edit images', 'my-domain')}
                    </ToolbarButton>)}
                />
            </MediaUploadCheck>
        </ToolbarGroup>
    </BlockControls>
    )}
    <MediaUploadCheck>
        {images.length > 0 ?
        <figure {...blockProps}>
            <div className="gallery-thumbnails">
                {
                displayImages(images)
                }
            </div>
        </figure>
        :
        <div {...blockProps}>
            <MediaPlaceholder
                accept="image/*"
                allowedTypes={['image']}
                onSelect={setImages}
                multiple={true}
                gallery={true}
                addToGallery={true}
                handleUpload={true}
                labels={ { title: __('Project Gallery', 'my-domain'
                ) } }
            />
        </div>
        }
    </MediaUploadCheck>
</>

The setImages() code:

const setImages = (value) => {
    const imageDetails = value.map(image => {
        const oembed = wp.data.select('core')
            .getEntityRecord('postType', 'attachment', image.id)
            ?.meta?._oembed_url || '';
        return {
            id: image.id,
            url: image.url,
            alt: image.alt,
            caption: image.caption,
            oembed,
        };
    });
    setAttributes({ images: imageDetails });
};

And I render the gallery like this:

const displayImages = (images) => (
    images.map((image, index) => {
        const hasOembed = image.oembed && image.oembed.trim() !== '';
        return (
            <div className={`gallery-thumbnail ${hasOembed ? 'has-oembed' : ''}`} key={index}>
                <figure>
                    <img src={image.url} alt={image.alt} />
                </figure>
            </div>
        );
    })
);

The problem is that getEntityRecord() is asynchronous: when onSelect is executed, sometimes the _oembed_url metafield is not yet available. As a result, some image.oembed values ​​are empty even though they exist. Once the data is finally loaded, the component isn’t automatically re-rendered to show the updated meta (and the has-oembed CSS class is never added to the div).

Question: How can I make my block wait for the metafields to become available before setting the images attribute or automatically update the component once getEntityRecord() has finished loading?

#correctly #retrieve #attachment #metafields #WordPress #block #MediaUpload

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *