Incorporate A Lightning Component within A Visualforce Page

A compelling rationale for embedding a Lightning Component within a Visualforce page lies in leveraging code reusability, especially for complex and frequently used functionalities. In my case, I’ve implemented a Lookup Component that serves across multiple Visualforce pages in the system.

Presently, there isn’t a widely accepted method for seamless communication between a Lightning component and its parent page.

I’ll be demonstrating an approach that streamlines this communication process.

Firstly, let’s delve into the Lookup Component itself. The code for it resides on Github here. This code originates (albeit substantially modified) from a highly informative blog post available here: melted wires blog. I’ve made alterations by eliminating component nesting, eliminating the necessity for Events to facilitate communication among nested components, introducing initialization code, filters, and importantly, a callback mechanism to relay results back into the Visualforce page.

Building the Component entails four essential elements:

  • An App Container (potentially named LookupContainer)
  • A Component (Lookup)
  • A Controller (LookupController)
  • A Controller Helper (LookupHelper)

The App Container essentially incorporates a dependency to the Component:

<aura:application access="global" extends="ltng:outApp">
    <aura:dependency resource="c:Lookup"/>
</aura:application>

The Component contains all the rest of the markup, including defining attributes and handlers, css and general HTML structure (this is just part of the file – for the rest, refer to the link above):

<aura:component controller="LookupController" access="global" >
    <ltng:require styles="/resource/SLDS0110/assets/styles/salesforce-lightning-design-system-ltng.css" />
    <aura:handler name="init" value="{!this}" action="{!c.init}"/>
    <aura:attribute name="lookupAPIName" type="String" description="Name of the lookup field ie Primary_Contact__c" access="global"/>
    <aura:attribute name="sObjectAPIName" type="String" required="true" description="The API name of the SObject to search" access="global"/>
    <!--etc -->

To include this component in the page, don’t forget to add the lighting.out script include:

<apex:includeScript value="/lightning/lightning.out.js"/>

Let’s assume you’re utilizing the Lightning Design System—if not, this component might stand out significantly amidst the conventionally styled Visualforce elements.

Below is a concise snippet showcasing the component:

Sample Page on GitHub Gist

Within the ‘createComponent’ call, you’ll provide:

  • The ‘recordId’ (presently viewed lookup on the contact).
  • An API name for the lookup to identify the object for name matches.
  • The icon type to display.
  • A filter for application (like specific account names or types).
  • Lastly, a callback function.

In the callback, you have the freedom to integrate any JavaScript function—perhaps an invocation of an ‘actionFunction’ or an event handler. This flexibility is useful, particularly if you’re hesitant to immediately save the selection results to the record (a possibility that requires alterations in the driving controller of the lookup).

Even if you opt to save the record using the Lookup controller, incorporating a callback here on the page can be advantageous—such as altering its state or signaling the occurrence of a successful save.

Setting up the lookup:

When the page first loads, it needs to retrieve the name associated with the supplied id (if any). To do this, in the component, you call an action on the controller like this:

<aura:handler name="init" value="{!this}" action="{!c.init}"/>

The init action calls the init method on the controller which inits the lookup and also finds the related name of the lookup id on the record and displays it:

init : function(cmp, event, helper){
      try{
        //first load the current value of the lookup field
        helper.init(cmp);
        helper.loadFirstValue(cmp);
 
        }catch(ex){
          console.log(ex);
        }
      }

In the helper, the real work gets done:

loadFirstValue : function(cmp){
 
        var action = cmp.get('c.getCurrentValue');
        var self = this;
        action.setParams({
            'type' : cmp.get('v.sObjectAPIName'),
            'value' : cmp.get('v.recordId'),
        });
 
        action.setCallback(this, function(a) {
            if(a.error && a.error.length){
                return $A.error('Unexpected error: '+a.error[0].message);
            }
            var result = a.getReturnValue();
            cmp.set("v.searchString", result);
 
            if (null!=result){
              // Show the Lookup pill
              var lookupPill = cmp.find("lookup-pill");
              $A.util.removeClass(lookupPill, 'slds-hide');
 
              // Lookup Div has selection
              var inputElement = cmp.find('lookup-div');
              $A.util.addClass(inputElement, 'slds-has-selection');
            }
        });
        $A.enqueueAction(action);
    }

Implementing the Callback:

The callback is a simple Javascript function that is passed to lightning. As lightning does not currently provide an attribute of function type, a string type is used here. Javascript being as it is, you can get away with this sort of naughtiness

The callback is defined here:

<aura:attribute name="callback" type="String" access="global"/>

Inside the controller, it is invoked like so:

var func = cmp.get('v.callback');
if (func){
    func({id:objectId,name:objectLabel});
}

This straightforward call, in this instance returning a JavaScript result—though flexible enough to transmit any data—provides significant capabilities to the component. With bidirectional communication established between the page and the component, the component seamlessly integrates into the page’s functionality, enabling participation in desired state changes or notifications.