In this blog post series, we will break down the development of a Custom Lookup Field Lightning Web Component (LWC) from scratch. We will cover each step in detail, like debouncing the server call to fetch records, event bubbling to close the dropdown when clicking outside the component. By the end of this series, you’ll have a fully functional lookup field component ready for your Salesforce projects.
Before we dive into the making our hands dirt, let’s declare the variables that we are gonna use for this component.
Explanation:
valueLabel needs to be sent from the parent component, which would be displayed as the selected record label in this component.
We would be creating a custom metadata for the each lookup type, hence we have declared the lookupName variable as a API property and this needs to be sent from the parent.
Define HTML Template
Lets write the HTML for label, input element, icons and drop down container with the associated properties and event handlers.
Create Custom Metadata
Create one custom metadata object with the following Object Name - SC_Custom_Lookup_Property. This custom metadata records will have these custom fields to store the Object API Name, Field API Name and Condition which we would use in the apex class to query the records.
Fetch Lookup Records
Now its time to dive into building methods to fetch records based on the custom metadata which we have created.
Build Apex Method
Lets define a wrapper class for our lookup record.
The apex method fetchLookupRecords will accept three arguments
searchString - the input whiich end user provide to search the record
filterValues - comma separated values for dynamic update of condition in the metadata
and returns List<LookupRecordWrapper>
Note: filterValues will be covered in Part-2 of this series
Explanation:
Line 7 - 17: Building the query string based on metadata record and the searchString.
Line 24 - 32: Iterating over the fetched records, and adding it to the lookupRecordList.
Line 30: Will be implemented with another apex method.
Helper Apex Method
The apex method getLabel will accept two arguments
record - sObject which was queried.
fieldName - nested field separated with dots. Eg: Account.Name
and returns String
To start, we’ll split fieldName using ’.’ as the separator and store the resulting values in fieldList.
If fieldList contains more than one element, it indicates that there’s a nested sObject structure. We’ll then iterate over fieldList to navigate through these nested sObjects from the record, finally retrieving and returning the string value.
If fieldList contains only one element, we can simply return the string representation of the value from the record.
Now lets, update the placeholder label with the method which we just built.
Write Javascript method
Let’s import the apex method, which we built in the lookupField component.
We will imperatively call the fetchLookupRecords, but with debounce. Does it seems like a new word? Lets dig deeper.
Why debounce?
Debouncing helps control how often a function is called when multiple events, like typing, happen quickly in a row. Without debounce, every keystore would trigger a server request, which could overload the server.
Here’s how we’ve added debounce for the imperative server calls:
We use setTimeout to wait 500 milliseconds after the user stops typing before actually calling the server.
We use clearTimeout to stop any ongoing timeout, so if the user keeps typing, we don’t send a new request every time.
We handle the loading state with this.loading property.
This way, we only make a server call once the user pauses for a moment, improving performance and making the app faster.
Handle Toggle Dropdown
We achieve opening and closing of the dropdown by leveraging event bubbling. There might be other ways too, like by stopping the event propagation, but I prefer this way as it seems easy to understand and maintain.
Explanation:
Event Bubbling is a mechanism where an event propagates up from the innermost element to the outer elements. We listen for a click event on the entire document and decide whether to close the dropdown based on where the click originated. More on event bubbling - Geeksforgeeks (Event Bubbling).
If the click happens inside the component, we prevent the dropdown from closing. If it happens outside, we hide the dropdown. This is controlled via the clickedOnComponent flag.
Filter, Select and Deselect Values
Let’s write event handlers to handle user interactions.
When user types in the input field, KeyUp event is fired and we are handling it with handleInputKeyUp() method, in which we are updating the search property and calling the fetchLookupRecordsImperative() method.
When user click on the record from the dropdown, handleListClick method will be triggered, in which we would be dispatching the change event to the parent with the selected record value with the input name using selectLookupRecord() method.
When user click on the cross icon, handleCrossIconClick() method will be triggered, in which we are dispatching a change event to parent with empty value using selectLookupRecord() method.
Style the Component
We want the input element, dropdown, icons and loading screen to neetly align with scrollable options for dropdown.
Here’s the CSS file that we need:
Explanation:
We give the dropdown container a subtle shadow for better visual separation from the page.
The max-height property ensures that if there are many options, the list doesn’t extend too far down the page. Instead, it scrolls within a defined height.
We also add hover effects on list items for visual feedback.
Conclusion
In this post, we walked through the structure and logic of building a custom lookup field in Salesforce LWC. We covered how to fetch records, debounce the server call based on user input, manage selected record value, and control dropdown visibility using event bubbling.