Search code examples
angularresourcescustom-elementangular-elements

How can images be used in an Angular Element


I am creating an angular element which is supposed to be embedded on multiple external sites.

The embedding process will ofc just be a reference to the compiled script and a DOM element representing the element:

<html>
   <head>
      <script src="element.js"></script>
   </head> 
   <body>
      <element></element>
   </body>
</html>

But the problem is that the element makes use of a few assets in .png, .svg, .gif and .jpg format.

And of course the compiled element used on a different site, cannot reference those resources.

The documentation for angular elements is very limited, and not even Angular Docs says anything about this.

What is the most optimal way to achieve this? I have seen some people who converted the resources to data and rendered it that way instead.


Solution

  • Embed your Image

    The best way to create a fully self-contained component is to embed your images into your components using DATA URIs.

    Instead of this:

    <img src="http://www.example.com/image.jpg"/>
    

    You encode the image file into a text format, like Base64, and then use that text data in the src attribute:

    <img src="data:image/png;base64,your-base-64-encoded-image-data" />
    

    More details here: https://www.thesitewizard.com/html-tutorial/embed-images-with-data-urls.shtml

    Yes, this does increase the size of your component, but now your component is self contained and is more portable to be used in other apps without needing to remember to also include the image in the new app.

    You can run Base64 command on a *nix machine:

    base64 -i "myimage.jpg"
    

    or:

    base64 -i "myimage.jpg" -o "base64.txt"
    

    Or use an online converter:

    Links were validated on 2022-02-14

    SVG Files

    Since SVG files are XML based you can embed SVG files directly into your HTML. You can also embed the SVG data into an <img> tag.

    SVG files are already text so you to not need to Base64 encode them. But you do need to percent-encode them. This produces a much smaller chunk of data.

    When embedding the SVG into an <img> tag you should remove the <?xml version="1.0"?> from the top of the SVG data.

    Here is a simple routine to perform the percent encoding:

    const encodeSvg = str => str.replace(/\s+/g, ' ').replace(/["%#{}<>&|\[\]^`;?:@=]/g, key => encodeURI(key));
    

    Here is a small SVG file:

    <?xml version="1.0"?>
    <svg xmlns="http://www.w3.org/2000/svg" width="12cm" height="12cm">
      <g style="fill-opacity:0.7; stroke:black; stroke-width:0.1cm;">
        <circle cx="6cm" cy="2cm" r="100" style="fill:red;"
                        transform="translate(0,50)" />
        <circle cx="6cm" cy="2cm" r="100" style="fill:blue;"
                        transform="translate(70,150)" />
        <circle cx="6cm" cy="2cm" r="100" style="fill:green;"
                        transform="translate(-70,150)"/>
      </g>
    </svg>
    

    This is what is looks like when it is percent-encoded:

    <img src="data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2212cm%22 height=%2212cm%22%3E %3Cg style=%22fill-opacity:0.7; stroke:black; stroke-width:0.1cm;%22%3E %3Ccircle cx=%226cm%22 cy=%222cm%22 r=%22100%22 style=%22fill:red;%22 transform=%22translate(0,50)%22 /%3E %3Ccircle cx=%226cm%22 cy=%222cm%22 r=%22100%22 style=%22fill:blue;%22 transform=%22translate(70,150)%22 /%3E %3Ccircle cx=%226cm%22 cy=%222cm%22 r=%22100%22 style=%22fill:green;%22 transform=%22translate(-70,150)%22/%3E %3C/g%3E %3C/svg%3E"/>