The lightning:tab component displays a tabbed view of data for users providing an organized way to present information. My latest addition to the list of custom aura components for flows I have developed leverages lightning tabs to load different components into view when running a flow screen.
The example for this post will be creating a flow where one can search for an account record by name and load the account record, the related contacts and opportunities onto the flow screen in separate tabs. Each tab will allow the user to edit the record. The example will reference the custom FlowEditRecordCmp component I built previously. Read this blog post if you want to familiarize yourself with the component before continuing.
Building the component to handle multiple tabs
To be able to handle loading multiple tabs dynamically I built two custom components.
FlowTabSetCmp: Iterates through an array of string values that contain tab component information (tab label, the inner component that needs to be loaded within the tab, component attributes, etc.)
FlowTab: Loads a single lightning tab along with the nest component by using the definition provided by parent FlowTabSetCmp.
FlowTabSetCmp
This component is the parent that is responsible for loading the custom FlowTab component. It has a single attribute that stores an array of strings. The array contains the definition of the different tabs that needs to be loaded. The following example is the format it is expecting.
[Tab Name],[Nested Component Name],[Nested Component Attribute Label 1];[Nested Component Attribute Value 1],[Nested Component Attribute Label 2];[Nested Component Attribute Value 2],….
The values are comma-separated overall and then delimited by semi-colon for the attribute name and value pairs. The first two values are always the tab name and the name of the component that needs to be loaded within the lightning:tab standard component. The remaining values will be the attribute values for that nested component.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<aura:component implements="lightning:availableForFlowScreens" access="global"> | |
<!–Attributes–> | |
<aura:attribute type="String[]" name="cmpAttrArr" /> | |
<lightning:tabset > | |
<aura:iteration items="{!v.cmpAttrArr}" var="cmpAttr"> | |
<c:FlowTab cmpAttrs="{!cmpAttr}"/> | |
</aura:iteration> | |
</lightning:tabset> | |
</aura:component> |
Within the lightning:tabset component is an aura:iteration where the custom FlowTab component will be loaded based on the number of tabs provided. Each individual tab definition is set as the cmpAttr attribute’s value.
In order to allow the tab definition data to be supplied by a flow, the attribute must be made available by defining it in the design file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<design:component> | |
<design:attribute name="cmpAttrArr" label="Component Tab Definitions" required="true"/> | |
</design:component> |
FlowTab Component
The FlowTab component consists of a single lightning:tab standard component. Nested within the component is the body attribute. This is where we will be dynamically loading the component specified in the tab definition stored in the cmpAttrs attribute.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<aura:component implements="lightning:availableForFlowScreens" access="global"> | |
<!–Attributes–> | |
<aura:attribute type="String" name="tabName" default=""/> | |
<aura:attribute type="String" name="cmpAttrs" default=""/> | |
<aura:attribute type="Boolean" name="showTab" default="true" /> | |
<!–Handlers–> | |
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/> | |
<aura:if isTrue="{!v.showTab}"> | |
<lightning:tab label="{!v.tabName}" aura:id="lTab"> | |
{!v.body} | |
</lightning:tab> | |
</aura:if> | |
</aura:component> |
There is a handler that executes the doInit controller function when the component is loaded. The controller function handles the logic of parsing the component definition and dynamically creating the nested component. The showTab boolean attribute is used in an aura:if component. This will come into play when building the javascript controller.
FlowTab Component Controller
The doInit function will get the value of the cmpAttrs attribute value and split the values by the comma character in order to store the values into an array of string. Since we are expecting the the first two indices to store the value of the tab label and nest component name respectively, we will iterate through the array starting at the third index to start processing any component attributes. Each of the key / value pairs are delimited by semi-colon which will require us using the split function again to store the name and value separately in an array . The attribute name (first index) is used for the property name of the attrVals object variable and the attribute value (second index) is used to set the property value.
({ doInit : function(component, event, helper) { var cmpAttrs = component.get('v.cmpAttrs');cmpAttrs_ var attrVals = {}; console.log('cmpattr: ' + cmpAttrs); if(cmpAttrs){ var cmpAttrArr = cmpAttrs.split(','); component.set('v.tabName', cmpAttrArr[0]); for(var i = 2; i < cmpAttrArr.length; i++){ var indvAttrArr = cmpAttrArr[i].split(';'); attrVals[indvAttrArr[0]] = indvAttrArr[1]; } console.log(attrVals); .......................................................
The second half of the function is where we will dynamically create the nested component using the $A.createComponent method and push the new component onto the body in the callback function. Note that we will using the value provided from the flow for the component type and the attrVals object to pass attributes to the method.
At the end of the callback we are toggling the showTab attribute to false then to true in order to update the lightning:tab label.
............................................ $A.createComponent( cmpAttrArr[1], attrVals, function(newCmp, status, errorMessage){ if (status === 'SUCCESS') { console.log('cmp created'); var body = component.get('v.body'); body.push(newCmp); component.set('v.body', body); component.set('v.showTab', false); setTimeout(function(){ component.set('v.showTab', true); }, 500) } } ); } } })
Creating the Flow
The flow consists of two screens. One to prompt the user to enter an account name and at the end of the flow to display a screen that loads the flow component.
After entering a name we will be retrieving a matching account record. The record name and Id is stored into a sObject variable. The flow will continue to retrieve any related contact and opportunity records using the account Id. Contacts and opportunities will be stored in sObject collection variables storing Id and name as well. I probably should check if there was a valid account record returned by querying by name, but in the interest of simplicity I omitted these usual validations.
Once all records have been retrieved we will start defining the tab component definitions that will we pass to the FlowTabSetCmp component. Except for the account record, a loop is used to process each record we need to display as a tab. I created text formula fields to generate the expected format.
The above screen shot shows an example of the formulas. Each record will have its own definition added to a string collection variable named tabs.
Once all tab definitions have been generated we can pass tabs collection variable to the component on the screen.
The components will process the tab definition and present each record as tab loading the lighting:recordEdit component.
Even though I have focused on loading recordEdit components within the tabs, the FlowTabSetCmp and FlowTab components are fairly generic so you can use it to fit your organization’s needs. All code and the flow covered in this post can be found in this Github repo.
Until next time!
I cannot get the first tab to load without clicking onto another tab and then returning to the initial tab. Any ideas?
LikeLike
AMAZING! One question: If one account with multiple Opportunity so there will be multiple oppty tabs in the screen, right? If there’re 100 oppties associated with that account, is there limitation or performance issue in this flow? Thanks.
LikeLike
Thanks! I have not done a stress test of this, but I would assume the more tabs loaded would result in performance issues. I would refer to the documentation lightning:tab https://developer.salesforce.com/docs/component-library/bundle/lightning:tab/documentation . Also there is mention of tab limits in this article, but it is specifically for the app builder. https://help.salesforce.com/articleView?id=sf.lightning_app_builder_limitations.htm&type=5
LikeLike