Dynamic Properties for Lightning Web Components

6 steps to maximising Lightning Web Component usability with dynamic properties

Moving from static to dynamic dropdown values in your component properties
A standard component showing the ability to customise behaviour with dynamic properties

Requirements

In order to recreate the look and feel of the standard component above I first wrote down my requirements.

  • Must have ‘Use This <Object Type>’ as an option in the dropdown list where <Object Type> is dynamic based on the current Lightning Page the timeline is dragged onto. So for the Contact lightning page the value should be ‘Use This Contact’
  • Must show all fields that represent a relationship to Account, Contact, Lead, Case or Opportunity only
  • Fields shown must be the label and not the API name to improve usability
  • Fields shown need to be only those accessible to the current user to adhere to security rules

Step 1 — Create an Apex class

Salesforce provides an abstract class — DynamicPicklist — that can be extended to provide the basic capabilities for the dynamic picklist.

global with sharing class TimelineParentPicklist extends VisualEditor.DynamicPickList {}

Step 2 — Get Context

Context tells me 2 things. What type of page is the component on and what object type has context (i.e. Am I on a Lightning Page or Community Page? Am I in a Contact or Account?). These dynamic values are set for you providing you get the value in your custom class constructor.

global with sharing class TimelineParentPicklist extends VisualEditor.DynamicPickList {VisualEditor.DesignTimePageContext context;global TimelineParentPicklist(VisualEditor.DesignTimePageContext context) {    this.context = context;
}
}
  1. pageType — HomePage, AppPage or RecordPage

Step 3 — Set a Default Value

A default value pre-populates the lightning web component property. Since we want this to be the value ‘Use This <Object Type>’ I can override the getDefaultValue method and use the context set above to derive the label being used. So if the Administrator renames the Contacts tab to Person then my value should change.

global with sharing class TimelineParentPicklist extends VisualEditor.DynamicPickList {  global override VisualEditor.DataRow getDefaultValue() {  String objectLabel = ((SObject) (Type.forName(‘Schema.’ + String.valueOf(this.context.entityName)).newInstance())).getSObjectType().getDescribe().getLabel();  VisualEditor.DataRow defaultValue = new VisualEditor.DataRow(  ‘Use This ‘ + objectLabel,‘Default_Picklist_Value’);  return defaultValue;  }
}

Step 4 — Get Values

The last method I implemented was getValues. This method returns the picklist item values to the property. For this method I query all fields on the current sObject then only return those fields accessible to the current user and that are lookup fields to Lead, Account, Contact, Case or Opportunity (since the timeline only works with these parent objects).

global with sharing class TimelineParentPicklist extends VisualEditor.DynamicPickList {   global override VisualEditor.DynamicPickListRows getValues() {  VisualEditor.DynamicPickListRows myValues = new VisualEditor.DynamicPickListRows();  myValues.addRow(getDefaultValue());  Schema.DescribeSObjectResult describeSobjects = ((SObject) (Type.forName(‘Schema.’ + this.context.entityName).newInstance())).getSObjectType().getDescribe();  Map<String, Schema.SObjectField> myFields = describeSobjects.fields.getMap();  for (String field : myFields.keySet()) {    Schema.DescribeFieldResult currentField = myFields.get(field).getDescribe();    if (
currentField.isAccessible() && currentField.isNamePointing() == false && currentField.getLabel() != ‘Master Record ID’ &&(String.valueOf(currentField.getReferenceTo()) == ‘(Lead)’ || String.valueOf(currentField.getReferenceTo()) == ‘(Contact)’ || String.valueOf(currentField.getReferenceTo()) == ‘(Account)’ || String.valueOf(currentField.getReferenceTo()) == ‘(Opportunity)’ || String.valueOf(currentField.getReferenceTo()) == ‘(Case)’) ) {
myValues.addRow(new VisualEditor.DataRow(currentField.getLabel(), field)); } }return myValues; }}

Step 5 — Test Class

Apex requires test classes and the implementation above is not excluded. By instantiating the TimelineParentPicklist class above and setting values for context I could call the class methods with values and test the expected results.

@isTest(seeAllData=false)private with sharing class TimelineParentPicklist_Test {  @isTest  static void testTimelinePicklistDefaultValue() {    VisualEditor.DesignTimePageContext context = new VisualEditor.DesignTimePageContext();    context.entityName = ‘Contact’;    TimelineParentPicklist timeline = new TimelineParentPicklist(context);    Test.startTest();    VisualEditor.DataRow defaultValue = timeline.getDefaultValue();    Test.stopTest();    System.assertEquals(‘Use This Contact’,defaultValue.getLabel(),‘Timeline Parent Picklist default value incorrect’);  }  @isTest  static void testTimelinePicklistValues() {    VisualEditor.DesignTimePageContext context = new VisualEditor.DesignTimePageContext();    context.entityName = ‘Contact’;    TimelineParentPicklist timeline = new TimelineParentPicklist(context);    Test.startTest();    VisualEditor.DynamicPickListRows picklistValues = timeline.getValues();    Test.stopTest();    System.assert(picklistValues.size() > 0, ‘No parent picklist values found for Contact’);  }}

Step 6 — Add property to Lightning Web Component

The final step was to add the dynamic property to the Lightning Web Component and test it.

<property name=”timelineParent” label=”Parent Record” type=”String” datasource=”apex://TimelineParentPicklist” required=”true” />
The dynamic picklist in all its glory

Summary

Dynamic picklists for your Lightning Web Components will improve reusability and maintainability. Implementing them takes a bit of work but ultimately delivers a better, less error prone, user experience.

Developer Advocate @ MuleSoft || Interested in solving unique challenges using different cloud service providers || All opinions are mine.