Daryl
Daryl

Reputation: 18895

Accessing From Context From WebResource Hosted In Form Post Xrm.Page Removal

What is the best approach to access the FormContext of a form from a WebResource hosted on the form?

The docs say to access the Form Context from a html WebResource to use parent.Xrm.Page.

An HTML web resource added to a form can’t use global objects defined by the JavaScript library loaded in the form. An HTML web resource may interact with the Xrm.Page or Xrm.Utility objects within the form by using parent.Xrm.Page or parent.Xrm.Utility, but global objects defined by form scripts won’t be accessible using the parent. You should load any libraries that an HTML web resource needs within the HTML web resource so they’re not dependent on scripts loaded in the form.

But Xrm.Page is deprecated, and will be removed. When it does, my assumption is parent.Xrm.Page will fail to work at that point in time as well.

This assumption is echo'd by a closed issue posted at the end of the doc page.

An HTML web resource added to a form can’t use global objects defined by the JavaScript library loaded in But if you look at the issues on the page above, someone asks the obvious question: To prevent a gap in functionality, how will an HTML web resource included on an entity form access the form's context, when the only supported method available today is being deprecated? Can we depend on there being a supported alternative to "parent.Xrm.Page" by the time Xrm.Page is removed?

To which the reply is to use the getContentWindow, and inject it in from the onload of the form. This still isn't great because there is no guarantee that the web resource will finish loading in time to be able to accept said value. A dev even commented as such and posted their work around on the getContentWindow doc page

function form_onload(executionContext) {
    const formContext = executionContext.getFormContext();
    const wrControl = formContext.getControl("new_myWebResource.htm");
    if (wrControl) {
        wrControl.getContentWindow().then(contentWindow => {
            let numberOfCalls = 0;
            let interval = setInterval(() => {
                if (typeof contentWindow.setClientApiContext !== "undefined") {
                    clearInterval(interval);
                    contentWindow.setClientApiContext(Xrm, formContext);
                }
                else
                    //stop interval after 1 minute
                    if (++numberOfCalls > 600) {
                         clearInterval(interval);
                         throw new Error("Content Window failed to initialize.");
                    }
            }, 100);
        });
    }
}

Is this currently the best/recommended approach for getting this to work, or will the parent.Xrm.Page still work even when the Xrm.Page has been removed?

Upvotes: 3

Views: 1948

Answers (1)

Victor Sanchez
Victor Sanchez

Reputation: 35

I have been using getContentWindow for a while, and it does wait for the web resource to load.

What this function does in the backend is to retrieve the content from the iframe, so I strongly believe that this waits for the contentWindow to be loaded. My thoughts are this contentWindow is doing the exactly same as the common contentWindow of an Iframe: https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/contentWindow

As an example, if you have two tabs, let's say: General and Details.

If you have in your onLoad the getContentWindow, and you have your web resource in your Details tab, the getContentWindow will load only when the Details tab is clicked so it is rendered the Web Resource.

function form_onload(executionContext) {
  const formContext = executionContext.getFormContext();
  const wrControl = formContext.getControl("new_myWebResource.htm");
  if (wrControl) {
    var contentWindow = await wrControl.getContentWindow();
    contentWindow.CallTheFunctionInYourWebResource(Xrm,formContext,...params)
  }
}

In your web resource you would have something like:

<html>...
<script>
function CallTheFunctionInYourWebResource(_xrm, _formContext) {
  // Do whatever you need to do here
}

</script>

Hope it helps!

Upvotes: 2

Related Questions