How to Create Admin Friendly Lightning Components

One of the more impressive aspects of the Lighting Component Framework is also one of the more subtle ones. This blog post walks through how developers can create functionality with components that are surfaced via Lightning App Builder. This way, Lightning Experience further empowers admins to construct new interfaces without any concern for underlying code.

The impressive part comes with the seamless integration of custom components and the App Builder interface. In Salesforce Classic, a developer creates a Visualforce page which may be applied to a page layout. However, that page was always cordoned off into an iframe and had very little connection to the record page itself. Admins could choose to have the page on the layout or not – there was no flexibility in how that page was rendered except via the code itself.

In Lightning Experience, the component, the App Builder and the record page are all built on the same framework and are designed to work together. This provides more opportunities for developers to create a variety of components which work in a variety of ways to give admins more options on how to create an interface for their users.  Before diving in deeper, make sure you’re already familiar with Using Lightning Components in the Lightning Experience on Trailhead.

Let’s look at how this works with the Lightning Components Framework.

Your friend the Design Resource

Every Lightning Component is a combination of specific resources (usually called a bundle). In a bundle, the most commonly used resources are:

  • Component – the XML based interface the user interacts with
  • Controller – the JavaScript code that manipulates the component

As more specific needs come up, other resources may come into play. For instance, the style resource can apply specific CSS classes to the component and the helper resource can be called in for reusable code the controller can call.

The design resource is an XML file whose purpose is to describe how the component works for other tools like Lightning App Builder. It outlines what kind of attributes are available, what parameters an admin can control and puts some limits on when the component will be surfaced.

Let’s say you wanted to expose the following attributes in your component:

<design:attribute name="subject" label="Subject"
                  description="Name of the person you want to greet" />
<design:attribute name="greeting" label="Greeting" />

You could add this design resource to the bundle:

<design:component label="Hello World">
   <design:attribute name="subject" label="Subject"
                     description="Name of the person you want to greet" />
   <design:attribute name="greeting" label="Greeting" />
   <sfdc:objects>
      <sfdc:object>Custom__c</sfdc:object>
      <sfdc:object>Opportunity</sfdc:object>
   </sfdc:objects>
</design:component>

The working parts of this design resource are:

  1. The parent design tag, which has a label that is used when displaying the options for this component.
  2. The attributes, whose names match the attribute you want to expose. If you want to make the option a picklist value, you can add a datasource to the attribute. For example, you could create a datasource with comma separated values for size like “Small,Medium,Large”. You can also add a default.
  3. The objects array, which describes which standard or custom objects this component is designed to work with. If the array is not present, it is assumed to work with all objects. If the array is present, it surfaces on tools associated with objects in the array (such as editing the record page via App Builder). Note: Namespaced objects require the namespace to be in the object name.

New Feature: Dynamic Picklists with Apex

The standard way to provide a series of options to the admin is via simple comma separated list. In Spring ’17, a feature was added to allow Apex to dynamically override those values. This allows developers to provide values to Lightning App Builder based on context, such as selected from a subset of known events currently in the system.

To use this feature, create an Apex class that utilizes the VisualEditor.DynamicPicklist interface:

global class MyCustomPickList extends VisualEditor.DynamicPickList{
    
    global override VisualEditor.DataRow getDefaultValue(){
        VisualEditor.DataRow defaultValue = new VisualEditor.DataRow('red', 'RED');
        return defaultValue;
    }
    global override VisualEditor.DynamicPickListRows getValues() {
        VisualEditor.DataRow value1 = new VisualEditor.DataRow('red', 'RED');
        VisualEditor.DataRow value2 = new VisualEditor.DataRow('yellow', 'YELLOW');
        VisualEditor.DynamicPickListRows  myValues = new VisualEditor.DynamicPickListRows();
        myValues.addRow(value1);
        myValues.addRow(value2);
        return myValues;
    }
}

Then refer to that class in the design attribute:

<design:component>
        <design:attribute name="property1" datasource="apex://MyCustomPickList"/>
</design:component>

Design for flexibility

Working with Lightning Components is quite different than building out Visualforce pages. While Visualforce had components, they were rarely used except in use cases with extreme need of reuse. Applications running Lightning Components use a component from the ground up every single time.

For instance, the Dreamhouse reference application includes a component called PropertyTileList. Let’s look at its component XML:

<aura:component controller="PropertyController"
                implements="flexipage:availableForAllPageTypes">
    
    <aura:attribute name="properties" type="Property__c[]"/>
    <aura:attribute name="pageSize" type="integer" default="8"/>
    <aura:attribute name="page" type="integer"/>
    <aura:attribute name="pages" type="integer"/>
    <aura:attribute name="total" type="integer"/>

    <aura:attribute name="searchKey" type="String" default=""/>
    <aura:attribute name="minPrice" type="integer" default="0"/>
    <aura:attribute name="maxPrice" type="integer" default="10000000"/>
    
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    <aura:handler event="c:RangeChange" action="{!c.onRangeChange}"/>
    <aura:handler event="c:EinsteinVisionEvent" action="{!c.onEinsteinVisionEvent}"/>

    <lightning:layout horizontalAlign="center" multipleRows="true">
        <aura:iteration items="{!v.properties}" var="property">
            <lightning:layoutItem padding="around-small" size="12" smallDeviceSize="6" mediumDeviceSize="4" largeDeviceSize="3">
                <c:PropertyTile property="{#property}"/>
            </lightning:layoutItem>    
        </aura:iteration>
    </lightning:layout>
    <c:PropertyPaginator page="{!v.page}" pages="{!v.pages}" total="{!v.total}"
                         pagePrevious="{!c.onPagePrevious}" pageNext="{!c.onPageNext}"/>
    
</aura:component>

Notice three things:

  1. The component references every possible attribute it may need to display.
  2. The component creates a handler for every event variant which may control it. So a range slider could trigger the search; but so might a component running Einstein Vision. This allows the same component to be used in a variety of situations, reacting to different page elements.
  3. The actual rendering of the tile is handled by a subcomponent, which could render that tile in a grid, list, div, etc. That component is the one actually responsible for firing off another event which allows different components to respond to the selected tile.

When designing your components, remember the whole concept of the event structure allows for components to interact without previously having to know the other elements exist. So any component which can trigger a RangeChange event (slider, input fields, buttons, etc) could control the property results in this Dreamhouse component.

The future… Custom page templates

Ever wanted to create your own page template? We are working hard on enabling this for our developer friends. Today, we provide several out of the box options for page templates that vary by page type. These templates are based on fairly standard 1, 2 and 3 column layouts. Based on your feedback, we would like to provide you with a way to build your own templates programmatically. For example, you can build a 4 column template that could be used for a record page, or a 3 column template that could be used for the Home page. You can even add some styling to a template to reduce the overall amount of white space on a page.

Go forth and Componentize

One of the reasons why Salesforce has fully embraced Lightning Components to build new user interfaces and tools is to build a consistent bridge between what Salesforce is using to build and what developer can use to build. Leveraging parts of the component like the design resource, making them flexible with Apex and making them interchangeable with events allows for developer to make versatile UI elements for administrators.

Leave your comments...

How to Create Admin Friendly Lightning Components