The wiring to do this is mostly there, but lightning apps don't have access to Sfdc.canvas
at present, it seems. If they did have access to Sfdc.canvas
then this would work.
You can send a message from the canvas app, and it will reach the containing window, but it seems there's no way to listen for it at present. You can also subscribe to events in the canvas app, and if you construct and post a correctly formatted message from the top window, the canvas app will receive it. Funnily enough, onCanvasSubscribed
will fire when the canvas app subscribes, but I cannot for the life of my figure out why it would, when it seems impossible to publish
a message from the lightning component.
So, in short, the answer to this is "it's not possible". The long answer is "it's a very bad idea, because you need to partially reimplement the canvas event messaging protocol, which exposes you to the risk that the unpublished, proprietary protocol will change, thus breaking your app without warning."
A note on access to Sfdc.canvas
The Salesforce Spring ’16 Release Notes state that Sfdc JavaScript Global Is Removed for lightning components, however:
The sole exception is the official Force.com Canvas API, which is available using the Sfdc.canvas object. Canvas APIs as documented in the Canvas Developer Guide remain available and are fully supported.
This seems to suggest that Sfdc.canvas
ought to be available to lightning components, and the fact that it appears to be unavailable could be considered a defect.
Warning: rest of this post covers non-standard, probably unsupported techniques
So, for posterity and those unmotivated by pragmatic programming practices, this is how you can send and receive messages from a canvas app within lightning.
Sending messages from canvas to lightning
Send a message as normal from the canvas app:
var srClient = signedRequest.client;
Sfdc.canvas.client.publish(
srClient,
{name : "mynamespace.statusChanged", payload : {status : 'Completed'}});
You ought to be able to listen for this message using:
Sfdc.canvas.parent.subscribe('mynamespace.statusChanged', callbackFn);
And indeed, if you run this in the javascript console, you can listen for these messages. If only lightning components had access to Sfdc.canvas
! Since we don't, the only way to receive this message in the lightning component is by listening for it directly on the window:
window.addEventListener('message', function (event) {
var data = JSON.parse(event.data);
if (data.targetModule === 'Canvas' &&
data.body &&
data.body.event &&
data.body.event.name === 'mynamespace.statusChanged')
console.log('payload from canvas app', data.body.event.payload);
}, false);
Sending messages from the lightning component to canvas app
We can subscribe to messages as normal in the canvas app:
var srClient = signedRequest.client;
Sfdc.canvas.client.subscribe(
srClient,
{
name : 'johnny.begood',
onData : function (event) {
console.log("Subscribed to custom event ", event);
}
});
// at this point, onCanvasSubscribed
in the lightning component seems to fire
It's very odd that onCanvasSubscribed
fires at this point. That seems pointless if there's no way official way to publish. Perhaps there is a way, but if there is, I haven't been able to find it.
Ideally, you'd be able to publish an event from the lightning app this way:
Sfdc.canvas.parent.publish('mynamespace.statusChanged', payload);
As with subscribing, this is not possible, but there is a workaround. From the lightning component, post a correctly formatted message to the outer wrapper iframe:
// canvas apps contain both an inner and outer iframe. The canvas app is
// in the inner iframe, but we post our message to the outer iframe
var outerIframe = document.querySelector('.forceCanvasApp iframe');
// we need the correct target URL for the outer iframe. hard-coding this
// is a Bad Idea. How to do it otherwise left as an exercise for the reader
// Hint: listen for the subscribe message via
// `window.addEventListener('message', listener)`
var wrapperUrl = 'https://my-domain.my.salesforce.com';
// to correctly format this message, you'll need to listen on the window
// for the canvas app subscribe message
// and extract the instance id
var instanceIdFromClientSubscribeMessage = '_:test01:6:0:canvasapp';
var payload = { whatever: 'you want' };
var wrappedPayload = {
parentVersion: '39.0',
clientVersion: '34.0',
body: {
type: 'event',
config: {
client: {
instanceId: instanceIdFromClientSubscribeMessage
}
},
event: {
name: 'johnny.begood',
method: 'onData',
payload: payload
}
}
}
outerIframe.contentWindow
.postMessage(JSON.stringify(wrappedPayload), wrapperUrl);