A Salesforce Lightning Component Design Pattern for Optimum Code Reuse

As the author of Salesforce.com’s official instructor-led course for Lightning Developers – Dev601: Programming Lightning Components as well as the Trailhead Lightning Component Framework Superbadge, I’ve spent a lot of time developing hands-on exercises that illustrate the mechanics of custom app development on the Salesforce.com platform.

What that course doesn’t officially cover, however, are design patterns for creating components for optimum re-use and application maintainability. Therefore I decided to author this blog article to act as additional reading materials for that course. Note that this post does assume that you are already quite familiar with Salesforce Lightning Component development.

Before we can discuss a pattern strategy, let’s define a couple of terms.

Tightly Coupled Components

A Tightly Coupled App is one where all of the components are nested within a container-component.In effect, you can treat the parent component as a de facto controller, handling any Lightning component events thrown by its members (typically by setting public properties and calling public methods of components other than the one that threw the event).

For instance, consider a scenario where a user chooses from a select-box of beers as illustrated by Figure 1. The BeerSelector component throws a component event containing the beer selection back to FriendsWithBeer.cmp. FriendsWithBeer.cmp then takes the beer selection and programmatically sets a beerId attribute in c:FWB_BeerContacts which triggers and APEX request, displaying a list of all contacts who like that particular beer.

app_org1

Figure 1: Tightly Coupled App Wireframe for our sample app, Friends with Beer

Loosely Coupled Components

Loosely Coupled components, illustrated by Figure 2, are assembled into an application with Lightning App Builder and therefore have no direct parent component other than a Lightning App Builder template. The only way these components can therefore communicate with each other is via Lightning Application events. In this scenario, encapsulation of functionality is an absolute-must as business users have the freedom to include or exclude any installed Lightning App Builder-enabled components in your Salesforce org.

app_org2

Figure 2: Loosely Coupled App Wireframe

Why Bother?

It’s a fair question. If you’ve tracked the progress of Lightning App Builder over the last few release cycles, it’s pretty clear that Salesforce is expending a lot of effort in this particular area of the framework. Having components that can be surfaced by both developers (as strongly-coupled apps), yet retain the flexibility to be deployed in App Builder, helps future-proof your code. It would not surprise me if within a couple of years, the vast majority of custom lightning applications are assembled using App Builder by both developers and power-users/admins.

Bringing it all together

I therefore suggest that you organize your Lightning applications around three separate tiers, as illustrated in Figure 3:

  • The Functional Tier are comprised of utility components that do all the actual output within your application. I’m talking about Lightning Base Components, utility components such as custom grids/tables, custom buttons, and whatnot.
  • The Declarative Application Tier are components that invoke Apex methods, encapsulate business logic, and pass data to the functional tier components for output to the user. These components also fire and listen to component events. A strongly-coupled Lightning application would consist of only components from the Declarative Application Tier and the Functional Tier.
  • The Orchestration Tier invokes components from the Declarative Application Tier, re-firing custom Lightning Component events as custom Lightning Application events. They serve as a wrapper for the Declarative Tier components that you want to surface in Lightning App Builder. They also wrap components in lightning:card, as this is the preferred methodology (in Winter 18) to ensure that your component is not transparent against the new Winter 18 Lightning custom background.
app_org3

Figure 3: Code Organization Pattern

Naming Conventions

You’ve probably inferred from the slides above that I’ve settled on a few naming conventions:

  • Functional component names start with a lower-case letter and are camel-cased.
  • Declarative Application Tier components start with applicationName_ComponentName. Since the name of my sample app is Friends With Beer, I’ve abbreviated the app name to FWB. Therefore, FWB_BeerSelector, FWB_BeerContacts, and so forth.
  • Orchestration Tier components are prefixed with applicationName_App_ComponentName
  • Component event names are prefixed with applicationName_evt_ComponentEventName
  • Application event names are prefixed with applicationName_aEvt_ApplicationEventName

Feel free to critique these choices and concepts in the comments section below. In (hopefully) about 30 days, you’ll be able to view my detailed explanations of these concepts (including a code walkthrough) as a Pluralsight video play-by-play with my fellow Lighting Component instructor Don Robins!

Need immediate help? For Lightning Component consulting, development, and best-practices code-reviews please send an email to info@figleaf.com. My company, Fig Leaf Software is a certified Service-Disabled Veteran-Owned Small Business located in Washington, D.C.

Building Uniquely Branded Lightning Component Single Page Apps for Salesforce Mobile

(part 1 of a 2-part series)

fwbfinalAt Dreamforce last week, Salesforce introduced a new feature that would enable administrators to more distinctly brand their Salesforce mobile apps.

As illustrated by the screenshot at right, that capability is actually available today through various CSS hacks, hand-wringing, and lots of tears.

Other titles that I’d considered for this post:

  • “Stuffing a round peg into a square hole”
  • “How to pull out all of your hair with maximum effort”
  • “I like to suffer”
  • “I should have become a patent lawyer”
  • “There be dragons here”

The fundamental problem is that the platform formerly known as Salesforce1 was clearly designed to render web pages (commonly referred to as Flexipages) and not single-page apps (SPAs). That’s why there’s no officially supported methodology (yet) for disabling the built-in “pull to refresh” feature, which can be quite convenient for refreshing web pages, but is inappropriate for single-page apps. There’s also lack of direct support for a “viewport” that would grab 100% of the visible screen, enabling developers to explicitly manage scrolling within their Lightning app.

Implementing Custom Branding in a Lightning App Builder Template

The first step in our branding odyssey is to create a Lightning App Builder template. This is a new feature in Winter ’18. The template defines drop target regions for Lightning App Builder components. About the only functionality that I’ve found that the app builder template will not support is (ironically) defining DESIGN properties on the template.

To define a template you need to:

  1. Add an implements=”lightning:appHomeTemplate” to the tag.
  2. Define a aura:attribute for each template region
  3. Define the template region in the DESIGN file

In order to implement the crazy branding, I also added the following global style to hide the flexipage page header, which by default consumes a fair amount of space at the top of the page:

<style>
 .oneAnchorHeader {
   display: none;
 } 
</style>

I also set a series of overrides to try and establish a “viewport” that occupied 100% of the width *and* height of the visible screen:

<style>
 .scroller.actionBarPlugin, 
 .oneFlexipage, 
 .flexipagePage,  
 .pageBody,  
 .mainbody, .mainbody > div {
    height: 100%
  }
</style>

Are we having fun yet?

Other global style overrides were added to remove some extraneous white-space as I’m trying to make use of every pixel in the phone form-factor.

Here’s the complete source code listing for the CMP file of the Lightning App Builder template:

<aura:component 
 implements="lightning:appHomeTemplate" 
 description="Friends with Beer Mobile Phone Template">
    
 <aura:attribute name="region1" type="Aura.Component[]" /> 
    
    <style>
        .oneAnchorHeader {
        	display: none;
        } 
        .scroller.actionBarPlugin  {
        	padding-bottom: 0px !important;
        }
        
        .scroller.actionBarPlugin, 
        .oneFlexipage, 
        .flexipagePage,  
        .pageBody,  
        .mainbody, .mainbody > div {
        	height: 100%
        }
        
        .oneFlexipage .pageBody {
        	margin-top: 0px;
        }
          
        .spacer-pull-to-load-more {
        	height: 0px !important;
        }
        
        .slds-table {
        	background-color: rgba(255, 255, 255, 0.3) !important;
        }
        
        .slds-card__header {
            margin: 0px !important;
            padding: 0px !important;
        }
        
    </style>
    
    <aura:handler name="render" 
                  value="{!this}" 
                  action="{!c.onRender}"/>
    
    
    <ui:scrollerWrapper class="maxHeight">
        <div class="mainbody" aura:Id="mainbody">
            <div class="fwbheader slds-align_absolute-center">
              Friends with Beer
            </div>
            {!v.region1}
        </div>
    </ui:scrollerWrapper>
    
</aura:component>

The corresponding design resource indicates the formfactors under which the region will be present:

<design:component label="Friends with Beer">
    <flexipage:template >
            <flexipage:region name="region1" defaultWidth="Small">
                <flexipage:formfactor type="MEDIUM" width="SMALL" />
            </flexipage:region>
    </flexipage:template>
</design:component>

Our template’s CSS resource is relatively tame by comparison. It loads a web font, styles the “Friends with Beer” bar, and sets the background image (beer, of course) for the app:

@font-face {
    font-family: 'Rambla';
    src: url('/resource/fwb/rambla/Rambla-Regular.otf');
}

.THIS .fwbheader {
    background-image: url('/resource/fwb/top-grain.png'), -webkit-radial-gradient(top center, circle, #9f5628, #5d2f17);
    background-repeat: repeat-x;
    color: #3a1515;
    text-shadow: 0px 1px rgba(255,255,255,.3);
    height: 50px;
    width: 100%;
    font-family: Rambla;
    font-size: 1.5em !important;
}

.THIS {
    background-image: url('/resource/fwb/background.png');
    background-size: cover;
}

.THIS .mainbody {
    position: absolute;
    width: 100%;
    height: 100%;
}

.THIS.maxHeight {
    height: 100%;
}

Disabling Pull to Refresh

You may have noticed that the template listens for the render event and fires off a controller function. The function, along with the use of ui:scrollerWrapper, intercepts the dragmove event that is fired when a user taps and drags the screen and prevents it from bubbling up to the native Salesforce mobile app.

({
    onRender: function(component,event,helper) {
        var cmpEl = component.getElement();
         cmpEl.addEventListener("touchmove", function(e) {
            e.stopPropagation();
        }, false); 
        var targetEl =  component.find("mainbody").getElement();
        targetEl.addEventListener("touchmove", function(e) {
            e.stopPropagation();
        }, false);       
    }
    
})

 
In my next post, we’ll review the Lightning components that were used to render data from Force.com, and how I was able to dock them into specific locations within the app. We’ll also discuss whether this approach is really viable on a going-forward basis or whether we should just file it away as an interesting hacking exercise that has run-amok.

And now, it’s time for a beer.

Happy coding!
 

 

 

Salesforce Lightning Components: Creating a Select Box/Tree Component

Need a user to select an item from a 2-tier hierarchy? Typically, you’d either use a lightning:select with embedded optgroup tags, or use the new lightning:tree component. But, for optimal code reuse, consider building a single component that enables a developer to toggle the input mechanism that they think is the most appropriate for their use-case. For instance, while lightning:tree is great for desktop and tablet apps, it’s not appropriate for mobile phones, or for input forms.

fwb1

lightning:tree (left)

IMG_2170

lightning:select (in Salesforce Mobile)

The following component enables a user to either select from a list or a tree, depending on the value of the “mode” attribute.

<aura:component controller="BeerContacts" access="global" extends="c:Base">

<aura:attribute 
 name="mode" 
 type="string" 
 access="global" 
 default="select" />

<aura:attribute 
  name="beers"       
  access="private"       
  type="Object[]"      
  description="hierarchical data to display" />

 <aura:attribute       
  name="selectedBeerId"       
  access="public"       
  type="Id"       
  default=""       
  description="User selection"/>

 <aura:attribute       
  name="labelPrefix"       
  access="public"       
  type="String"       
  default="Friends with "      
  description="Text prefix for select options"/>

 <aura:handler    
  name="change"         
  value="{!v.selectedBeerId}"         
  action="{!c.onBeerChange}" />

 <aura:handler         
  name="init"         
  value="{!this}"         
  action="{!c.doInit}" />

 <!-- fire event when a selection is made -->
 <aura:registerEvent       
  name="onBeerSelected"       
  type="c:FWB_BeerSelected"/>

 <aura:if isTrue="{!v.mode == 'select'}">
   <div>
    <lightning:select     
     name="beerId"           
     label="Select Beer"      
     variant="label-hidden"      
     value="{!v.selectedBeerId}">

    <option value="">Select Beer</option>
    <aura:iteration items="{!v.beers}" var="thisBeerGroup">
        <optgroup label="{!thisBeerGroup.label}">
        <aura:iteration items="{!thisBeerGroup.items}"               
                        var="thisBeer">

            <option value="{!thisBeer.name}">
             {!v.labelPrefix}{!thisBeer.label}
            </option>
        </aura:iteration>
        </optgroup>
     </aura:iteration>
   </lightning:select>
  </div>
  <aura:set attribute="else">
    <lightning:tree items="{!v.beers}"                               
       header=""                               
       onselect="{!c.onTreeSelect}"/>
  </aura:set>
 </aura:if>
</aura:component>

While you can’t prevent a user from selecting a category in lightning:tree, you can provide them with guidance that they need to select a leaf node as illustrated by the component’s controller onTreeSelect function:

({
    doInit : function(component, event, helper) {
        helper.onInit(component,event,helper);
    },
    onBeerChange: function(component,event,helper) {
        var compEvent = component.getEvent("onBeerSelected");
        compEvent.setParam("beerId",component.get('v.selectedBeerId'));
        compEvent.fire();
    },
    onTreeSelect: function(component,event,helper) {
        var compEvent = component.getEvent("onBeerSelected");
        var selectedName = event.getParam('name');
        if (selectedName.indexOf('type:') == -1) {
            compEvent.setParam("beerId",selectedName);
            compEvent.fire();
        } else {
            var toastEvent = $A.get("e.force:showToast");
            if (toastEvent) {
                toastEvent.setParams({
                    "type" : "warning",
                    "message": "Please select a beer, not a category."
                });
                toastEvent.fire();
            }
        }
    }
})

And finally, you can use the same data structure to populate both the select box and tree control, as outlined by the following helper method, which is invoked by the controller’s doInit() method:

({
    onInit : function(component,event,helper) {
        helper.runApex(
            component,
            "c.getBeers",
            function(response) {
                var result = [];
                var groupName = "";
                var thisGroup = {items : []};
                for (var i=0; i<response.length; i++) {
                  if (groupName != response[i].Type__c) {                          
                        if (thisGroup.items.length > 0) {
                            result.push(thisGroup);
                        }
                        groupName = response[i].Type__c;
                        thisGroup = {
                            label: response[i].Type__c,
                            name: 'type:' + response[i].Type__c,
                            expanded : true,
                            items : []
                        };
                    }
                    thisGroup.items.push({
                        name: response[i].Id,
                        label: response[i].Name
                    });

                }
                component.set('v.beers',result);
            }
        );
    }
})

For reference, the getBeers() Apex method simply retrieves a category and beer name from a custom object. Note that the data is sorted by the fields that we are going to use for grouping in both the tree and select box:

public with sharing class BeerContacts {

    @AuraEnabled
    public static List<Beer__c> getBeers() {
      return [
         select Id,Name,Type__c,Country__c
         from Beer__c
         order by Type__c,Name
      ];
    }
}

Happy coding!

Using optgroup in lightning:select

IMG_2170.PNG

Grouped Options in Salesforce Mobile

Every coding framework/toolset has strengths and weaknesses. There are some incredibly powerful things that you can do very simply, but often times there are incredibly simple things that require more brainpower (and a lot more coding) than should really be necessary.

Unfortunately, grouping options read from force.com in a Salesforce lightning:select control falls into the latter category.

In order to achieve the grouping effect, as seen in the screenshot at right, I needed to pull data using Apex, group it client-side into an array of objects, and then loop through that array of objects in the lightning component.

In this use-case, we’re working with a custom object named Beer__c with a field named Type__c that holds our types of beer, e.g. Ale, Lager, Stout, etc.

Why beer? Because beer is both the cause and solution to all of our problems. Clearly.

Step 1: Ordering Apex Results

public with sharing class BeerContacts {

   @AuraEnabled    
   public static List<Beer__c> getBeers() {
     return [ 
       select Id,Name,Type__c,Country__c
       from Beer__c
       order by Type__c,Name
     ];
   }
}

Step 2: Group the Results into an Array of Objects

    doInit : function(component, event, helper) {
        helper.runApex(
            component,
            "c.getBeers",
            function(response) {
             
                // transform into object array
                var result = [];
                var groupName = "";
                var thisGroup = {items : []};
                for (var i=0; i<response.length; i++) {
                    if (groupName != response[i].Type__c) {
                        if (thisGroup.items.length > 0) {
                            result.push(thisGroup);
                        }
                        groupName = response[i].Type__c;
                        thisGroup = {
                            Name: response[i].Type__c,
                            items : []
                        };
                    } 
                    thisGroup.items.push({
                        Id: response[i].Id,
                        Name: response[i].Name
                    });
                    
                }
                component.set('v.beers',result);
            }
        );
    }

Step 3: Output the Array of Objects into a lightning:select

<lightning:select name="beerId"
             label="Select Beer"
             variant="label-hidden">              
  <option value="">Select Beer</option>    
  <aura:iteration items="{!v.beers}" var="thisBeerGroup">
     <optgroup label="{!thisBeerGroup.Name}">
     <aura:iteration items="{!thisBeerGroup.items}" var="thisBeer">
         <option value="{!thisBeer.Id}">{!thisBeer.Name}</option>
     </aura:iteration>
     </optgroup>
   </aura:iteration>
</lightning:select>

Screenshots from Dev601: Programming Lightning Components

I recently completed revamping the Dev601: Programming Lightning Components course for Salesforce.com. Dev601 is a 450-page, 5-day instructor-led course that comprehensively walks developers through building custom extensions for Lightning Experience, Salesforce Mobile, and other platforms.

You learn how to build custom query-by-example interfaces using Apex, Lightning Data Service, and standard components…
ss1

You coordinate the display of multiple views using Application and Component events…
ss2

Build sophisticated forms with data validation and execute CRUD operations with Lightning Data Service…
ss3

And implement loosely-coupled components that are flexible enough to be assembled by non-programmers in Lightning App Builder…

ss5

Cool!!!

Need some Lightning Component consulting/professional services? Contact my company, Fig Leaf Software, at info@figleaf.com

Turn an Ext JS 6 Segmented Button into an Activity Timeline with CSS

My Ext JS 6/Java project for the National Institutes of Standards (NIST) requires an “activity path” indicator. No such widget exists directly in Ext, so I created a CSS override to the ExtJS segmented button in order to give me what I needed. Ultimately I’ll make this more robust by converting it over to Sass and using Ext JS Sass variables, but the raw overrides are presented here for your consideration.

Ext JS button definition:

 {
   xtype: 'segmentedbutton',
   cls: 'timeline',
   height: 40,
   items: [
     {
       text: 'Step 1'
     },
     {
       text: 'Step 2'
     },
     {
       text: 'Step 3'
     }
   ]
}

CSS overrides:

<style>

    /* white right-facing triangle */
    .timeline .x-segmented-button-item-horizontal:before {
	    content: " ";
	    height: 0;
	    width: 0;
	    position: absolute;
	    left: 2px;
	    top: 0px;
	    border-style: solid;
	    border-width: 19px 0 19px 19px;
	    border-color: transparent transparent transparent #fff;
	    z-index: 0;
	}

	.timeline .x-btn-default-small {
		border: 0px !important;
	}

	/* overlay right-facing triangle of standard button color, leaving
	edges of white right-facing triangle */
    .timeline .x-segmented-button-item-horizontal:after {
	    content: " ";
	    height: 0;
	    width: 0;
	    position: absolute;
	    left: 0px;
	    top: 0px;
	    border-style: solid;
	    border-width: 19px 0 19px 19px;
	    border-color: transparent transparent transparent #5fa2dd;
	    z-index: 10;
	}

	/* white arrow overlay to left of first button */
	.timeline .x-segmented-button-item-horizontal:first-child:after {
		 border-color: transparent transparent transparent white;
	}

	.timeline .x-segmented-button-item-horizontal:first-child:after, .timeline .x-segmented-button-item-horizontal.x-btn-pressed:first-child:after {
		 border-color: transparent transparent transparent white !important;
	}

	.timelinebutton.x-btn-pressed:after {
	    border-left-color: #477aa6;
	}

 	.timeline .x-segmented-button-item-horizontal.x-btn-pressed + .x-segmented-button-item-horizontal:after {
 		border-color: transparent transparent transparent #477aa6;
 	}

 	.timeline .x-segmented-button-item-horizontal.x-btn-pressed:after {
 		border-color: transparent transparent transparent #5fa2dd;
 	}

 	/* eliminate right edge border that Ext JS puts into place. */
 	.timeline .x-btn-focus.x-btn-pressed {
 		box-shadow: none !important;
 	}

 	/* set hover of triangle to standard hover color of button */
 	.timeline .x-btn-over + .x-segmented-button-item-horizontal:after {
 		border-color: transparent transparent transparent #5795cb;
 	}
</style>

My Review of Alien:Covenant

(mild spoilers)

I desperately wanted to like Alien Covenant, if not simply for the fact that they killed off James Franco within the first 5 minutes. Unfortunately, I found it to be a grossly derivative rehashing of “best of” moments from other Alien and rogue AI movies. By the time the credits roll, it’s clear that the “Engineers” from Prometheus not only developed the xenomorphs, but also created a virus that activated in 1987 and wiped out all creativity in Hollywood.

The plot is basically this:

In about 100 years, humanity has advanced to the point where we can create huge interstellar spaceships crewed with absolute morons, Danny McBride, and the daughter of actor Sam Waterston. A solar flare interrupts their trip so that they can receive a recorded message from the sole survivor of Prometheus mission singing “Country Roads” and change course to investigate.

No, I am not making this up.

Once they arrive it becomes quite evident that not a single member of the crew or the ship’s other 2000 inhabitants who are tasked with colonizing another planet, were ever trained in exobiology. Also, none of them have apparently ever seen a James Cameron or Ridley Scott movie before. They all received automatic weapons training, however. So I’m thinking that in the year 2100, everyone is a Republican.

On the bright side, the crew member who lights up a Marlboro on the strange, new planet is the first one to die.  Apparently he never got the Surgeon General’s memo that smoking and alien spores are bad for your health.

This, of course, is followed in short order by the blatant ignoring of quarantine procedures and series movie continuity.  Also, we see the use of firearms within a spaceship because in the future, there’s nothing that could possibly go “boom” on a spaceship. Oh, wait… no.

Anyway, the surviving crew members meet up with the android “David” from Prometheus who’s been living on this alien world for 10 years. Apparently, “David” has developed a deep hatred of humanity because his creator foolishly gave him access to Facebook and Instagram. This triggers his built-in HAL-9000 subroutines to further drive the plot towards an ending that should surprise absolutely no one at all.

In summary, a more appropriate title for this movie would have been “Alien: Been there, done that.”