Problem
In Lightning Experience, a textbox is rendered for lookup and master-detail fields. After you start typing in a few characters, Salesforce starts showing matching records in an autocomplete list. This assumes you know which parent record you’re looking for. If the parent object has a manageable number of records, say 200 or less, it would be better to have a dropdown list of all the choices.
Solution: Lookup Dropdown Lightning Component
This new “Lookup Dropdown” Lightning component is a generic, record detail custom component. An admin drags it onto a record detail page, configures the “Lookup Field” to use and the button label, and the component will automatically determine what the parent object is, query all the records in it and dynamically generate a dropdown list with all the queried records with each item showing the record’s name for the label and its Salesforce record Id as the value. When someone clicks the button, the lookup or master-detail field of the given record is updated with the chosen dropdown value.
Component Markup
The component starts with two main attributes:
Controller: This defines the LookupDropdownController Apex class that it will interact with for getting the various parent records and saving the lookup or master-detail value back to the database in Salesforce. The internals of this Apex class will be shown later.
implements: This defines which interfaces the component implements and the behavior it supports. In this instance, the flexipage:availableForRecordHome interface tells us that this component can be shown on record detail pages, the force:hasRecordId interface instructs the Lightning component framework to give the component the current record id this component is on via the “RecordId” attribute that is automatically inherited, and the force:hasSObjectName interface gives the component the SObject name of the record that this component is on in the sObjectName attribute that is automatically inherited.
The attribute elements describe the “variables” that can be used in the javascript component controller or bound to via the component’s designer attributes. The two primary attributes are the lookupField and the submitButtonLabel. The lookupField is the API name of the lookup or master-detail on the current record that should have its parent object’s records queried and dynamically generated into the dropdown list. This is done at design time when editing the page layout. The submitButtonLabel describes the text to show on the button and its default value is “Update”. This is also configurable at design time when editing the page layout.
The aura:handler elements are events that the component registers for. The “init” event says to invoke “initLookupDropdown” component controller method when the component is first initialized. The “force:refreshView” event is invoked when the page that this component is on is refreshed and it instructs the component to invoke its “initLookupDropdown” method again. It does this so that if the lookup field is updated elsewhere on the page, the component will automatically update itself to show the latest value.
The aura:If block is there for diagnostic purposes and is hidden by default. If someone selects “Yes” for debugEnabled, the components attributes are outputted so one can more easily debug the issue.
The lightning:select element is a Lightning base component that renders as a dropdown list. Its values and label are dynamically updated when the component is initialized when the page opens.
The lightning:button saves the chosen dropdown value back to Salesforce by invoking the “saveLookupValue” function on the component’s controller.
Design Attributes
The designer file lets one declare which attributes at design time are bound to the component and drive various behavior. This component has three configurable options:
lookupField: Lets one give the API name of the lookup or master-detail of the record this component is on.
submitButtonLabel: The label to display on the “Save” button of this component. The default value is “Submit” but can be overridden.
debugEnabled: This “Yes” or “No” dropdown list allows one to show additional diagnostic information when “Yes” is selected. This defaults to “No”.
Component Controller
The component controller is comprised of Javascript methods and functions that update the component’s view and interact with a Salesforce controller if needed to do various operations. This controller has two functions:
initLookupDropdown
This function invokes the apex controller’s “loadLookupDropdown” function and passes it the sObjectName, lookupField, and recordId. It passes back the selectableRecords that are then dynamically bound to the dropdown list and sets the dropdown list’s label.
saveLookupValue
This function invoke’s the apex controller’s “saveLookup” function to save the selected dropdown list value to the given lookup or master-detail field on the current record. If the call is successful, the force:refreshView event is fired so that all the components on the page are refreshed. This is necessary in case this field is shown on any other components on the page and its value is updated to reflect the new value.
As you can tell from the javascript, an asynchronous AJAX call is being invoked and then a callback function is used to process the response of the service invocation.
LookupDropdownController Apex Controller
In order for a method or function on an apex class to be invocable from a Lightning component’s controller, it must be static and decorated with the @auraEnabled attribute. The controller has two primary functions:
loadLookupDropdown
This function takes in the objectName, lookupField, and recordId and dynamically determines the lookupField’s label to show, dynamically queries all the records on the parent object, and gets the lookup value of the current value on the record. This information is passed back to the component to dynamically build the dropdown list.
saveLookup
This function saves the selected lookup value in the dropdown back to the given record.
Benefits
- This component can be used on any record of any object.
- It can be placed anywhere on the record’s page layout.
Considerations
- This shouldn’t be used for a parent object that has say more than two hundred records in it. Searching through too many records would be burdensome.
- Only the name is shown on the dropdown list’s items.
- This took about 8 hours to develop and more coding than I was expecting so be mindful of how long a component may take to implement.
What was your first Lightning component development experience like?
Thank you for this GREAT tool. In addition to being useful in itself, it’s a good how-to primer for relative Lightning Component newbies like me.
I’ve used it in a couple of spots as designed by you and it works very well. However, I also tried to modify it so that I could embed it in my own component, and I’m having a bit of difficulty. I commented out the built-in “submit button (because I’m using my own, separate button on my form. I’m calling it from the markup of my component (with a tag) and I set default value for the “lookupField” attribute in the markup (to field name of my lookup field) and did the same for the lookupField design attribute.. When I run it, your component instantiates in my component, but I get an “Options are loading.” message in the dropdown which never resolves. Can you offer me any suggestions as to what might need to be tweaked?
Again, thanks for this great tool.
Hey Lee,
This component was originally designed only for being put on a record home page. By being put on a record home page, it automatically has the “sObjectName” and “recordId” attributes populated from LEX. Those attributes are needed so that the Apex Controller knows which object the lookup field is on and the current value of the lookup for the desired record.
With that said, to use this within another custom component, you have to specify the “sObjectName” and “recordId” attributes manually too in addition to the lookup attribute. The sObjectName is the API name of the standard or custom object and the recordId is the 15 or 18 alphanumeric id of the record, if there is one, that you’re editing. If there is no record, you may have to tweak some other code because I suspect you may encounter a null reference error using the code as it stands today but it shouldn’t be too hard to adjust.
If you need anything else, let me know.
Happy Coding,
Luke
Instead of starting from scratch, I found this and was able to modify it in less than an hour to have a dynamic filter, using another field on the record as a comparison. Seems to be working perfectly! Thank you.
Kevin,
Glad it helped! This component and my other prototypes are available on GitHub at https://github.com/lfreeland/MetilliumLightningPrototypes. Would love a pull request for this enhancement, if you’re allowed to share.