Dynamic Properties for Lightning Web Components

6 steps to maximising Lightning Web Component usability with dynamic properties

Dave Norris
5 min readSep 22, 2020
Photo by Dan Gold on Unsplash

Making your Lightning Web Components into reusable assets that can be used across different Lightning Pages and objects requires some thought.

I have been working towards a timeline component that is reusable and one of the issues whilst testing this was the ability to select a different record to use as the basis for the timeline. See this issue for details.

The best example of why this is important started with Cases. Plotting the related records to Cases was important but what Administrators wanted to do was add the timeline of the related Contact to the page. Since the component assumed you only needed to plot the timeline corresponding to the current pages object type — you couldn’t do it.

If you research existing standard components available to Administrators you’ll see that Salesforce provides this capability. Let’s look at the standard component called Related List — Single. This component allows an Administrator to show related records to either the current object OR to a related record. In order to provide the Administrator with a user friendly way of changing the behaviour at run time a dynamic property exists. This property is a simple drop down list of options.

A standard component showing the ability to customise behaviour with dynamic properties

What I wanted was something similar. I had already added static properties with static picklist options. What I needed was the ability to dynamically set the drop down values at run time.

Queue dynamic properties.

Dynamic properties allow developers to expose picklist values to a Lightning Web Component where the values are provided by an Apex class. The developer can provide a more user friendly, reusable component as a result.

Here are the steps I took.

Requirements

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

  • Create a property called Parent Record
  • 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

So let’s look at how I approached the development to meet the above requirements.

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 {}

This class has a number of methods that can be overriden to provide your custom functionality.

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;
}
}

Context is of type DesignTimePageContext and has the following properties

  1. entityName — The API name of the sObject used in the current Lightning Page
  2. 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” />

So now when the timeline component is on a page I can dynamically change the sObject and record on which the data is based. The list is restricted to only those values that are valid therefore making it easier to use and less likely to cause an unexpected error.

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.

You can get all the code featured in the above article in the timeline-lwc GitHub repository below.

--

--

Dave Norris

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