Tuesday, 18 May 2021

Working with Picklists in Apex and Lightning Web Components

 Working with Picklists in Apex and Lightning Web Components 


1.Use dynamic Apex when working with records with no record type.

2.Use UI API callouts when working with record types in Apex.

3.Use the UI API getPicklistValues wire adapter when working with Lightning Web Components.

4.Use an Apex utility class like PicklistUtils to avoid code duplication.




--> Using Picklist in Apex 


ex : 


global with sharing class PicklistUtils {

    // Cache for dynamic Apex calls

    private final static Map<String, List<Schema.PicklistEntry>> apexCache = new Map<String, List<Schema.PicklistEntry>>();

    

     public static List<Schema.PicklistEntry> getPicklistValues(sObjectType sObjectType,Schema.sObjectField field ) {

        // Try to get entries from cache

        String cacheKey =

            String.valueOf(sObjectType) +

            '.' +

            String.valueOf(field);

            

            

        List<Schema.PicklistEntry> entries = apexCache.get(cacheKey);

        

        if (entries == null) {

            // Get picklist values

            entries = field.getDescribe().getPicklistValues();

            // Exclude inactive picklist values

            entries = getActivePicklistEntries(entries);

            // Cache entries

            apexCache.put(cacheKey, entries);

        }

        return entries;

    }

    

     private static List<Schema.PicklistEntry> getActivePicklistEntries(

        List<Schema.PicklistEntry> entries

    ) {

        List<Schema.PicklistEntry> activeEntries = new List<Schema.PicklistEntry>();

        for (Schema.PicklistEntry entry : entries) {

            if (entry.isActive()) {

                activeEntries.add(entry);

            }

        }

        return activeEntries;

    }


    global class PicklistEntries {

        global PicklistEntry defaultValue;

        global List<PicklistEntry> values;

    }


    global class PicklistEntry {

        global String label;

        global String value;

    }

    

 }


it has three important limitations 


1.It relies on hard-coded object and field names.

2.It returns values that include inactive picklist values.

3.It doesn't handle values for specific record types.


--> Generic Helper method


global with sharing class PicklistUtils {

    // Cache for dynamic Apex calls

    private final static Map<String, List<Schema.PicklistEntry>> apexCache = new Map<String, List<Schema.PicklistEntry>>();

   

   

   global static List<Schema.PicklistEntry> getPicklistValues(String objectName,String fieldName) {

   

        // Try to get entries from cache

        String cacheKey = objectName + '.' + fieldName;

        List<Schema.PicklistEntry> entries = apexCache.get(cacheKey);

        if (entries == null) {

            // Use dynamic Apex to get object description

            // Schema.describeSObjects throws System.InvalidParameterValueException if objectName is invalid

            Schema.DescribeSobjectResult objDescription = Schema.describeSObjects(

                new List<String>{ objectName }

            )[0];

            // Retrieve field map and check that field exists

            Schema.SObjectField field = objDescription.fields.getMap()

                .get(fieldName);

            if (field == null) {

                InvalidParameterValueException e = new InvalidParameterValueException(

                    'fieldName',

                    fieldName

                );

                e.setMessage(

                    'Could not find field ' +

                    fieldName +

                    ' on object ' +

                    objectName

                );

                throw e;

            }

            // Get picklist values

            entries = field.getDescribe().getPickListValues();

            // Exclude inactive picklist values

            entries = getActivePicklistEntries(entries);

            // Cache entries

            apexCache.put(cacheKey, entries);

        }


        return entries;

    }

    

     private static List<Schema.PicklistEntry> getActivePicklistEntries(

        List<Schema.PicklistEntry> entries

    ) {

        List<Schema.PicklistEntry> activeEntries = new List<Schema.PicklistEntry>();

        for (Schema.PicklistEntry entry : entries) {

            if (entry.isActive()) {

                activeEntries.add(entry);

            }

        }

        return activeEntries;

    }


    global class PicklistEntries {

        global PicklistEntry defaultValue;

        global List<PicklistEntry> values;

    }


    global class PicklistEntry {

        global String label;

        global String value;

    }

   

   

   }


it has one important limitations :


1.It doesn't handle values for specific record types.


--> UI API endpoint


if you need to work with picklist values for specific record types, you'll need 

to do a callout to this UI API endpoint.




ex :


global with sharing class PicklistUtils {


 // Cache for UI API calls

    private final static Map<String, PicklistEntries> uiApiCache = new Map<String, PicklistEntries>();



   global static PicklistEntries getPicklistValues(String objectName,Id recordTypeId,String fieldName) {


        String requestUrl =

            URL.getSalesforceBaseUrl().toExternalForm() +

            '/services/data/v51.0/ui-api/object-info/' +

            objectName +

            '/picklist-values/' +

            recordTypeId +

            '/' +

            fieldName;


        // Try to get entries from cache

        PicklistEntries entries = uiApiCache.get(requestUrl);

        if (entries == null) {

            // Prepare UI API request

            HttpRequest request = new HttpRequest();

            request.setMethod('GET');

            request.setHeader(

                'Authorization',

                'Bearer ' + UserInfo.getSessionId()

            );

            request.setEndpoint(requestUrl);

            // Call UI API

            try {

                HttpResponse httpResponse = new Http().send(request);

                if (httpResponse.getStatusCode() == 200) {

                    // Parse JSON response into PicklistEntries

                    entries = (PicklistEntries) JSON.deserialize(

                        httpResponse.getBody(),

                        PicklistEntries.class

                    );

                    // Cache entries

                    uiApiCache.put(requestUrl, entries);

                } else {

                    System.debug(

                        'HTTP ' +

                        httpResponse.getStatusCode() +

                        ' while calling UI API, Response ' +

                        httpResponse.getBody()

                    );

                    throw new CalloutException(httpResponse.getBody());

                }

            } catch (System.Exception e) {

                System.debug('ERROR: ' + e);

                throw e;

            }

        }


        return entries;

    }

    

     global class PicklistEntries {

        global PicklistEntry defaultValue;

        global List<PicklistEntry> values;

    }


    global class PicklistEntry {

        global String label;

        global String value;

    }

 }

 

--> Using picklists in Lightning Web Components

 

You can avoid Apex altogether by calling directly the UI API with the 'getPicklistValues' wire adapter

 from the lightning/uiObjectInfoApi module.


The getPicklistValues adapter retrieves all picklist values for a given record type and field name. 

Using the adapter is interesting for performance reasons because it benefits from the Lightning Data Service cache. 

Additionally, this also reduces the amount of Apex code you have to maintain and test (no need for a custom Apex controller). 


ex :

    

    import { LightningElement, wire } from 'lwc';

import { getPicklistValues } from 'lightning/uiObjectInfoApi';

import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';

 

export default class Example extends LightningElement {

    @wire(getPicklistValues, {

        recordTypeId: '012000000000000AAA', // Default record type Id

        fieldApiName: INDUSTRY_FIELD

    })

    getIndustryPicklistValues({ error, data }) {

        if (data) {

            /*

            Do something with data.defaultValue and data.values[]

            Values are represented as objects like this:

            {

                attributes: null,

                label: "Agriculture",

                validFor: [],

                value: "Agriculture"

            }

            */

        } else if (error) {

            // Handle error

        }

    }

}


No comments:

Post a Comment