Get the media image parameters of height, width and other properties in custom block Grid preview

Hi,
Any help or feedback on this would be most appreciated.

I am now working on a custom Block Grid preview for a block grid to display in the CMS where this block grid is displayed. It is a shame that Umbraco 15 does not, as previous versions have, include a set of defualt block grid views and block grid previews as examples.

Anyway, I have found no documentation in the custom block grid tutorial that includes displaying an image and it’s properties.

I have used this tutorial on Medium as a starting point:
Umbraco 14 custom views

Using this, I first had to change UmbMediaPickerPropertyValue to UmbMediaPickerPropertyValueEntry as it has become deprecated.

Here is the code of the custom block grid view as it stands:

import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { PlTextStyles } from '../../styles/site.pl.styles';
import type { UmbBlockEditorCustomViewElement } from '@umbraco-cms/backoffice/block-custom-view';
import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element";
import { html, customElement, property, css, state} from '@umbraco-cms/backoffice/external/lit';
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
import type { UmbBlockDataType } from '@umbraco-cms/backoffice/block';
import { UmbBlockGridTypeModel } from '@umbraco-cms/backoffice/block-grid';
// for images
import { UmbImagingRepository } from '@umbraco-cms/backoffice/imaging'; // Import the repository
import type { UmbMediaPickerPropertyValueEntry } from '@umbraco-cms/backoffice/media';


const elementName = "image-block";

// eslint-disable-next-line local-rules/enforce-umb-prefix-on-element-name
@customElement(elementName)
// eslint-disable-next-line local-rules/umb-class-prefix
export class ImageBlockCustomView
	extends UmbElementMixin(UmbLitElement)
	implements UmbBlockEditorCustomViewElement
{

	@property({ attribute: false })
	content?: UmbBlockDataType;

	@property({ attribute: false })
	settings?: UmbBlockDataType;

	@state()
	blockType?: UmbBlockGridTypeModel;

	@state()
	images: UmbMediaPickerPropertyValueEntry[] = [];

	@state()
	imageUrls: Map<string, string> = new Map();

	#imagingRepository = new UmbImagingRepository(this);


	async updated(changedProperties: Map<string | number | symbol, unknown>)
	{
		super.updated(changedProperties);
		if (changedProperties.has('content') && this.content?.image)
		{
			console.log("Images ITEM IMAGE :: : ", this.content?.image);
			this.images = this.content.image as UmbMediaPickerPropertyValueEntry[];

			console.log("Images ITEMS: ", this.images);
			await this.fetchLogoUrls();
		}
	}

	async fetchLogoUrls()
	{
		for (const imageItem of this.images)
		{
			
			if (imageItem.mediaKey && !this.imageUrls.has(imageItem.mediaKey))
			{
				const url = await this.resolveMediaUrl(imageItem.mediaKey);
				if (url)
				{
					this.imageUrls.set(imageItem.mediaKey, url);
					this.requestUpdate(); // Trigger a re-render to update URLs
				}
			}
		}
	}

	async resolveMediaUrl(mediaKey: string): Promise<string>
	{
		const { data } = await this.#imagingRepository.requestThumbnailUrls([mediaKey], 0, 0);
		console.log("IMAGE DATA::::: ", data?.[0]);
		return data?.[0]?.url ?? '';
	}

	override render()
	{
		console.log("IMAGE content:: ", this.content);
		console.log("IMAGE settings:: ", this.settings);
		return html`
			<h5>My Custom View</h5>
         <div class="logo-container">
             ${this.images.map((imageItem: UmbMediaPickerPropertyValueEntry) => html`
                 <img src="${this.imageUrls.get(imageItem.mediaKey) || ''}" alt="Logo" />
             `)}
         </div>
		`;
	}

	static override styles = [
		UmbTextStyles,
		PlTextStyles,
		css`
			:host {
                display: block;
                height: 100%;
                box-sizing: border-box;
                border-radius: 9px;
                padding: 12px;
            }
		`,
	];
}

export default ImageBlockCustomView;

declare global
{
	interface HTMLElementTagNameMap
	{
		[elementName]: ImageBlockCustomView;
	}
}

What I am having trouble with is accessing the actual height and with properties of the image. I also aim to add an alternative text field to the image media type, and wish to be able to display this as well, if needed in the block grid preview.

Please can any one assist and point me in the right direction with this?

Thank you,

Kind regards,

Pete

Hey @PeatLaur! It’s a little sidestep away from what you’re asking about, but I created a package called BlockPreview that saves you the effort of having to build custom previews for every block. Might be of use to you!

I can recommend using the <umb-imaging-thumbnail /> element to render images. It supports width, height, and crop mode, and it handles the whole requestThumbnailUrls from the repository. I think you can shave most of your block view away:

import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { PlTextStyles } from '../../styles/site.pl.styles';
import type { UmbBlockEditorCustomViewElement } from '@umbraco-cms/backoffice/block-custom-view';
import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element";
import { html, customElement, property, css, state, repeat} from '@umbraco-cms/backoffice/external/lit';
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
import type { UmbBlockDataType } from '@umbraco-cms/backoffice/block';
import { UmbBlockGridTypeModel } from '@umbraco-cms/backoffice/block-grid';
// for images
import type { UmbMediaPickerPropertyValueEntry } from '@umbraco-cms/backoffice/media';

interface MyBlockContent extends UmbBlockDataType {
    image?: Array<UmbMediaPickerPropertyValueEntry>;
}

const elementName = "image-block";

// eslint-disable-next-line local-rules/enforce-umb-prefix-on-element-name
@customElement(elementName)
// eslint-disable-next-line local-rules/umb-class-prefix
export class ImageBlockCustomView
    extends UmbElementMixin(UmbLitElement)
    implements UmbBlockEditorCustomViewElement
{

    @property({ attribute: false })
    content?: MyBlockContent;

    @property({ attribute: false })
    settings?: UmbBlockDataType;

    @state()
    blockType?: UmbBlockGridTypeModel;

    override render() {
        return html`
            <h5>My Custom View</h5>
            ${repeat(
                this.content?.image ?? [], 
                (image) => image.key,
                (image) => 
                    html`<umb-imaging-thumbnail class="thumbnail" width="300" height="300" mode="crop" unique=${image.mediaKey} alt="Logo"></umb-imaging-thumbnail>`
            )}
        `;
    }

    static override styles = [
        UmbTextStyles,
        PlTextStyles,
        css`
	    :host {
                display: block;
                height: 100%;
                box-sizing: border-box;
                border-radius: 9px;
                padding: 12px;
            }

            .thumbnail {
                max-width: 300px;
                max-height: 300px;
            }
		`,
    ];
}

export default ImageBlockCustomView;

declare global
{
    interface HTMLElementTagNameMap
    {
        [elementName]: ImageBlockCustomView;
    }
}

Notice how a custom interface resolves the image property and that we are passing different parameters to the imaging element.

HI @jacob and @rickbutterfield,

A big thank you for you replies.
A big part of my research here is to understand the code for Umbraco 15 Back Office Typescript.

@rickbutterfield , I am have looked at you great application for previewing block Grids, and looked at the source code to try to understand where the width and height values for an uploaded and displayed image come from in the code.

@jacob , I am looking for the code that gets the actual width and height of an uploaded image, your example uses hard coded values for width and height of 300. Also, we can add extra values to an image, like alternative text.

html<umb-imaging-thumbnail class="thumbnail" width="300" height="300" mode="crop" unique=${image.mediaKey} alt="Logo"></umb-imaging-thumbnail>

I appreciate your feedback and was waiting for any replies to help me. I have been on other projects recently, but I will soon be turning my attentions back to Umbraco 15/16 and typescript and what changes I need to make, like replacing macros with block grid to enable us to upgrade to Umbraco 15 and 16.
I will look again at the back office source code to see if I can get the variables for width and height.

In the old back office we could get those values.

Thanks again,

Kind regards,
Pete

I been trying to sort this myself before the Easter break and was making progress.
I will share my outcomes when I am back on it.

You need to request the media item entity through the API to get its properties. The Backoffice has a repository for that:

import { UmbMediaDetailRepository } from '@umbraco-cms/backoffice/media';

mediaRepository = new UmbMediaDetailRepository(this);

const {data: image} = await this.mediaRepository.requestByUnique(mediaKey)
console.log(image);

Hi @jacob,
Thank you for getting back to me with this.
I am in the middle of upgrading my computer this week and and my internet so as soon as I can I will look at this. Excited to get to taking a look as this, it’s looking like it could be the next step in my understanding of the Umbraco Typescript back office.
I did not want you to feel that I have not seen your reply. I am very appreciative indeed for your efforts in getting this back to me.
Kind regards,
Pete

1 Like