[SalesForce] Unable to subscribe to Platform Event Notifications in a Lightning Component via Lightning Out

The ultimate objective is to detect if a visualforce page (or the lightning component embedded within it) is already being viewed/used by a user when someone else opens it and subsequently restrict edit'ability as needed.

I started off looking into Salesforce Platform Events, but am open to better approaches.

Referencing Platform Event Notifications in a Lightning Component and lightning:empApi documentation here, I was able to publish/subscribe to platform events in Lightning Experience without issues.

However, when I dropped the same component onto a Visualforce page (Lightning Out), empApi.subscribe() in ControllerJS (please see below) doesn't get called at all, resulting in subscription service to not work…? I don't see exceptions anywhere either. Is this a bug or could I be missing something?

I was hoping to utilize the stateful nature of lightning component UI and pair it with init and change events in component to capture and restrict access based on who is viewing the page.

Because I couldn't get above to work, another way would be to subscribe to platform event notifications with Apex Triggers, BUT wouldn't that require me to create a separate object to track who opened the page first and surround my logic around that object? Now I am asking myself, why even use platform events if that is the case (and make do with just a custom object to log who is viewing the page)? So, more importantly, are platform events the right solution in this situation?

Publisher Class

public with sharing class PlatformEvents {

    public static String publish(String targetId, String entityName, String entityType){

        String returnMessage = null;

        if(String.IsNotBlank(targetId)){
            try{
                List<Platform_Event_Test__e> events = new List<Platform_Event_Test__e>();
                events.add(new Platform_Event_Test__e(Target_Id__c = targetId, Entity_Name__c = entityName, Entity_Type__c = entityType));

                system.debug(logginglevel.ERROR, '-------------------------------> PlatformEvents publish() 1: ' + targetId + ' ' + entityName + ' ' + entityType);
                List<Database.SaveResult> publishResult = EventBus.publish(events);

                for(Database.SaveResult result : publishResult) {
                    if(result.isSuccess())
                        returnMessage = 'SUCCESS';
                    else {
                        returnMessage = 'ERROR(S): ';
                        for(Database.Error error : result.getErrors())
                            returnMessage += (error.getStatusCode() + ' - ' + error.getMessage());
                    }       
                }
            } catch (Exception ex) {
                returnMessage = 'ERROR(S): ' + ex.getMessage() + ' - ' + ex.getStackTraceString();
                system.debug(logginglevel.ERROR, '-------------------------------> PlatformEvents publish(): ' + returnMessage);
            }
        }

        return returnMessage;
    }

}

Invoking Publisher like so (via Execute Anonymous in Developer Console):

String returnMessage = PlatformEvents.publish('0081A000117cQiI', 'TEST', 'TEST');
system.debug(logginglevel.ERROR, '------------> ' + returnMessage);

Subscriber component (PlatformEventSubscriber.cmp)

<aura:component implements="flexipage:availableForAllPageTypes" access="global" >

    <aura:handler name="init" value="{!this}" action="{!c.init}" />
    <aura:attribute name="targetId" type="String" />
    <lightning:empApi aura:id="empApi" />   

    targetId = {!v.targetId} <!-- This value prints in Lightning Experience, but not in a Visualforce page via Lightning Out -->

</aura:component>

Subscriber component ControllerJS:

    ({
        init : function(component, event, helper) {
            try{
                var empApi = component.find("empApi");

                console.log("-------------> In c:PlatformEventSubscriber.cmp controller init ");

                var errorHandler = function (message) {
                    console.log("-------------> Received error ", message);
                }.bind(this);
                empApi.onError(errorHandler);

                var channel = '/event/Platform_Event_Test__e';
                var replayId = -1;
                var callback = function (message) {
                    component.set("v.targetId", message.data.payload.Target_Id__c);
                }.bind(this);

                empApi.subscribe(channel, replayId, callback).then(function(value) {
                    //This line executes in Lightning Experience, but not in a Visualforce page via Lightning Out
                    console.log("-------------> Subscribed to channel " + channel);
                });
            } catch(err){
                console.log('-------------> init ERROR: ' + err + ' ** MESSAGE: ' + err.message + ' ** STACK: ' + err.stack);
            }
        }
    })

For your convenience, lightning dependency app to host Lightning Components in Visualforce page (LightningComponentApp.app):

<aura:application access="GLOBAL"  extends="ltng:outApp">
    <aura:dependency resource="c:PlatformEventSubscriber" />
</aura:application>

For your convenience, visualforce page (LightningComponentContainer.vfp):

<apex:page title="Platform Events Test" sidebar="false">
    <apex:includeScript value="/lightning/lightning.out.js"/>  

    <div id="contentDiv"/>

    <script>
        $Lightning.use("c:LightningComponentApp", function() {
            $Lightning.createComponent("c:PlatformEventSubscriber",
                {},
                "contentDiv",
                function(cmp) {
            });     
        });
    </script>
</apex:page>

Best Answer

lightning:empApi is not supposed to work using VF page/Lightning out.

If you see the Experience section in the documentation of this component, you will find as below. Only those components which has Lightning Out / Visualforce listed in the Experience section will work in that experience.

Experience

Lightning Experience, Salesforce Mobile App

enter image description here

Related Topic