Create dynamic options in custom components for your Admins to configure using VisualEditor.DynamicPickList, Lightning Web Components and Lightning App Builder

Posted by

Hello readers! I have been eager to get another Salesforce post out and I’m really excited about a current idea that I have been noodling on over the past few months. Most of the ideas that I come up with stem from client experiences and meaningful conversations with stakeholders involving the “what ifs” of our days. They have shaped and molded many creative ways that we can leverage the Salesforce platform to our advantage. This blog post is the very beginning of that idea.

Background:

I was the lead architect on a project that was building out a partner community for thousands of users with a diverse and advanced set of data visibility, intractability and filtering needs. With some additional requirements around user experience, the need to prevent partners from copying list view data and the need to make all or some tables expose actionable buttons to do other business processes, it was looking more and more like we were needing a custom component over exposing a standard list view to partners for various objects data that they were responsible for managing. As you all know, if we had endless resources, scope and time, we could probably find a way to design and build something this robust. We ended up building what I would refer to as the “diet” version that was incredibly useful, but the “what if” always stuck with me and I started digging into it in my free time. I set out to understand what it would require to build and how dynamic and scalable could we truly make a tool like this.

A click down into the problem to solve:

Here are a list of my business requirements if I were acting as a primary stakeholder asking for the feature:

  • Test that as a partner that is part of an enterprise level client with many partners working across many business units, I can dynamically configure a way to see and do the following:
    • See specific data related to specific objects in various contexts across a partner portal (ie. perhaps I have a diverse set of case management to provide service for distinct partners who operate in different capacities in various parts of a partner portal that need different levels of service and information exposed to them as they track the progress of their case submission at different times) <– Not complicated, right?
  • Test that as a partner who is accessing information at the list view level, I am prevented from copying information onto my personal device
  • Test that as a partner, I am able to have dynamic filtering that is bound to the configured list view and has the following:
    • Accepting of various data types such as strings, dates, checkboxes, numbers, etc.
    • Allows the dynamic fields that are shown within the specific datatable to also be filterable
    • Ensure that the fields we are showing are actually accessible by the currently logged in user
  • Test that as a partner, I can drive action from all or certain rows of the list view if it is necessary for my business process:
    • This could include a link to download an attachment or a button to open up a modal which launches a whole other process
  • Test that as we onboard new partners and expand the capabilities of existing partners, we have an effective go to market strategy that involves little to no code changes
    • can big configured within production minus fundamental structural changes that require code

Where do we start?

This seems like a giant bite to take right? Thats why I wanted to break it out into several different blog posts that walk readers through my progress on designing the architecture and implementing the framework to find out just how far I might be able to take this and understand exactly how dynamic and scalable this can be as it starts to grow.

So, as I build certain capabilities as part of this idea, I wanted to showcase them in a granular fashion that would provide a piece of the larger puzzle while also offering some cool parts of the platform along the way. Phew! That was a lot to get through, now lets talk about dynamic picklist options in custom components…

Building Blocks

The sole focus in solving this entire problem falls within the user and the admin. We want to ensure that the user has a stellar experience and we can set up a customized capability for them in a matter of minutes that drives the value each partner will be seeking as they access the portal to conduct their day to day activities that support their business. Similarly, we want to put a ton of power into the hands of an Admin and empower them to make the changes needed to set up a new partner or add capability to an existing partner. Additionally, each of these solutions must be simple, configurable and scalable to meet current and future needs of the business.

With both of these critical items in mind, I started with the configurability of the custom component and put myself in the drivers seat of the admin that would be adding or modifying these changes from within the app builder.

First, I created a Lightning Web Component called DynamicDataTable that you see below:

DynamicDataTable.html

<template>
    <lightning-datatable>
        
    </lightning-datatable>
</template>

DynamicDataTable.js

import { LightningElement, api } from 'lwc';
import getRecords from '@salesforce/apex/DynamicQueryImpl.getRecords';

export default class DynamicDataTable extends LightningElement {

    @api sobject;
    @api fieldset;

    async connectedCallback() {

        let records = await getRecords({'sobjectName': this.sobject, 'fieldsetname': this.fieldset})

    }
}

DynamicDataTable.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>50.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__HomePage</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__HomePage">
            <property name="sobject" type="String" datasource="apex://DynamicQuerySupportedObjects" />
            <property name="fieldset" type="String" datasource="apex://DynamicQuerySupportedFieldSets" />
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

Okay, so there are a few important things to note here. Don’t pay attention to the hmtl file right now, that will find its way to importance in later posts. The most important element here is actually the js-meta.xml file. Navigate to the targetConfigs and notice how we are designating different properties. Before we get into the more complex role this is playing, lets make sure we cover the basics first. In its simplest form, these property tags are meant to allow a Lightning Web Component (LWC) to be configurable from within the app builder. If you are familiar with the Aura framework, you might remember the design file that you could create to allow configurable properties as well; same thing. If you need a bit of brushing up on the subject, you can click on this link to learn more about that capability within the LWC framework.

Knowing that these properties can expose various amount of data types is super useful for configuration of the component in the app builder, however with out specific use case, we need to take it a level further to allow admins to create something on the Salesforce side and have it dynamically render as a selectable option. Below is a screenshot of what all of this looks like for context before we continue on:

So what if I wanted to make this dynamic? Make it truly configurable so that an admin could drag and drop a single component onto a community or app builder page, select an object and fieldset of their choosing, save it and then have the list view data associated with your selection automatically render. To achieve this, we need some programmatic intervention and a new custom object to support it.

For the object, I created one called Dynamic_Data_Table that stores the SObject_Name__c and the Field_Set_API_Name__c. This is the making of the ability to create a field set on an object with specific fields you would like to expose to a particular instance of a component for it to render the appropriate amount of information for whoever is viewing it. An admin could easily go to the Dynamic Data Tables tab and create a new record with the corresponding object and field set and have that automatically render as an option when configuring the Lightning Web Component in a community or app builder. See the record below:

But how do we actually make the beginnings of this framework operational to the admin configuring it? Great, they created a record but what next?

VisualEditor.DynamicPickList Class

If you scroll up for a second to the meta.xml file, you’ll see another interesting component (ha!) about it which is the datasource reference. Typically, if it were a hard coded representation of options, you would see the actual options within the data source to choose from that could map back over to a publicly accessible variable within the JavaScript file of the component and be exposed in the builder, however now we see these weird class references:

datasource=”apex://DynamicQuerySupportedObjects”

datasource=”apex://DynamicQuerySupportedFieldSets”

This is such a super cool aspect of the platform. Salesforce gives you the ability to extend the VisualEditor.DynamicPicklist class and return a default value and values to your configurable properties in your Lightning Web Components to make your selections dynamic and scalable. See both classes below:

DynamicQuerySupportedObjects.cls

global class DynamicQuerySupportedObjects extends VisualEditor.DynamicPickList{

    List<String> sObjects = new List<String>();

    public DynamicQuerySupportedObjects() {
        this.sObjects = DynamicDataTableConfigCtrl.getSObjects();
    }
    
    global override VisualEditor.DataRow getDefaultValue(){
        return new VisualEditor.DataRow(sObjects[0], sObjects[0]);
    }
    global override VisualEditor.DynamicPickListRows getValues() {

        VisualEditor.DynamicPickListRows values = new VisualEditor.DynamicPickListRows();
        for (String sobj : sObjects) {
            VisualEditor.DataRow tempVal = new VisualEditor.DataRow(sobj, sobj);
            values.addRow(tempVal);
        }
        return values;
    }
}

DynamicQuerySupportedFieldSets.cls

global class DynamicQuerySupportedFieldSets extends VisualEditor.DynamicPickList{

    List<Dynamic_Data_Table__c> ddts;

    public DynamicQuerySupportedFieldSets() {
        this.ddts = DynamicDataTableConfigCtrl.queryDynamicConfig();
    }
    
    global override VisualEditor.DataRow getDefaultValue(){
        return new VisualEditor.DataRow(ddts[0].SObject_Name__c + ': ' + ddts[0].Field_Set_API_Name__c.replace('_', ' '), ddts[0].Field_Set_API_Name__c);
    }
    
    global override VisualEditor.DynamicPickListRows getValues() {

        VisualEditor.DynamicPickListRows values = new VisualEditor.DynamicPickListRows();

        for (Dynamic_Data_Table__c ddt : ddts) {
            VisualEditor.DataRow tempVal = new VisualEditor.DataRow(ddt.SObject_Name__c + ': ' + ddt.Field_Set_API_Name__c.replace('_', ' '), ddt.Field_Set_API_Name__c);
            values.addRow(tempVal);
        }
        return values;
    }  
}

DynamicDataTableConfigCtrl.cls

public class DynamicDataTableConfigCtrl {

    public DynamicDataTableConfigCtrl() {

    }

    public static List<String> getSObjects() {
        
        Set<String> sobjects = new Set<String>();

        for (Dynamic_Data_Table__c mtd : queryDynamicConfig()) {
            sobjects.add(mtd.SObject_Name__c);
        }
        return new List<String>(sobjects);
    }

    public static List<Dynamic_Data_Table__c> queryDynamicConfig() {
        return [SELECT SObject_Name__c, Field_Set_API_Name__c FROM Dynamic_Data_Table__c];
    }
}

As you can see above, we are utilizing the global override of the getDefaultValue() and getValues() method given to us from the extension, we can return the dynamic values that are shown and populated within the app builder of the custom component.

The DynamicDataTableConfigCtrl is serving as a utility class to optimize the data manipulation and query needs across more implementations that we might consider as part of the architecture.

This is incredibly powerful because now we can use the data model referenced above to create new records that reference objects and fieldsets to associate to a given instance of the custom component. Now that we have this in place, we can use the configured information to go and fetch the correct field set and show the adequate data that is referenced from the field set associated.

There is way more to come with this capability and idea that I am working on, however I found it useful to post various aspects and capabilities of the platform as I go along. Read more about creating dynamic picklists for custom components here.

Thanks for reading and happy coding! 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s