With the ability to add lightning components to flow screens now I wanted build a component that could be used with flows that can display a table of data. Naturally I thought of the lightning:datatable component. The question is, how can I build a component that can be used in a flow screen and be able to pass data and column details to the component from the flow.
I originally wanted to cover building the components and the flow in the same post, but it seemed like a longer read than I expected so I will be splitting this into two posts instead. In this post I will review the two custom lightning components I built for this project.
Search Results Component
I created a component labeled as “FlowDataTableCmp”.
To allow the component to be added to a flow screen the it needs to implement lightning:availableForFlowScreens. The key component here is lightning:datatable, which is what we are using to display the lead search results.
The component contains the following attributes:
- dataArr: A string array consisting of comma separated field values that will be parsed and used to update the data attribute.
- data: This attribute will be used to set the data of the lightning data table component.
- columnsStr: A string with column detail separated by a semi-colon. Each semi-colon separated value will in turn have column object details separated by commas. The format would look like ,,;,,
- Columns: We will parse the value stored in columnsStr and build a js objects to be stored in this attribute used to define the columns of the data table.
- maxRowSelection: Used to limit the selection of rows to only 1 (since user should only choose one matching record).
- recordId: Attribute will store the Id of the selected record from the data table.
- Key: key field of the data table
<aura:component implements=”lightning:availableForFlowScreens” access=”global”>
<aura:attribute name=”recordId” type=”String” />
<div style=”height: 300px”> </aura:component> |
The handler will execute the doInit controller function where it will parse column and data provided to the component.
The lightning:datatable component will execute the setRecordId controller function once a row has been selected.
Component Design
A design file needs to be created so that the component attribute is available for input and output selection from the flow.
<design:component > <design:attribute name=”dataArr” label=”Data Array” required=”true”/> <design:attribute name=”columnsStr” label=”Columns” required=”true”/> <design:attribute name=”recordId” label=”Record Id” /></design:component> |
Here we are simply adding design attributes for the dataArr, columnsStr, key and recordId component attributes.
Search Results Javascript Controller
doInit
The first function of the controller is doInit, which will execute at initialization.
The first half of the function we will be taking the value of v.columnsStr and parsing the values. We will split the string value by the semi-colon character to convert the string into a string array (colStrArr) with each index value made up of the comma-separated value. For each value of the first array we will split each comma-separated value by the comma character (colDetailArr). After verifying that the resulting array from the split function results in an array of size 3, we will create a JS object with the following properties:
- Label: What is displayed in the column header. The first index of colDetailArr.
- Fieldname: Background field name for the column, used when loading data to the table. The second index of colDetailArr.
- Type: The data type of the column. Third index of colDetailArr.
An example of the values parsed would look like this: First Name,firstname,text;Last Name,lastname,text.
Each column object will be pushed into the colArr array and subsequently stored in the v.columns attribute. The column field name will also be pushed into the fieldNameArr array to be used in the latter half of the function where we will be loading the table data.
doInit : function(component, event, helper) { var dataArr = new Array(); var colsStr = component.get('v.columnsStr'); var fieldNameArr = new Array(); if(colsStr){ console.log(colsStr); var colStrArr = colsStr.split(';'); if(colStrArr){ console.log(colStrArr); var colArr = new Array(); for(var i = 0; i < colStrArr.length; i++){ console.log(colStrArr[i]); var colDetailArr = colStrArr[i].split(','); console.log(colDetailArr); if(colDetailArr.length === 3){ var colObj = {label: colDetailArr[0], fieldName: colDetailArr[1], type: colDetailArr[2]}; console.log(colObj); colArr.push(colObj); fieldNameArr.push(colDetailArr[1]); } } component.set('v.columns', colArr); } } var dataStrArr = component.get('v.dataArr'); console.log(dataStrArr); if(dataStrArr){ for(var j = 0; j < dataStrArr.length; j++){ var fieldArr = dataStrArr[j].split(','); if(fieldArr.length === fieldNameArr.length){ var jsonStr = '{'; for(var k = 0; k < fieldArr.length; k++){ var delimeter = k === (fieldArr.length - 1) ? '' : ','; jsonStr += '"' + fieldNameArr[k] + '" : ' + '"' + fieldArr[k] + '" ' + delimeter; } jsonStr += '}' console.log(jsonStr) dataArr.push(JSON.parse(jsonStr)); } } component.set('v.data', dataArr); } },
The second half of the function will be processing the table data provided to the component in v.dataArr. The array values consists of field values separated by commas. The function assumes that the order of the field values are the same as the column header values passed in the columnsStr attribute.
Each string value of the array is then split by the comma character to generate an array of values for that specific record. The new array is iterated through and a json string is constructed with the corresponding field name as key and the field value as the pair value. The completed json string is then converted into a json object and pushed into the dataArr array which in turn will be stored in v.data to be loaded as table data values.
setRecordId
The second function of the controller will be handle the event of when the user select a row on the table. It will get the selected row and pull the value of the field that we have specified as the key.
setRecordId : function(component, event, helper){ var selectedRows = event.getParam('selectedRows'); var key = component.get('v.key'); if(selectedRows){ if(selectedRows.length === 1){ console.log(selectedRows[0][key]); component.set('v.recordId', selectedRows[0][key]); } } },
Here is how component could look like when added to a flow screen.
Record Edit Component
If a user selects an existing lead record to update instead of creating a new record I we want to provide a way for user to update the record with new changes. To accomplish that let’s build a component that will use the force:recordEdit component that will load the edit page onto the flow screen. the force:recordEdit component includes built in functionality to display fields already on the object page layout and to save the changes to the record.
Attributes for the component:
- recId: Accepts the Id value of a sObject record.
- showMsg: Controls whether or not to display the ui:message component.
<aura:component implements=”lightning:availableForFlowScreens” access=”global”>
<aura:if isTrue=”{!v.showMsg == FALSE}”> <aura:set attribute=”else”> </aura:component> |
The force:recordEdit component will be displayed while the showMsg attribute is false. We will also have a lightning button to allow the user to submit the record for saving which will call the saveRecord controller function.
Successful saves will be handled by the onSaveSuccess handler and will execute the saveSuccessful function. Unfortunately there did not seem to be an onSaveError event for the component. There is an idea for this but does not seem to be picking up traction. Support might be moving in favor of lightning:recordEdit instead, however, I like the simplicity of force:recordEdit.
Component Design
To allow any selected record to be passed to component a design file is created with an design attribute referencing the recId component attribute.
<design:component > <design:attribute name=”recId” label=”Record Id” /> </design:component> |
The Controller
The javascript controller consists of two short functions:
- saveRecord: Fetches the force:recordEdit component by id and fires the built-in e.recordSave event to initiate the record save.
- saveSuccessful: Handles the force:recordSaveSuccess by setting the showMsg attribute to true which will hide the force:recordEdit component and display the ui:message component with the success message.
({ saveRecord : function(component, event, helper) { console.log('saving record'); component.find("recEdit").get("e.recordSave").fire(); }, saveSuccessful : function(component, event, helper){ console.log('handling successful save'); component.set('v.showMsg', true); }, })
Here is how the component would look like when added to a flow screen.
Note that although the goal is to make a lead entry flow the component’s I have built a fairly generic and can be used for other purposes as well. You can check out the components in the github repo I created as well. Stay tuned for the follow-up post where I cover how to build the a visual flow as an example scenario of how to utilize the two custom components!
Nice!
LikeLiked by 1 person
this is excellent work, had been using old HTML tables previously to display multiple records and this is a huge improvement. I’m using this lightning component on a homepage in the right column (ie. small display) and was wondering if you had any thoughts on how to size the three columns so everything can be displayed without getting cut off? my title on column three is getting cut off and column one doesnt need as much space as it is automatically given. Wondering if you have any ideas as i saw you put some inline css for height but columns seem a little trickier
Great job
LikeLike
Glad you found the component helpful! Have you checked out the lightning datatable component specs? The min and max column width attributes might be what you need. https://developer.salesforce.com/docs/component-library/bundle/lightning:datatable/specification
LikeLike