3

We're currently using an aura component with the <force:canvasApp> component to embed our Canvas App. As mentioned in the answer to this post, Sfdc.canvas is not fully exposed to the aura component. I've attempted to load canvas-all.js into the aura component using <ltng:require>, but from the aura component's controller/helper javascript files, I can't get Sfdc.canvas.parent to show up (even though the browser console shows Sfdc.canvas.parent as existing).

Does anyone have a working example of firing a custom canvas app event via Sfdc.canvas.parent.publish from an aura component into the embedded Canvas App? Any other way to fire an event from Lightning to the Canvas App?

Update (Edit 1)

As suggested by @rzr below, using postMessage() from the outer iframe in the Canvas App would do the job (you can see an example by @studds here and postMessage() in action to communicate between Lightning and a VF page here). Only problem is that it is seemingly impossible to touch the DOM inside the <force:canvasApp> component as per Lightning Locker DOM security.

Does anyone know how to send any communications from Lightning to the embedded <force:canvasApp> app?

2 Answers 2

1

I think the canvas-all.js is only meant to be used in the app being canvassed, and not in any Aura/LWC component.

Is there anything keeping you from using the standard iframe postMessage API? If you see the documentation for force:canvasApp it states that the component takes a canvasId="someId" property that you should use for targeting events:

"An unique label within a page for the Canvas app window. This should be used when targeting events to this canvas app."

I havent tried this out but it might send you in the right direction:

component.find("someId").getElement().contentWindow.postMessage("message", "some message");

Then in your app being canvassed:

$(window).on("message", function(e) {
  console.log(e.originalEvent.data)
});
2
  • 1
    Since the canvas app is a separate component, you can't access the DOM via .getElement() (see the Invalid DOM Access section). So component.find("myCanvasApp").getElement() throws an error. Otherwise your approach is right on target. Feb 17, 2019 at 20:48
  • Here's how to use the canvasId to get the unique outer iframe identifier: const canvasIframeOuterId = 'canvas-outer-' + namespace + ':' + appName + ':' + canvasId; Feb 17, 2019 at 20:51
1

The response I got from Salesforce support was that it's not possible from a lightning component, but I was able to work around it. See my answer here.

Since using events between a Visualforce page and a canvas app is supported, I wrapped our canvas app in an empty Visualforce page and used this blog article as an example to communicate between the lightning component and the Visualforce page by loading the Visualforce page in an iframe:

visualforceCanvasAppPage.page

<apex:page>
    <apex:includeScript value="/canvas/sdk/js/48.0/controller.js" />
        
    <script>
        function publishToCanvasApp() {
            Sfdc.canvas.controller.publish({
                name : 'mynamespace.myevent',
                payload : {}
            });
        }

        // Listen for lightning component postMessage and pass to canvas app
        window.addEventListener(
            "message",
            event => {
                console.log("message event", event);

                if (event.data.EXAMPLE == "example payload to Visualforce page") {
                    publishToCanvasApp();
                }
            },
            false
        );
    </script>

    <apex:canvasApp developerName="DEVELOPERNAME" />
    
    <!-- <apex:outputLink value="{!$Page.visualforceCanvasAppPage}">
        You can uncomment this to find out what the vfHost parameter value of the lightning component should be.
    </apex:outputLink> -->  
</apex:page>

lightningComponent.cmp

<aura:component>
    <aura:attribute name="vfHost" type="String" default="ORGNAME--SANDBOXNAME--x.xx99.visual.force.com" />

    <iframe aura:id="vfFrame" src="{!'https://' + v.vfHost + '/apex/visualforceCanvasAppPage'}" />
</aura:component>

lightningComponentController.js

({
    sendMessageToCanvasApp: function(component, event, helper) {
        let vfFrame = component.find("vfFrame");
        let el = vfFrame.getElement();

        // post message to Visualforce iframe to relay to canvas app
        el.contentWindow.postMessage({"EXAMPLE": "example payload to Visualforce page"}, "*");
    }
})

externalSite.js

Sfdc.canvas.client.subscribe(
    salesforceSignedRequest.client,
    [{
        name: 'mynamespace.myevent', 
        onData: function (e) {
            alert("Hello from canvas app myevent!"); 
        } 
    }]
);

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .