Salesforce Lightning Lookup Component : Version 2

Lightning Yellow Pages

Version 2

This is Version 2 of the Salesforce Lightning Lookup Component. I had one of those, ‘Why didn’t I do it like that instead?‘ moments in the shower this morning. Essentially, it’s less code as I’ve done away with the rendering of the inner list components and plumped straight for throwing the Apex Controller results into an attribute and using an <aura:iteration> to render them. The original version is here for reference.

Note: There appears to be a bug with using <aura:if> in the AccountLookup component. If you experience difficulty with this, a workaround can be found here

On with the show …

I’ve been playing around with Salesforce Lightning components and one of the obvious things that seems to be missing is a UI component to lookup a record in a Salesforce custom object and return its Id.

This was really simple in Visualforce. You could just use an <apex:inputField> tag and bind it to an objects lookup field in your controller. Salesforce would take care of the rest. Lightning, however, is a different beast and centres around building client side apps or components in Javascript and utilising various API’s for dealing with any server side requests.

There’s quite a bit of code here so I’ve added it all to a github repository. (Links at the bottom)


There were a few things to consider:

  • It should work both within a desktop app and within the Salesforce Lightning mobile app.
  • It should be intuitive and have the look and feel of a lightning app.
  • It should be reusable, easily configured and plumbed into another component or app.


As I said, there’s quite a bit of code so before we begin, like all good cake recipes, let’s look at the finished result (6Mb Animated GIF file, please be patient while it loads):

Ligntning Lookup Component

Ligntning Lookup Component

Look & Feel (using the Salesforce Lightning Design System)

The look and feel comes from the Salesforce Lightning Design System or SLDS for short. This is a prerequisite, so you’ll need to install this before you press ahead and load any of the code into your org. I’m using SLDS092 but you should be able to a later version if you wish. You can install it into your org using the links here. This library provides a whole plethora of CSS, images to make your app and components look like the real deal. It also covers the UI design and how to structure your markup. For the lookup component I’ve used the markup described in the Lookups component of the SLDS. In some cases I’ve replaced the standard HTML tags with lightning framework markup such as <ui:inputText> instead of <input>. I’ve also had to create an <c:svg> component to use instead of the <svg> tag which is not supported in lightning. This is described in the Trailhead module: Build a Lightning App with the Lightning Design System.

Show me the code!

Lightning component bundle: svg

First things first, I need to show you the svg component. You need this so you can render the icons as nice scalable graphics without using the <svg> tag. Now this is pretty much lifted straight from the trailhead module so I’m not going to bore you with how it works. You can check out the trailhead yourself.

Here’s the component markup:


<aura:component >
  <aura:attribute name="class" type="String" description="CSS classname for the SVG element" />
  <aura:attribute name="xlinkHref" type="String" description="SLDS icon path. Ex: /assets/icons/utility-sprite/svg/symbols.svg#download" />
  <aura:attribute name="ariaHidden" type="String" default="true" description="aria-hidden true or false. defaults to true" />

It has a renderer defined which looks like this:


  render: function(component, helper) {
    //grab attributes from the component markup
    var classname = component.get("v.class");
    var xlinkhref = component.get("v.xlinkHref");
    var ariaHidden = component.get("v.ariaHidden");

    //return an svg element w/ the attributes
    var svg = document.createElementNS("", "svg");
    svg.setAttribute('class', classname);
    svg.setAttribute('aria-hidden', ariaHidden);
    svg.innerHTML = '<use xlink:href="'+xlinkhref+'"></use>';
    return svg;

Apex Controller: LookupSObjectController

We’re going to need an apex controller on the server side to query the database and return the matching records. To do this create the following aura enabled Apex class:

Apex class

 * Apex Controller for looking up an SObject via SOSL
public with sharing class LookupSObjectController 
     * Aura enabled method to search a specified SObject for a specific string
    public static Result[] lookup(String searchString, String sObjectAPIName)
        // Sanitze the input
        String sanitizedSearchString = String.escapeSingleQuotes(searchString);
        String sanitizedSObjectAPIName = String.escapeSingleQuotes(sObjectAPIName);

        List<Result> results = new List<Result>();

        // Build our SOSL query
        String searchQuery = 'FIND \'' + sanitizedSearchString + '*\' IN ALL FIELDS RETURNING ' + sanitizedSObjectAPIName + '(id,name) Limit 50'; 

        // Execute the Query
        List<List<SObject>> searchList = search.query(searchQuery);

        // Create a list of matches to return
        for (SObject so : searchList[0])
            results.add(new Result((String)so.get('Name'), so.Id));
        return results;
     * Inner class to wrap up an SObject Label and its Id
    public class Result
        @AuraEnabled public String SObjectLabel {get; set;}
        @AuraEnabled public Id SObjectId {get; set;}
        public Result(String sObjectLabel, Id sObjectId)
            this.SObjectLabel = sObjectLabel;
            this.SObjectId = sObjectId;

Notice the @AuraEnabled keyword. This allows the method to be called from the lightning framework. The lookup method constructs a SOSL query using the passed in search string and the objects API name. It returns a list of Results that contain a label and the Id of the matching SObjects.

Lightning component bundle: LookupSObject

On the client side we have a reusable Lightning component. The component contains attributes to specify the SObjects API Name, the label and plural label to apply along with an optional SVG image path and class from the SLDS. If the latter aren’t specified it uses a default icon, in this case a star.

There are two events registered: updateLookupIdEvent and clearLookupIdEvent. The former is fired when the user chooses an entry from the list, the latter is fired when the user clears a selection (via the cross icon.)

The component uses a keyup event to pass the user entered search string to a controller method. This in turn calls the server side Aura Enabled method.

The markup generated adheres to the SLDS Lookup component design. We’re going to use an <aura:iteration> to bind to the results of the Apex controller search. The matches attribute is where the results will get stored. You’ll notices inside the iteration is <a> element which uses the SObjects Id within the rendered element id so that we can easily capture it within the controller when the item is selected.

The component markup looks like this:


<aura:component controller="LookupSObjectController" >
    <!-- Required Scripts/Styles -->
    <!-- Salesforce Lightning Design System : -->
    <ltng:require styles="/resource/SLDS092/assets/styles/salesforce-lightning-design-system-ltng.css" />
    <!-- Attributes -->
    <aura:attribute name="sObjectAPIName" type="String" required="true" description="The API name of the SObject to search" />
    <aura:attribute name="label" type="String" required="true" description="The label to assign to the lookup, eg: Account" />
    <aura:attribute name="pluralLabel" type="String" required="true" description="The plural label to assign to the lookup, eg: Accounts" />
    <aura:attribute name="listIconSVGPath" type="String" default="/resource/SLDS092/assets/icons/custom-sprite/svg/symbols.svg#custom11" description="The static resource path to the svg icon to use." />
    <aura:attribute name="listIconClass" type="String" default="slds-icon-custom-11" description="The SLDS class to use for the icon." />
    <aura:attribute name="searchString" type="String" description="The search string to find." />
    <aura:attribute name="matches" type="LookupSObjectController.Result[]" description="The resulting matches returned by the Apex controller." />

    <!-- Events -->
    <aura:registerEvent name="updateLookupIdEvent" type="c:UpdateLookupId"/>    
    <aura:registerEvent name="clearLookupIdEvent" type="c:ClearLookupId"/>    
    <!-- Lookup Markup : See -->
    <div class="slds"> 
    <div aura:id="lookup-div" class="slds-lookup" data-select="single" data-scope="single" data-typeahead="true">
        <!-- This is the Input form markup -->
        <div class="slds-form-element">
            <label class="slds-form-element__label" for="lookup">{!v.label}</label>
            <div class="slds-form-element__control slds-input-has-icon slds-input-has-icon--right">
                <c:svg class="slds-input__icon" xlinkHref="/resource/SLDS092/assets/icons/utility-sprite/svg/symbols.svg#search" />
                <!-- This markup is for when an item is currently selected -->
                <div aura:id="lookup-pill" class="slds-pill-container slds-hide">
                        <span class="slds-pill slds-pill--bare">
                            <span class="slds-pill__label">
                                <c:svg class="{!'slds-icon ' + v.listIconClass + ' slds-icon--small'}" xlinkHref="{!v.listIconSVGPath}" />{!v.searchString}
                            <button class="slds-button slds-button--icon-bare" onclick="{!c.clear}">
                                <c:svg class="slds-button__icon" xlinkHref="/resource/SLDS092/assets/icons/utility-sprite/svg/symbols.svg#close" />
                                <span class="slds-assistive-text">Remove</span>
                    <!-- This markup is for when searching for a string -->
                    <ui:inputText aura:id="lookup" value="{!v.searchString}" class="slds-input" updateOn="keyup" keyup="{!}" />
            <!-- This is the lookup list markup. Initially it's hidden -->
            <div aura:id="lookuplist" class="slds-lookup__menu slds-hide" role="listbox">
                <div class="slds-lookup__item">
                    <button class="slds-button">
                        <c:svg class="slds-icon slds-icon-text-default slds-icon--small" xlinkHref="/resource/SLDS092/assets/icons/utility-sprite/svg/symbols.svg#search" />
                        &quot;{!v.searchString}&quot; in {!v.pluralLabel}
                <ul aura:id="lookuplist-items" class="slds-lookup__list" role="presentation">
                    <aura:iteration items="{!v.matches}" var="match">
                        <li class="slds-lookup__item">
                            <a id="{!globalId + '_id_' + match.SObjectId}" role="option" onclick="{!}">
                                <c:svg class="{!'slds-icon ' + v.listIconClass + ' slds-icon--small'}" xlinkHref="{!v.listIconSVGPath}" />{!match.SObjectLabel}

There is an associated controller to handle the events. The search method, as mentioned earlier, is called from the keyup event on the search. The select method is called when the user chooses a result from the rendered list. The clear method is called when the user clears the selected item by clicking the cross icon:


     * Search an SObject for a match
    search : function(cmp, event, helper) {

     * Select an SObject from a list
    select: function(cmp, event, helper) {
        helper.handleSelection(cmp, event);
     * Clear the currently selected SObject
    clear: function(cmp, event, helper) {

You’ll notice that each of the methods delegate to a helper. The helper does all the grunt work and looks like this:


     * Perform the SObject search via an Apex Controller
    doSearch : function(cmp) {
        // Get the search string, input element and the selection container
        var searchString = cmp.get('v.searchString');
        var inputElement = cmp.find('lookup');
        var lookupList = cmp.find('lookuplist');

        // Clear any errors and destroy the old lookup items container
        inputElement.set('v.errors', null);
        // We need at least 2 characters for an effective search
        if (typeof searchString === 'undefined' || searchString.length < 2)
            // Hide the lookuplist
            $A.util.addClass(lookupList, 'slds-hide');

        // Show the lookuplist
        $A.util.removeClass(lookupList, 'slds-hide');

        // Get the API Name
        var sObjectAPIName = cmp.get('v.sObjectAPIName');

        // Create an Apex action
        var action = cmp.get('c.lookup');

        // Mark the action as abortable, this is to prevent multiple events from the keyup executing

        // Set the parameters
        action.setParams({ "searchString" : searchString, "sObjectAPIName" : sObjectAPIName});
        // Define the callback
        action.setCallback(this, function(response) {
            var state = response.getState();

            // Callback succeeded
            if (cmp.isValid() && state === "SUCCESS")
                // Get the search matches
                var matches = response.getReturnValue();

                // If we have no matches, return nothing
                if (matches.length == 0)
                    cmp.set('v.matches', null);
                // Store the results
                cmp.set('v.matches', matches);
            else if (state === "ERROR") // Handle any error by reporting it
                var errors = response.getError();
                if (errors) 
                    if (errors[0] && errors[0].message) 
                        this.displayToast('Error', errors[0].message);
                    this.displayToast('Error', 'Unknown error.');
        // Enqueue the action                  

     * Handle the Selection of an Item
    handleSelection : function(cmp, event) {
        // Resolve the Object Id from the events Element Id (this will be the <a> tag)
        var objectId = this.resolveId(;

        // The Object label is the inner text)
        var objectLabel = event.currentTarget.innerText;

        // Log the Object Id and Label to the console
        console.log('objectId=' + objectId);
        console.log('objectLabel=' + objectLabel);
        // Create the UpdateLookupId event
        var updateEvent = cmp.getEvent("updateLookupIdEvent");
        // Populate the event with the selected Object Id
            "sObjectId" : objectId

        // Fire the event;

        // Update the Searchstring with the Label
        cmp.set("v.searchString", objectLabel);

        // Hide the Lookup List
        var lookupList = cmp.find("lookuplist");
        $A.util.addClass(lookupList, 'slds-hide');

        // Hide the Input Element
        var inputElement = cmp.find('lookup');
        $A.util.addClass(inputElement, 'slds-hide');

        // 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');


     * Clear the Selection
    clearSelection : function(cmp) {
        // Create the ClearLookupId event
        var clearEvent = cmp.getEvent("clearLookupIdEvent");
        // Fire the event;

        // Clear the Searchstring
        cmp.set("v.searchString", '');

        // Hide the Lookup pill
        var lookupPill = cmp.find("lookup-pill");
        $A.util.addClass(lookupPill, 'slds-hide');

        // Show the Input Element
        var inputElement = cmp.find('lookup');
        $A.util.removeClass(inputElement, 'slds-hide');

        // Lookup Div has no selection
        var inputElement = cmp.find('lookup-div');
        $A.util.removeClass(inputElement, 'slds-has-selection');

     * Resolve the Object Id from the Element Id by splitting the id at the _
    resolveId : function(elmId)
        var i = elmId.lastIndexOf('_');
        return elmId.substr(i+1);

     * Display a message
    displayToast : function (title, message) 
        var toast = $A.get("e.force:showToast");

        // For lightning1 show the toast
        if (toast)
            //fire the toast event in Salesforce1
                "title": title,
                "message": message

        else // otherwise throw an alert
            alert(title + ': ' + message);

The doSearch method takes care of calling the Apex controller and storing the results in the matches attribute.

The handleSelection method takes the selected entry and fires the updateLookupIdEvent before modifying the CSS classes to show the Lookup in its selected state.

The clearSelection method fires the clearLookupIdEvent before modifying the CSS classes to revert back to the search state.

The resolveId method splits the SObject Id from the end of the element id. This is used by the handleSelection method to obtain the SObjects Id. Remember, we hashed it in to the <a> elements id.

The displayToast is simply an error logging method that renders a toast popup in the Salesforce1 app or an alert in the browser.

Lightning event: UpdateLookupId

This is the event fired when the user selects an item from search results. It has a single attribute that gets set with the Id of the selected record. You’ll notice this is a component event rather than an application event. Be careful here if you are nesting your components you’ll only be able to handle it in the outermost component. Consider changing it to an Application event if this is an issue.


<aura:event type="COMPONENT" description="Update Lookup Id" >
    <aura:attribute name="sObjectId" type="Id" required="true" description="The Id of the selected SObject." />

Lightning event: ClearLookupId

This is the event fired when the user clears the selected item. It has no attributes. Again, you’ll notice this is a component event rather than an application event. Once again, be careful here if you are nesting your components you’ll only be able to handle it in the outermost component. Consider changing it to an Application event if this is an issue.


<aura:event type="COMPONENT" description="Clear the Lookup" />

Ok, great. Now can I see how you use it?

We’re going to wrap this up with an example Account Lookup component which you can plug straight into Salesforce1 via a Component Tab. Since the component implements flexipage:availableForAllPageTypes you could plug it straight into Lightning Builder if you wished. If you wanted to embed it in a Visualforce page you could do that as well. I won’t go into how to do that here but you’ll find details in the Lightning developer documentation.

Lightning component: AccountLookup

First lets look at the component markup. There’s an attribute to hold the Record Id. This is hooked up to the force:recordView component which is only rendered if an Id is present. We have two event handlers listening for the UpdateLookupId and ClearLookupId events. Note the names tie up to the registered events in the LookupSObject component. These event handlers hook up to the components controller via the handleAccountIdUpdate and handleAccountIdClear methods. The LookupSObject component markup takes the labels and icon resources as attributes.


<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes">
    <!-- Attributes -->
    <aura:attribute name="recordId" type="Id" description="The current record Id to display" />
    <!-- Event handlers -->
    <aura:handler name="updateLookupIdEvent" event="c:UpdateLookupId" action="{!c.handleAccountIdUpdate}"/>
    <aura:handler name="clearLookupIdEvent" event="c:ClearLookupId" action="{!c.handleAccountIdClear}"/>
    <!-- Lookup component -->
    <c:LookupSObject label="Account" pluralLabel="Accounts" sObjectAPIName="Account" listIconSVGPath="/resource/SLDS092/assets/icons/standard-sprite/svg/symbols.svg#account" listIconClass="slds-icon-standard-account" />

    <!-- Record view -->
    <aura:if isTrue="{!v.recordId!=null}">
        <force:recordView recordId="{!v.recordId}" />

Now lets take a look at the controller. You’ll see the handleAccountIdUpdate method gets the Id from the Event and updates the recordId attribute. The handleAccountIdClear simply nulls the recordId attribute. And that’s it, simples.


     * Handler for receiving the updateLookupIdEvent event
    handleAccountIdUpdate : function(cmp, event, helper) {
        // Get the Id from the Event
        var accountId = event.getParam("sObjectId");

        // Set the Id bound to the View
        cmp.set('v.recordId', accountId);

     * Handler for receiving the clearLookupIdEvent event
    handleAccountIdClear : function(cmp, event, helper) {
        // Clear the Id bound to the View
        cmp.set('v.recordId', null);

If you want to plug the component into Lightning builder, you’ll need to include a design in the component bundle. Since there are no attributes that need passing into our Account Lookup component it just looks like this:



Creating the Lightning Component Tab

  • Navigate to: Setup->Create->Tabs
  • Click on New Lightning Component Tab.
  • Select the AccountLookup from the dropdown list.
  • Enter a Tab Name.
  • Choose a Tab Style.

… and you’re nearly good to go.

All that remains is to add it to the Salesforce1 Navigation.

Navigate to: Setup->Mobile Administration->Salesforce1 Navigation

Add the Component tab to your navigation, fire up the Salesforce1 mobile app and give it a whirl!


All the code samples assume the default namespace. If you have your own namespace you’ll need to change the references from c:mything to mynamespace:mything.

If you are using a different version of the SLDS, you’ll need to change the paths in the code from SLDS092 to match your version.

Code and Resources:

The code is based on Version 0.92 of the SLDS:

Github repository containing all the above code (Note: this includes some updates as per the discussions in the comments thread):

Links and stuff:

Salesforce Lightning Design System

Trailhead: Build a Lightning App with the Lightning Design System



Author, brainstormer, coder, dad, explorer, four chord trickster, gig goer, home worker, inquisitor, joker, knowledge seeker, likes: marmite, note scribbler, opinionator, poet, quite likes converse, roller skater, six music listener, tea drinker, urban dweller, vinyl spinner, word wrangler, x-factor hater, Yorkshireman (honorary), zombie slayer (lie).

Tagged with: , , , ,
Posted in apex, code,, lightning, salesforce
28 comments on “Salesforce Lightning Lookup Component : Version 2
  1. Varun says:

    Hello Tony – Gr8 article you have together here, its an awesome control. can you tell me if we can use the component multiple times for different objects on same page? I’m looking to write a component with two Lookup inputs, one for Account and another for Contact. I’m confused if I need multiple Event handlers in this case of just the ones you mentioned in your article?

    My intention is to update field like this:
    var recId = event.getParam(“sObjectId”);
    cmp.set(‘[CustomObj].Account__c’, recId);

    var recId = event.getParam(“sObjectId”);
    cmp.set(‘[CustomObj].Contact__c’, recId);

    But this seems like I need two handlerFunctions each for Account and Contact?

    • Tony Scott says:

      As the example stands you’re going to have some confusion if you use multiple instances of the component within another component as you won’t know which one triggered the event.

      So a few small code changes are required:

      Firstly, add a new attribute to the LookupSObject component, we’ll use this to pass in something to identify the instance of the component:

      <aura:attribute name="instanceId" type="String" required="true"
      description="An id to identify the instance the component" />

      Now add a similar attribute to the UpdateLookupId event:

      <aura:attribute name="instanceId" type="String" required="true" description="The id to identify the instance the component" />

      In the LookupSObjectHelper method: handleSelction make the changes to where the event is fired to look like:

      // Get the Instance Id of the Component
      var instanceId = cmp.get('v.instanceId');

      // Populate the event with the selected Object Id
      "sObjectId" : objectId, "instanceId" : instanceId

      // Fire the event;

      Now when we instantiate the component we can add the instance id. We’d modify the AccountLookup component like this:

      <c:LookupSObject label="Account" pluralLabel="Accounts" sObjectAPIName="Account" instanceId="MyAccount"

      Now we can add some logic in the AccountLookupController handleAccountIdUpdate method to check the externalId and fork the code accordingly:

      * Handler for receiving the updateLookupIdEvent event
      handleAccountIdUpdate : function(cmp, event, helper) {
      // Get the Id from the Event
      var accountId = event.getParam("sObjectId");

      // Get the Instance Id from the Event
      var instanceId = event.getParam("instanceId");

      // Determine the instance Id of the component that fired the event
      if (instanceId == "MyAccount")
      // Set the Id bound to the View
      cmp.set('v.recordId', accountId);
      console.log('Unknown instance id: ' + instanceId);

  2. varunchaddha says:

    Thanks Tony – I got that to working with your immense help 🙂 … Just one more thing, when I try to load the Component with values preset using button click, it does not show the item preselected?

    I’m trying to preload the Lookup field with a value, now I assign the Lookup sobject Field with correct value and when I retrieve the field value I get the value back, but the Lookup component still shows up as Empty input.

    Any insights on, where I should look into the code to make it work. I Intend to load the Form with lookup value preselected in the lookup component OR preselect the value on a Button click. Right now I have to search and then select the value manually.

    • Tony Scott says:

      Sorry, I’ve just got back after getting caught in the atrocities in Paris. I’ll post a reply when I’ve got my head in order. Please bear with me.

      • varunchaddha says:

        Hey not to worry. I appreciate all your help anytime. Didn’t knew you were caught up in that, the incident itself is horrifying and is all over the news. My prayers are with the families of the lost souls.

  3. Thanks for you blog post. I’ve made some changes to make it work in a VF page + removed the need to have nested components if you were interested.

  4. Here is the code, cleaned up and forked. I have removed some name space stuff, so it’s possible that it may have a couple of errors.

  5. vipul says:

    It is working good but I have encountered 1 error where selecting a value from the displayed list and click on cross sign to remove the selected item and again search for a string and select a new value, value is not selected and I got an error at the bottom of page saying: “rerender threw an error in ‘markup://aura:expression’ : Cannot read property ‘childNodes’ of null”
    Do you have any idea what is it all about?
    I appreciate all the good work

  6. vipul says:

    Hi Dear,
    I am getting this error when searching second time :
    “”Something has gone wrong. Cannot read property ‘childNodes’ of null. Please try again.””
    Do you have any clue ???
    Thanks for helping me out …

  7. Vipul says:

    Thanks for the reply, Ya I missed it.
    What do you mean by “Removing the and from the AccountLookup.cmp “? I did not understand. If possible you can post something here ….

    • Tony Scott says:

      Sorry, WordPress stripped out the html tags. Should read: Removing the <aura:if> from the AccountLookup.cmp prevents the error. For now suggest wrapping in a DIV and using CSS to hide/show it until Salesforce fix they framework.

  8. Vipul says:

    Thanks for your help , I tried it and at least that error is not coming now…

    • Tony Scott says:

      I was kinda hoping Salesforce might fix it and it would go away on its own as it clearly looks like a platform bug. If I get a chance later I’ll post up the CSS workaround.

  9. Vipul says:

    yes I am trying something like this :

    but I am stuck , record is not visible

  10. Vipul says:

    opps , It did not came up ..
    But it is working now using CSS and Styling

  11. […] For those of you having tried to implement the Lightning Lookup from my post: […]

Comments are closed.

About Me
Product Services Developer at:
All views expressed here are my own. More about me and contact details here.
Please sponsor me …
My wife and I are running the Great North Run to support The Nick Alexander Memorial Trust

If you've found my blog useful, please consider sponsoring us. Sponsor me on BT MyDonate

Enter your email address to follow this blog and receive notifications of new posts by email.

Copyright (there isn’t any, feel free to reuse!)

To the extent possible under law, Tony Scott has waived all copyright and related or neighboring rights to Examples and Code Samples. This work is published from: United Kingdom.

%d bloggers like this: