Creating a configurable record picker in Lightning Web Component has been traditionally achieved through custom lookup components, like the Generic Multi-Select Lookup Component, for multi-record selection. While such components remain relevant for multiple-record selection, Salesforce introduced the lightning-record-picker control to simplify the selection of single records within custom LWC (Lightning Web Component) screens. This new control negates the need for custom components solely for record lookup. This guide focuses on crafting a configurable record picker in LWC, facilitating dynamic filter criteria and field adjustments at runtime without necessitating code modifications.
How does the Record Picker Control Operate?
Before employing this control, let’s delve into its internal workings. The Record Picker control utilizes a GraphQL Wire Adapter to conduct record searches, display them, and facilitate user selection. Its internal use of GraphQL enables us to apply record filtering and exhibit additional fields within the lookup.
Consider the following code snippet to display a Record Picker for the Account object.
<template> <lightning-card title="Record Picker"> <lightning-record-picker label="Accounts" placeholder="Search Accounts..." object-api-name="Account"> </lightning-record-picker> </lightning-card> </template>
When the record picker is displayed on the page and a record is searched, it initiates a GraphQL API request. You can inspect the network tab in the browser debugger to observe the GraphQL request made for the record search. In the image below, two callouts highlight the GraphQL API call:
- Callout 1: When performing a search in the record picker, it triggers a GraphQL request, adding the URL parameter “&aura.RecordUi.executeGraphQL=1”.
- Callout 2: This represents the request made to the GraphQL API with the searched text.
On the basis of the searched text, the result is returned from Salesforce Object and shown in record picker control.
How do you use the Record Picker?
To employ this element, you require a minimum of two attributes: object-api-name, which specifies the object for displaying the record, and label, which serves as the caption or header text for the control.
<template> <lightning-card title="Record Picker"> <lightning-record-picker label="Accounts" object-api-name="Account"> </lightning-record-picker> </lightning-card> </template>
This control has below important propertiesmatching-infoThis is field information that will show in the record picker. Like, show the name field for record selection.display-infoThis will show additional fields with the primary field (matching-info). Multiple additional fields can be shown. Like, show email and phone along with the primary name field.filterThis attribute is used to filter the record picker. Like, show only USA records
Create a Configurable Record Picker Component
The Record Picker component suits simple applications well. However, for more control over record selection, customization becomes necessary.
While all attributes can be customized, I focused on modifying three attributes—matching-info, display-info, and filter. To allow for customization, I introduced two custom metadata types.
The Record Picker Config custom metadata type will contain object and field details displayed in the Record Picker. You’ll need to create custom fields based on the images below within this metadata type.
Add records in this metadata object.
Developer Name | Display Field Name | Display Secondary Fields | Object API |
---|---|---|---|
Account_Picker_On_Patient_Page | Name | Email__c, PersonPhone | Account |
We can include multiple additional fields by adding them in a comma-separated format within the ‘Display Secondary Fields’ field.
The ‘Field Condition Operator’ list comprises operators utilized with the filter field. It’s necessary for GraphQL to conduct searches using filter criteria. Input these values within the Field Condition Operator picklist.
Add records in this metadata object.Record Picker ConfigCondition FieldCondition OperatorCondition ValueAccount_Picker_On_Patient_PageNameLikea%
We are ready with configuration object creation. We also created configuration records.
Apex Class
We have to develop an Apex class responsible for retrieving configuration records from the custom metadata type, considering the provided configuration name.
public class RecordPickerController { @AuraEnabled(cacheable=true) public static RecordPickerDTO.ObjectInfo getRecordPicker(string metadataName){ return RecordPickerService.getRecordPicker(metadataName); } }
public class RecordPickerDTO { public class ObjectInfo{ @auraenabled public string ObjectName {get;set;} @auraenabled public string PrimaryField {get;set;} @auraenabled public string SecondaryFields {get;set;} @auraenabled public List<FieldCondition> Conditions {get;set;} } public class FieldCondition{ @auraenabled public string FieldName {get;set;} @auraenabled public string Condition {get;set;} @auraenabled public string ConditionValue {get;set;} @auraenabled public boolean IsLiteral {get;set;} @auraenabled public boolean IsDynamicValue {get;set;} } }
public class RecordPickerSelector { //Retrieve Custom Metadata Records public static RecordPickerConfig__mdt getMetadataConfig(string metadataName){ return [SELECT Id, ObjectAPI__c, DisplayFieldName__c, DisplaySecondaryFields__c, (SELECT Id, RecordPickerConfig__c, ConditionFieldAPI__c, Condition__c, ConditionValue__c, IsLiteral__c, IsDynamic__c FROM Record_Picker_Condition_Configs__r) FROM RecordPickerConfig__mdt where DeveloperName=:metadataName]; } }
public class RecordPickerService { //Get Record Picker Setting for provided configuration public static RecordPickerDTO.ObjectInfo getRecordPicker(string metadataName){ RecordPickerConfig__mdt mdt=RecordPickerSelector.getMetadataConfig(metadataName); RecordPickerDTO.ObjectInfo objDto=new RecordPickerDTO.ObjectInfo(); objDto.ObjectName=mdt.ObjectAPI__c; objDto.PrimaryField=mdt.DisplayFieldName__c; objDto.SecondaryFields=mdt.DisplaySecondaryFields__c; //Loop all condition for record picker List<RecordPickerDTO.FieldCondition> fields=new List<RecordPickerDTO.FieldCondition>(); for(RecordPickerConditionConfig__mdt cm : mdt.getSObjects('Record_Picker_Condition_Configs__r')) { RecordPickerDTO.FieldCondition fCondition=new RecordPickerDTO.FieldCondition(); fCondition.FieldName=String.valueOf(cm.get('ConditionFieldAPI__c')); fCondition.Condition=String.valueOf(cm.get('Condition__c')); fCondition.ConditionValue=String.valueOf(cm.get('ConditionValue__c')); fCondition.IsLiteral=Boolean.valueOf(cm.get('IsLiteral__c')); fCondition.IsDynamicValue=Boolean.valueOf(cm.get('IsDynamic__c')); fields.add(fCondition); } objDto.Conditions=fields; return objDto; } }
Let’s craft a Lightning Web Component to create a Configurable Record Picker. This component will utilize the RecordPickerController apex class to fetch the record picker configuration and set the properties of the lightning-record-picker control based on the retrieved configuration.
<template> <template lwc:if={config}> <lightning-record-picker label={label} placeholder={placeHolder} object-api-name={config.ObjectName} matching-info={matchingInfo} display-info={displayInfo} filter={filter}> </lightning-record-picker> </template> </template>
import { LightningElement,api,wire,track } from 'lwc'; import getRecordPicker from '@salesforce/apex/RecordPickerController.getRecordPicker'; export default class ObjectRecordPicker extends LightningElement { @api recordPickerConfigName; @api label='Account'; @api placeHolder='Select...'; config=undefined; @track matchingInfo; @track displayInfo; @track filter; @wire(getRecordPicker, {metadataName:'$recordPickerConfigName'}) wiredConfigs({ error, data }) { if (data) { this.config = data; this.error = undefined; this.setCriteria(); this.setFields(); } else if (error) { this.error = error; this.config = undefined; } } //Set Primary and Additional Fields setFields(){ if(this.config){ this.matchingInfo = { primaryField: { fieldPath: this.config.PrimaryField } } if(this.config.SecondaryFields){ var secondFields=this.config.SecondaryFields.split(','); var dispFields=[]; var fldsNames=[]; for(var cg of secondFields){ dispFields.push({ fieldPath: cg}); fldsNames.push(cg); } this.matchingInfo.additionalFields=dispFields; this.displayInfo={additionalFields:fldsNames}; } } } //Set Filter Criteria setCriteria(){ var fltrs=[]; if(this.config.Conditions) { for(var cg of this.config.Conditions){ fltrs.push( { fieldPath: cg.FieldName, operator: cg.Condition, value: cg.ConditionValue, }, ); } if(fltrs.length>0){ this.filter = { criteria: fltrs }; } } } }
<?xml version="1.0" encoding="UTF-8"?> <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata"> <apiVersion>58.0</apiVersion> <isExposed>true</isExposed> <masterLabel>Object Record Picker</masterLabel> <targets> <target>lightningCommunity__Page</target> <target>lightningCommunity__Default</target> <target>lightning__RecordPage</target> <target>lightning__AppPage</target> <target>lightning__HomePage</target> <target>lightning__Tab</target> </targets> <targetConfigs> <targetConfig targets="lightning__RecordPage"> <property name="recordPickerConfigName" label="Record Picker Config Name" type="String" /> <property name="label" label="Record Picker Label" type="String" /> <property name="placeHolder" label="Record Picker Place Holder" type="String" /> </targetConfig> </targetConfigs> </LightningComponentBundle>
The component is set up for direct use on the record page, but it can also be employed within other LWC components. In the code below, “Account_Picker_On_Patient_Page” refers to the name of the record picker configuration record.
<c-object-record-picker record-picker-config-name='Account_Picker_On_Patient_Page' label='Account' place-holder='Select Account'></c-object-record-picker>
I haven’t included the configuration code for literal values. The “Is Literal” field can be utilized to set literal values. If you require assistance with this configuration, let’s connect.