3

I am hosting a Lightning component inside of a Visualforce page, and I noticed the force:* events aren't working (ex: force:showToast and force:navigateToObjectHome). They work fine when the component is hosted in a Lightning Page or App. Debugging the JavaScript, the events seem to be firing correctly, but there aren't any listeners.

I followed the directions here for hosting a component in a VF page. I'm not sure what I'm doing wrong.

Host.page

<apex:page standardcontroller="CallReport__c">
    <!-- This page is a host page for the call report lightning component. -->
    <apex:includeLightning />

    <div class="slds">
        <div class="slds-grid slds-grid--align-center slds-p-around--small">
            <div class="slds-large-size--3-of-12"></div>

            <div class="slds-size--1-of-1 slds-large-size--6-of-12">
                <div id="callReport"></div>         
            </div>

            <div class="slds-large-size--3-of-12"></div>
        </div>
    </div>

    <script>
        var recordId = '{!callreport__c.id}';

        // c:callReport implements force:hasRecordId and requires the call report ID
        $Lightning.use("c:DepApp", function() {
          $Lightning.createComponent('c:callReport',
              { recordId : recordId },
              'callReport',
              function(cmp) {
                // do some stuff
              });
        });
    </script>
</apex:page>

DepApp.app

<aura:application access="GLOBAL" extends="ltng:outApp">
    <aura:dependency resource="c:callReport"/>
    <aura:dependency resource="markup://force:*" type="EVENT"/>
</aura:application>
11

After a few days of research, I've discovered Visualforce pages are hosted inside of an iframe when you switch to the Lightning Experience. Force events are handled by the main page, and events raised inside of the VF page can't escape the iframe. You can find out more here.

Fortunately, there's a work-around. Browsers provide a way to post messages between a page in an iframe and its host window. Lightning Out takes advantage of this capability to allow code in a Visuaforce host page to post events to the main page running Lightning. You just need to wire things up in the $Lightning.createComponent() method in the VF page.

The first step is to add the force namespace as a dependency in your dependency app. This will pull in the force events so you can fire them from your components.

depApp.app

<aura:application access="GLOBAL" extends="ltng:outApp">
    <aura:dependency resource="c:someComponent"/>
    <aura:dependency resource="markup://force:*" type="EVENT"/>
</aura:application>

The second step is to wire up listeners in your VF page to respond to these events by forwarding them to the host Lightning app. There are two ways you can do this. One is supported; the other is not.

sforce.one

Lightning Out exposes a number of navigation events via an object sforce.one. It is a wrapper around internal code that uses the browser's window.postMessage() function to forward events to the host page. For example, if you want to navigate to the Account object's home view, you could run the code below.

sforce.one.navigateToObjectHome('Account');

SfdcApp

While very useful, sforce.one doesn't expose all the possible force events. One of those not available is force:showToast. There is an object internal to Lightning Out called SfdcApp that exposes a way to raise events up to the host page. I'm pretty sure this is unsupported so use at your discretion.

SfdcApp.projectOneNavigator.fireContainerEvent('force:showToast', { message: 'Save Successful!' });

The final piece is to wire up listeners in the VF page to forward these calls to the host. There are two things to note in the code below. One, the target component, c:someComponent, implements force:hasRecordId and needs a record ID. I show how to get that from the VF page. Two, SfdcApp is unsupported as far as I know, so I created a helper function postEventToOneApp that takes a fallback action in case SfdcApp does not exist. This helps to future proof the code.

<apex:page standardcontroller="someComponent__c">
    <!-- This page is a host page for the someComponent lightning component. -->
    <apex:includeLightning />

    <div class="slds">
        <div id="someComponent"></div>
    </div>

    <script>
        var recordId = '{!someComponent__c.id}';

        $Lightning.use("c:depApp", function() {
            var attributes = {
                recordId: recordId
            };

            var targetElementId = 'someComponent';

            var postEventToOneApp = function(name, params, fallbackAction) {
                if (SfdcApp && SfdcApp.projectOneNavigator && SfdcApp.projectOneNavigator.fireContainerEvent) {
                    // Not officially supported by Salesforce
                    SfdcApp.projectOneNavigator.fireContainerEvent(name, params);
                } else if (fallbackAction) {
                    // Fallback if no Salesforce support of fireContainerEvent
                    fallbackAction();
                }
            };

            $Lightning.createComponent('c:someComponent', attributes, targetElementId,
                function(cmp) {
                    // Add two handlers. One for showing toasts and another to navigate to object home.
                    $A.eventService.addHandler({
                        event: 'force:showToast',
                        handler: function(event) {
                            postEventToOneApp('force:showToast', event.$params$, function() {
                                alert(event.$params$.message);
                            });
                        }
                    });

                    $A.eventService.addHandler({
                        event: 'force:navigateToObjectHome',
                        handler: function(event) {
                            postEventToOneApp('force:navigateToObjectHome', event.$params$, function() {
                                sforce.one.navigateToObjectHome(event.$params$.scope);
                            });
                        }
                    });
            });
        });
    </script>
</apex:page>
3
  • I turned this answer into a blog post which you can find at joebuschmann.com/…
    – Joe B
    Jan 16 '17 at 16:10
  • $A's eventService method seems like a private api because there's no reference of it in the sf docs. So things might break if sf decides/finds out that about it and removes it being publically accessed.
    – Praveen
    Jan 16 '17 at 16:32
  • How does this work with nested components? For example, a component which is inside the 'c:someComponent'. Do we need to bubble this up somehow?
    – Coova
    Feb 22 '19 at 17:30
2

They are not available currently and its a limitation of the platform .The docs clearly state that

enter image description here

This means you will need to write code to achieve these inside the visualforce page by examining if the event is undefined .

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.