Discussions

Ask a Question
Back to All

How To: Use iMIS REST API to embed a virtual file path in an IQA result

This 'how to' was developed in response to an iMIS customer that is experiencing poor page load times due to overuse of included images from IQA query results. I'm posting it here hopes that it proves useful to others in the community.

The problem: One of the really nifty things you can do with iMIS is embed images directly into a page from IQA results by injecting markup into the display column property(ies) of an IQA.

Unfortunately, if an IQA returns multiple rows, or returns images that are of indeterminant size, this practice can result in very large web pages that perform poorly because they are a) huge and b) cannot take advantage of caching and other performance optimizations within the browser or our hosting environment.

This problem can be managed by tightly controlling the sizes of the images included in the IQA results, but that can require a significant effort from staff and you can still wind up with problems in environments where members/users can upload new images without staff intervention.

The solution: most iMIS iParts get around this issue by obtaining a URL path to an image from the iMIS service API. If you're developing a custom iPart that displays images from the iMIS database, you're probably already aware of how to get a virtual file path from the API, however, if you're using images with one of our more generic web parts like a QueryList, you'll need something more along the lines of what I'm posting here.

Summary: The basic idea is pretty simple: instead of embedding the actual encoded image in the query display, we just want to embed the data the API will need in order to retrieve a file path, and then we add javascript to obtain the file path once the document is loaded.

Step 1: Embed our API parameters in the display properties of the query. This is done from the Display tab of the IQA. Add a custom column that injects markup with data you'll need to get an image file path. In the case of an IQA with a contact BO source, we can get the member profile image, just need the party id to get the member's profile image path:

Example:

'<span class="image" id="my_image_prefix_' + vBoNetContactData.Id + '" ></span>'

Step 2: Add javascript to the content record page tag that scans the document for elements that match our id attribute prefix, makes the API call, and adds the image file to the document. Javascript can be added in PageBuilder under the 'Advanced' section of the page's 'Properties' tab.

Example:

function  setImageUrls(){
    const  tagPrefix = 'my_image_prefix_';
    // all of the <span> tags in the doc that start with our tag prefix
    const  imageSpans = document.querySelectorAll('span[id^="my_image_prefix_"]');
    // set the background image style for each tag to the correct virtual file path to the virtual file path
    imageSpans.forEach(function(currentValue){
        const  idAttr = currentValue.getAttribute("id");
        const  partyId = idAttr.substr(tagPrefix.length);
        console.log(idAttr + ", " + partyId);
        fetchProfileVirtualFileData(partyId, idAttr);
    });
}

function  fetchProfileVirtualFileData(partyId, idAttr) {
    let  xhr = new  XMLHttpRequest();
    xhr.open('POST', "https://my.imisdomain.com/api/PartyImage/_execute", true);
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.setRequestHeader('RequestVerificationToken', document.getElementById("__RequestVerificationToken").value);
    xhr.onload = function() {
    let  response = null;
    if (xhr.status === 200 || xhr.status === 201 || xhr.status === 202) {
        if (xhr.responseText) {
            var  virtualFile = JSON.parse(xhr.responseText);
            console.log(JSON.stringify(virtualFile));
            console.log(virtualFile.Result["VirtualPath"]);
            let  virtualPath = virtualFile.Result["VirtualPath"];
            if (virtualPath.startsWith('~')){ virtualPath = virtualPath.substr(1); }
            const  imageSpan = document.getElementById(idAttr);
            imageSpan.style.backgroundImage = "url(" + virtualPath + ")";
            }
        }
    };
    xhr.send(JSON.stringify(getProfileVirtualFileRequestBody(partyId)));
}

  

function  getProfileVirtualFileRequestBody(partyId){
    const  requestBody = {
        "$type":  "Asi.Soa.Core.DataContracts.GenericExecuteRequest, Asi.Contracts",
        "OperationName":  "GetProfileVirtualFile",
        "EntityTypeName":  "PartyImage",
        "Parameters": {
            "$type":  "System.Collections.ObjectModel.Collection`1[[System.Object, mscorlib]], mscorlib",
            "$values": [
                {
                    "$type":  "System.String",
                    "$value":  partyId
                }
             ]
       },
       "ParameterTypeName": {
               "$type":  "System.Collections.ObjectModel.Collection`1[[System.String, mscorlib]], mscorlib",
                "$values": [
                    "System.String"
                ]
            },
        "UseJson":  false
};
return  requestBody;
}
window.addEventListener("DOMContentLoaded", setImageUrls);

Theoretically, this can be done much more succinctly via the javascript Fetch API, but I've done this example using XHR because I'm still learning the ins and outs of fetch. If anyone comes across this and wants to post an example using fetch that'd be awesome =)!

Hope this helps the community! Cheers!

See Also:
https://developer.imis.com/reference/partyimage

Base64 image encoding pros and cons:
https://bunny.net/blog/why-optimizing-your-images-with-base64-is-almost-always-a-bad-idea/
https://stackoverflow.com/questions/1574961/how-much-faster-is-it-to-use-inline-base64-images-for-a-web-site-than-just-linki