PCF – How to use images in component
A few days ago a twitter user (kr!$#n@) mentioned me and some other developers in a tweet. Included was a question regarding PCF.
I was happy to help and honored to be one of the persons the community asks for help. I suggested to use the Office UI Fabric. Thanks to Scott for mentioning that it is now called differently – Fluent UI. I really appreciate it. I always try to get the names right (even if they change a lot).
But the goal of kr!$#n@ is to not include any external urls.
In my opinion, this can only be achieved when you ship the icons as images and use them inside of the PCF component. I provided a demo repo on GitHub for kr!$#n@.
Since this was a question in the community I thought it is worth an article.
In this article we will learn how to ship and use images in a PCF component.
The idea
Actually I have never had the need for images in one of my PCF components so far. But since there is the image type for resources (link to docu) I thought: “Can’t be that hard to implement”.
Unfortunately, it is not as straight forward as I thought/hoped it would be. I searched for a solution and found a discussion in the Power Apps community. The fifth answer (as well from Andrew Ly, who also has asked the question) gave me the hint/solution. It was not 100% working for me but it was pretty close.
The idea is to load the image as a resource, defined in the manifest, via the PCF API (link to docu). The response can then be used as a data URL in the source of an image tag.
Lets dive in and see how this could be achieved.
Update (2021-04-09): SVG-Images are currently not supported. The content you get from the WebAPI when retrieving the image will be empty when you are using SVG-Images.
Structure
Lets take a look at our file and folder structure.
First of all, we have to create a new PCF component project. I used the following command in VS Code.
pac pcf init --namespace Demo --name IconDemoComponent --template field
This will create a new project “IconDemoComponent” with the namespace “Demo”.
After creating a new PCF component project we end with the following structure.
|- IconDemoComponent
|- generated
|- ManifestTypes.d.ts
|- ControlManifest.input.xml
|- index.ts
|- package-lock.json
|- package.json
|- ...
Since this will be a pretty simple implementation we will only add an image and a css file. Both in a separate folder (one css and one img).
For demo purpose I have created a simple PNG-File from the free FontAwesome user icon (link).
The following should be our new structure
|- IconDemoComponent
|- css
|- IconDemoComponent.css
|- generated
|- ManifestTypes.d.ts
|- img
|- user.png
|- ControlManifest.input.xml
|- index.ts
|- package-lock.json
|- package.json
|- ...
Manifest
In the Manifest we only have to add two resources.
- The CSS -File and
- The PNG-File
Both will be added within the resources tag.
The CSS-File with the following line
<css path="css/IconDemoComponent.css" order="1" />
The PNG-File with the following line
<img path="img/user.png" />
The Manifests resource tag should look like this:
CSS
The css will only define the height and width of our img-tag.
img{ width:100px; height:100px; }
TypeScript
The index.ts is the most interesting file in this implementation.
First of all we have to define the img element.
private imgElement:HTMLImageElement;
In the “init” function we will define the img element, request the getResource endpoint and append the img element to the container. This will be achieved with the following 3 lines
this.imgElement = document.createElement("img"); context.resources.getResource("user.png", this.setImage.bind(this, false, "png"), this.showError.bind(this)); container.appendChild(this.imgElement);
The important part is the second line. The getResource (docu) function expects three parameters
- Image id
- success callback
- failure callback
Since the PowerApps Component Framework will flatten the file structure we have to “search” the image in the root folder and not in the “img” folder, where we actually have saved it. This is the reason why we only use “user.png” and not “img/user.png”.
Our showError function does actually nothing in this demo. It is just present because the getResource function does need it.
private showError(): void { }
The setImage function is already a bit more interesting.
private setImage(shouldUpdateOutput:boolean, fileType: string, fileContent: string): void { let imageUrl:string = this.generateImageSrcUrl(fileType, fileContent); this.imgElement.src = imageUrl; }
In this function, we do get the image data Url from a function called “generateImageSrcUrl”. This function takes the file type and file content (in base64). We will look at this function in a minute. We then set the image data Url as the source of the img element.
The most important part is the “generateImageSrcUrl” function, even though it basically only is one line.
private generateImageSrcUrl(fileType: string, fileContent: string): string { return "data:image/" + fileType + ";base64, " + fileContent; }
The data URL basically begins with “data” followed by the file type, in our case “image/png”. The file type comes partly as a parameter. After that, we have the indicator that the content is presented in base64. At the end, we do have the actual file content.
Conclusion
If we know how it is working it basically are only some rows of code to achieve our goal of using images in a PCF component.
One important thing to mention is, that this will not work in the harness. It only works in an actual Model-Driven App (MDA). This was mentioned by Oleksandr (OOlashyn) in the mentioned Community discussion.
You can find the demo Repo on GitHub.
https://github.com/BenediktBergmann/PCF-Image-Demo
I hope you enjoyed this article. Please leave me a comment or contact me in another way if you have any questions or feedback.
You can also subscribe and get new blog posts emailed to you directly.
Thanks, Benedikt!
but this example doesn’t work
look this:
context.resources.getResource(“user.png”, this.setImage.bind(this, false, “png”),
– how we pass fileContent parameter ?
Hej Rustam,
thanks for your comment.
What exactly is not working for you?
The getResource function needs 3 parameters. The first one is the file it should load, the second one is the success callback function (in our case setImage) and the last one is the error callback function.
So after getResource has loaded the image “user.png” it will call setImage with the “user.png” as a base64 string.
Hope this helps.
//Benedikt
I’ve downloaded sample code from GitHub. After running it the logged Image Src value is “data:image/png;base64, undefined” which is definitely not the valid image address 🙁
Hey! I was looking for something like this for a long time! So thank you! I have one question, I need to do something like this, but in this format -> /image/ some text here /image/
How can I add those two images at both sides of a text? could you give me some ideas? Thank you so much
So you mean the path to the image is dynamical?
I have the folder with the code .ts and .css and one folder that is called img and inside I have both images, when I upload de solution with the custom control, the images don’t show, only the text, my html code is like this:
let capa: HTMLDivElement = document.createElement(“div”);
let id: string = “IconDemo”;
let stringHTML: string = “”;
stringHTML = `
Datos del Contacto
`;
capa.setAttribute(“id”, id);
capa.innerHTML = stringHTML;
container.appendChild(capa);
I am learning to use custom controls, so I am pretty lost. Thank you so much for answering 🙂
Looks like wordpress is filtering out some stuff from your code snippets.
Could you upload your code to a GitHub repo and share the link? I could take a look at it then.
It did not work for me either, the getResource method returns .
One of the awnsers here (https://powerusers.microsoft.com/t5/Power-Apps-Pro-Dev-ISV/Is-there-an-example-of-using-Image-Resources-within-PCF-control/td-p/342152) suggested to just embed the icon in the CSS, which worked perfectly.
Hej Jean,
thanks for the comment. The post is now already some time old. I have to revisit this one.
Hi,
This does not seem to work in canvas app. Any workaround to use local images in canvas app?
Thanks
Hej,
thanks for the information, that it is not working with Canvas Apps.
I don’t have a workaround at the moment. I would have to look into that.