function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Nikhil Garg 17Nikhil Garg 17 

@Remote action in VF page and LWC Context

Hi all,

I'm trying to call VF page from LWC using window.Postmessage() and in vf page listening event using Window.addEventListner(). In vf page I'm accessing the apex class method using @RemoteAction. so when I'm directly previewing the VF Page then apex method is calling but when I'm calling vf page from lwc and then invoking remote action then it gives me error "No controller found".

here is my vf page code

<apex:page controller="dataBackup">
    <apex:remoteObjects />
     <script type="text/javascript"></script>
    <script>
       
     window.addEventListener("message",function(event){
         console.log(event);
             downloadCSVAsZip();
},false);

function downloadCSVAsZip(){
         Visualforce.remoting.Manager.invokeAction(
            '{!$RemoteAction.dataBackup.getObj}',
            function(result, event){
                console.log('test');
                console.log(event);
            },
            {escape: true}
                );
    }

</script>
    
    <apex:form >
        <apex:pageblock title="Zip From Dummy CSV>
            <input type="button" onclick="downloadCSVAsZip(this);" value="Download" />
        </apex:pageblock>
    </apex:form>
</apex:page>

Kindly give any solution for this.

Thanks!

thesunloverthesunlover

For the LWC 2 VF communication to work properly you should 1st figure out where the correct postMessage function stays: 

        const iframe = component.find(iframeAuraID).getElement();
        if (iframe.tagName.toLowerCase() !== 'iframe') {
            throw Error('Technical error: Can\'t detect the iframe');
        }
        this.vfMessageReceiver = this.findPostMessageReceiver(iframe.contentWindow);

    /**
     * @name findPostMessageReceiver
     * @description A helper function to find where the correct Window object is located
     * @param {Window} current - the window object of the iframe
     * @returns
     */
    findPostMessageReceiver: function (current) {
        if (current.postMessage.name === 'postMessage') {
            return current;
        } else {
            let i = 0;
            while (i < (current.window && current.window.length)) {
                if (current.window[i].postMessage.name !== 'postMessage') {
                    return current.window[i];
                }
                i += 1;
            }
        }
        return current;
    },
Then once you have it you could call the sendMessage function:
    /**
     * @name sendMessage
     * @description Sends a message to VF, interop platform & 3rd party apps.
     * @param {Object} message
     */
    sendMessage: function (message) {
        this.vfMessageReceiver &&
        this.vfMessageReceiver.postMessage.call(
            this.vfMessageReceiver,
            message, '*'
        );
    },
Note that, you can't specify a different location rather than '*' and the manual checks on the listener should be added.

Then for the VF 2 LWC. It looks like we could provide the parent origin and in this case we won't need to do manual checks:

let msgHolder;
function getMessageHolder() {
    let current = window.parent;
    if (!current.length) {
        return current;
    }
   
    for (let i=0; i < current.length; i++) {
        if (current[i] !== window) {
            return current[i].parent;
        }
    }
    return current;
}
/**
 * Sends a message to the LWC
 * @param {Object} message
 */
function sendMessage(message) {
    if (!msgHolder) {
        msgHolder = getMessageHolder();
    }
    msgHolder.postMessage.call(msgHolder, message, window.location.ancestorOrigins[0]);
}