How To Implement Bidirectional Communication LWC With VF is added inside iframe.

Hello #Trailblazers,
In this blog post we are going to talk about how to communicate between LWC and Visualforce page.
While running the VisualForce page in Salesforce, you need to know about two things.
Different DOMs. A Visualforce page hosted in Lightning Experience is loaded in an iframe. In other words, it’s loaded in its own window object which is different from the main window object where the Lightning Components are loaded.
Different Origins. Visualforce pages and Lightning Components are served from different domains. For example, if you are using a developer edition:
In our case, that means that a VisualForce page can’t use the parent window reference to access content or execute code in the Lightning Component wrapper. Similarly, the Lightning component can’t use the iframe’s contentWindow reference to access content or execute code in the VisualForce page it wraps.
The window.postMessage() method safely enables cross-origin communication between Window objects; e.g., between a page and a pop-up that it spawned, or between a page and an iframe embedded within it.
Normally, scripts on different pages are allowed to access each other if and only if the pages they originate from share the same protocol, port number, and host (also known as the “same-origin policy“). window.postMessage() provides a controlled mechanism to securely circumvent this restriction (if used properly).
You can get the complete code From GitHub Repo.
<!--
@description :
@author : Amit Singh
@group :
@last modified on : 03-28-2021
@last modified by : Amit Singh
Modifications Log
Ver Date Author Modification
1.0 03-28-2021 Amit Singh Initial Version
-->
<template>
<lightning-card variant="Narrow" title="lWC-VF Biredirectonal Communication" icon-name="custom:custom43">
<div class="slds-p-horizontal_small">
<lightning-textarea name="message" label="Message From VF" value={receivedMessage}></lightning-textarea>
</div>
<div class="slds-p-horizontal_small">
<lightning-input type="text" variant="standard" onchange={handleChange} name="messgaeToSend"
label="messgaeToSend">
</lightning-input>
</div>
<div class="slds-p-around_small">
<lightning-button variant="brand" label="Send to VisualForce" onclick={sendMessgaeToVisualForce}></lightning-button>
</div>
<div class="slds-p-around_small">
Visual Force Content
</div>
<div class="slds-p-horizontal_small">
<iframe height="500" width="100%" frameborder="0" src="/apex/SimpleVF"></iframe>
</div>
<p slot="footer">
<lightning-badge label="LWC"></lightning-badge>
<lightning-badge label="VF"></lightning-badge>
<lightning-badge label="Bidirectonal Communication"></lightning-badge>
</p>
</lightning-card>
</template>
/**
* @description :
* @author : Amit Singh
* @group :
* @last modified on : 03-28-2021
* @last modified by : Amit Singh
* Modifications Log
* Ver Date Author Modification
* 1.0 03-28-2021 Amit Singh Initial Version
**/
import { LightningElement,wire} from 'lwc';
import getVisualforceOrigin from '@salesforce/apex/VFC_DomainsController.getVisualforceOrigin';
export default class PocLwcWithIframe extends LightningElement {
@wire(getVisualforceOrigin) visualForceOrigin;
receivedMessage;
messageToSend;
connectedCallback() {
// Binding EventListener here when Data received from VF
// param1 - the event which will listen the message
// param2 - the method which will be called when the message will be recieved
// param3 - event capture
window.addEventListener( "message", this.handleResponse.bind(this), false );
}
handleResponse(message){
// check the origin match for both source and target
if (message.origin === this.visualForceOrigin.data) {
this.receivedMessage = JSON.stringify(message.data);
}
}
handleChange(event) {
this.messageToSend = event.detail.value;
}
sendMessgaeToVisualForce() {
let message = {
message : this.messageToSend,
source : 'LWC'
};
let visualForce = this.template.querySelector("iframe");
if( visualForce ){
visualForce.contentWindow.postMessage(message, this.visualForceOrigin.data);
}
}
}
Code Highlights: sendMessgaeToVisualForce Method
Code Highlights : connectedCallback Method
Code Highlights : @wire(getVisualforceOrigin) Method
var lexOrigin = '{!lexOrigin}';
window.addEventListener("message", function (event) {
if (event.origin === lexOrigin) {
var receivedfromLWC = event.data;
let outputLWC = document.getElementById('text-area-sfdc-lwc');
outputLWC.innerHTML = JSON.stringify(receivedfromLWC);
}
}, false );
Code Highlights:
/**
* @description :
* @author : Amit Singh
* @group :
* @last modified on : 03-28-2021
* @last modified by : Amit Singh
* Modifications Log
* Ver Date Author Modification
* 1.0 03-28-2021 Amit Singh Initial Version
**/
public with sharing class VFC_DomainsController {
public string lexOrigin {
get {
return URL.getOrgDomainUrl().toExternalForm().split('.my.')[0]+'.lightning.force.com';
// Exprcted outcome for Developer Orgs & Sandbox https://your-domain-dev-ed.lightning.force.com/
// Expected outcome for Prod Org - https://your-domain.my.lightning.force.com/
}
set;
}
@AuraEnabled(cacheable = true)
public static string getVisualforceOrigin() {
string visualOrigin = '';
string baseUrl = URL.getOrgDomainUrl().toExternalForm();
// Expected Format = https://domain.my.salesforce.com
// Expected Format for DE, Sandbox & Production ORgs = domain--c.visualforce.com
visualOrigin = baseUrl.split('.my.')[0] + '--c.' + 'visualforce.com';
return visualOrigin;
}
}
In this second scenario, we still have the same arrangement: a VisualForce page wrapped in a Lightning component. This time we need communication to happen in the opposite direction: The VisualForce page sends messages to the Lightning component.
function sendToLigntningWC(){
let lightningOrigin = '{!lexOrigin}';
let textAread = document.getElementById('text-area-sfdc');
let messgaeToLWC = {
message : textAread.value,
source : 'VisualForce Page'
}
window.parent.postMessage( messgaeToLWC, lightningOrigin );
}
Code highlights:
connectedCallback() {
// Binding EventListener here when Data received from VF
// param1 - the event which will listen the message
// param2 - the method which will be called when the message will be recieved
// param3 - event capture
window.addEventListener( "message", this.handleResponse.bind(this), false );
}
handleResponse(message){
// check the origin match for both source and target
if (message.origin === this.visualForceOrigin.data) {
this.receivedMessage = JSON.stringify(message.data);
}
}
Code Highlights:
<!--
@description :
@author : Amit Singh
@group :
@last modified on : 03-28-2021
@last modified by : Amit Singh
Modifications Log
Ver Date Author Modification
1.0 03-28-2021 Amit Singh Initial Version
-->
<apex:page lightningStylesheets="true" controller="VFC_DomainsController">
<apex:slds></apex:slds>
<apex:form>
<script>
var lexOrigin = '{!lexOrigin}';
window.addEventListener("message", function (event) {
if (event.origin === lexOrigin) {
var receivedfromLWC = event.data;
let outputLWC = document.getElementById('text-area-sfdc-lwc');
outputLWC.innerHTML = JSON.stringify(receivedfromLWC);
}
}, false );
function sendToLigntningWC(){
let lightningOrigin = '{!lexOrigin}';
let textAread = document.getElementById('text-area-sfdc');
let messgaeToLWC = {
message : textAread.value,
source : 'VisualForce Page'
}
window.parent.postMessage( messgaeToLWC, lightningOrigin );
}
</script>
<div class="slds-grid slds-gutters slds-p-around_medium">
<div class="slds-col">
<p>Message Send To LWC</p>
<input type="text" id="text-area-sfdc" />
</div>
<div class="slds-col">
<p>Message Recieved FROM LWC</p>
<div id="text-area-sfdc-lwc" class="slds-box" />
</div>
</div>
<button class="slds-button slds-button_outline-brand" onclick="sendToLigntningWC();return false;">Send to LWC</button>
</apex:form>
</apex:page>
window.postMessage() is a standard web API that is not aware of the Lightning and Locker service namespace isolation level. As a result, there is no way to send a message to a specific namespace or to check which namespace a message is coming from. Therefore, messages sent using postMessage() should be limited to non sensitive data and should not include sensitive data such as user data or cryptographic secrets.
Hi Amit,
I am not able to communicate between LWC and VFpage inside the Iframe. I used postMessage API with ‘*’ as a second parameter (Domain).
But nothing is happening on vfpage EventListener.
Can you please help me?
What is the error in the browser console?
Hi Amit
If this isnt the secure way for communication between lwc and VF then whats the alternative for secure connection? Is it LMS (lightning messaging service)?
Thanks
Lightning Message Service is the preferable way. However LMS won not work if the VF page is inside Iframe of any LWC or AURA in that case you have use this approach.