[SalesForce] Need help with LWC and iframe

I appreciate you looking. Here's the problem I'm having:

  1. I have a project that includes pdfjs as a static resource. Part of this resource is an html file, viewer.html, that is the actual pdf viewer. This is unmodified, save for #2.
  2. The html file is modifed with an event listener so I can pass it data via postMessage.
  <script>
    if (window.addEventListener) {
      // For standards-compliant web browsers
      window.addEventListener("message", handleMessage, false);
    } else {
      window.attachEvent("onmessage", handleMessage);
    }

    function handleMessage(evt){
      ...
    }
  </script>
  1. I have an LWC that is simply an iframe where the iframe src is viewer.html
<template>
    <div>
       <iframe src={viewerUrl} style={viewerStyling}></iframe>
    </div>
</template>
get viewerUrl() {
  return PDFJS + "/web/viewer.html";
}
  1. The LWC retrieves data via Apex controller, processess it, and then passes the data to the iframe via postMessage. Viewer.html receieves the data and loads the file.
  loadData() {
    let data = {
      pdfData: this.pdfData,
      ...
    };

    this.template.querySelector("iframe").contentWindow.postMessage(data, "domain.name");
  }

The iframe LWC is the child of a larger component. Before I continue, I want to say that everything is working well. I'm seeing issues when the components are initially loaded.

I was seeing an issue when the components are initially loaded where a documents are loaded intermittently and I traced it down to when the event listener is registered in viewer.html.

Good load:

...
other child: renderedCallback

viewer.html: load
viewer.html: addEventListener

pdfjs child: loadData

viewer.html: handleMessage
...

Bad load:

...
other child: renderedCallback

pdfjs child: loadData

viewer.html: load
viewer.html: addEventListener
...

For those that failed the listener is registered AFTER I've retreived the data and attempted to post it to the iframe. Since there's no listener registered yet in viewer.html postMessage just silently fails.

My guess is that this is related to when the iframe is loaded into the DOM (I could be very wrong, though). My question, is there a way to force-load the iframe without doing it manually? Ultimately I need the html file's event listener to be loaded and ready for me when I need it.

I apologize in advance if this is a super-easy fix (which I hope it is), but my trick bag is empty and I'm not sure what to do.

Thanks for any an all help

Best Answer

This is a timing issue, depending on when you are calling loadData() in your LWC and the Apex time to return the data, the iFrame may or may have not registered the listener yet.

An easy solution would be to just load the data after the iFrame finished loading:

<template>
    <div>
       <iframe src={viewerUrl} style={viewerStyling} onload={loadData}></iframe>
    </div>
</template>

For reference, here is the MDN onload event:

Notes

The load event fires at the end of the document loading process. At this point, all of the objects in the document are in the DOM, and all the images, scripts, links and sub-frames have finished loading.

Related Topic