This post shows how to embed a Lightning Component in a Visualforce page, using a simplified technique and incorporating a callback.
A good reason to embed a Lightning Component in a Visualforce page is so that you can benefit from code reuse for a complicated and repeated feature. In my example, I’m using a Lookup Component that I built, as the lookup is used in several Visualforce pages around the the system.
Currently there is not a good published way to communicate between a Lightning component and the parent page.
I’m going to show a technique that makes this communication quick and easy.
First, the Lookup Component itself. The code for it is on Github here. This code is derived (but is significantly changed) from this very useful blog post here: melted wires blog. I have removed the nesting of components, removed the need to use Events to communicate between nested components, added init code, filters and lastly a callback to get the results back into the VisualForce page.
To construct the Component, 4 elements are needed.
- An App Container (maybe call it LookupContainer)
- A Component (Lookup)
- A Controller (LookupController)
- A Controller Helper (LookupHelper)
The App Container simply contains 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"/>
I’m going to assume that you are using the lightning design system – if you are not, this component will look very out of place with all the regular styled visualforce.
Here is a small sample page with the component:
https://gist.github.com/rapsacnz/e813550f8fcc4e0357c2
Inside the createComponent call, you supply:
- The recordId (the currently displayed lookup on the contact).
- An API name so that the lookup knows what object to look for name matches on.
- The type of icon to display
- A filter to apply (perhaps only accounts with a certain name or a certain type)
- And finally a callback.
In the callback, you can add any javascript function you like – perhaps a call to an actionFunction or some sort of event handler. This is useful if you don’t want to immediately save the results of your selection to the record (which is possible – this requires some changes to the controller that drives the lookup).
Even if you were to save the record using the Lookup controller, you might still want to call some callback here on the page – to change it’s state or show acknowledge that a save has occurred.
Initializing 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);
}
Using 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});
}
As you can see, this is a very simple call (in this case passing back a javascript result – however you can pass anything), that adds a lot of power to the component. Now that you have two way communication from page to component and back, the component truly becomes part of the page and can participate in any state changes or notifications that you desire.
Happy Coding!
Moving Forward in Lightning
Have questions? Let us know in a comment below, or contact our team directly. You can also check out our other posts on customizing your Salesforce solution.
how do you enable scrolling on an embedded lightning component inside of a visualforce page?
I think you would need to wrap your scrollable content in a ui:scrollerwrapper component and use that in the visualforce page. You may need to specify an explicit height on the div in the visualforce page to get the scroller to show. Let me know how it goes.
I am trying to create a Lightning Component where, i cant select multiple users,roles and profiles with assignes_to field. Can someone help me.
Can you post the entire example?