A great deal can be done with custom Lightning Components without the need for any Apex code. The lightning:inputField component is designed to be used with the lightning:recordEditForm to provide a quick and easy way to edit records without needing an Apex controller and can be combined with force:recordData for greater flexibility. Additionally, there are declarative/configuration features of Lightning Components and App Builder that can be added into the mix to provide powerful functionality that may have previously required Apex code. This post explores the combination of all of those with a custom Lightning Component that uses no Apex code.
Quick Side Note
René Winkelmeyer (@muenzpraeger) wrote a similar post on the Salesforce Developer’s blog, including a good example with explanations of lightning:outputField and lightning:recordViewForm as well and the benefits of using input/outputField (FLS, labels, type handling, etc.). Please refer to that and the Lightning Components Developer Guide for more information.
The Component
The component is an SLA component (sla.cmp) that is designed to appear on record home pages for selected objects related to the Account object. If the SLA is near expiration or expired, the component appears for the user to update. If it is not expired or not near expiration, it does not appear.
Object Restriction and Field Configuration
Before we look at the code, let’s look at how we can support the usage of the component for selected objects related to the Account.
The component design file (sla.design) is as follows:
1 2 3 4 5 6 7 8 9 10 |
<design:component label="SLA"> <design:attribute name="accountLookupApiName" label="Acccount Lookup API Name" description="API Name of the Lookup field to the Account" /> <sfdc:objects> <sfdc:object>Contact</sfdc:object> <sfdc:object>Case</sfdc:object> <sfdc:objec>My_Object__c</sfdc:object> </sfdc:objects> </design:component> |
The sfdc:objects element supports limiting the objects whose record home pages may have the component added to them. The component will only show up in the App Builder component sidebar for those objects in the design file. For example, it will not show up in the Opportunity record home page in the App Builder. Note if there is nothing specified it is available for all.
The accountLookupApiName design:attribute is used to dynamically get the Id or the related Account. If added to the Case page, the page configurer would specify AccountId, if added to a custom object they might specify Account__c, or whatever their custom API name is.
Conditional Rendering
Another requirement is that the component should only show when the SLA is expired or nearing expiration. This can be done 100% declarativey within the App Builder with a component visibility filter.
There are a couple of interesting things to note about the visibility filter. It has the ability to look up to parent fields much like formulas or SOQL queries. This one is referencing a checkbox field. Although there is an SLA Date field, it cannot be used in the filter, because date types are not supported. The work around is to just create a checkbox formula field and reference that. There is a formula field SLA_Renewal_Needed__c that is defined as SLAExpirationDate__c < TODAY() – 5.
Fields related to the current User, as opposed to the Record, can also be added, so I could have added an additional filter to only show this component for a certain profile, for example. Additionally, multiple conditions and conditional logic can be specified.
Once saved, the component hover on the page in the App Builder shows a little orange eye icon in the upper right hand corner.
The Component Code – lightning:recordEditForm and lightning:inputField
The component is divided into two parts. One is responsible for getting the related Account’s Id from the record being viewed. The other is responsible for editing the SLA information. The SLA editing part has been abstracted into its own component (slaAcct.cmp) as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<aura:component access="global" > <aura:attribute name="recordId" type="String" access="public"/> <div class="slds-page-header" role="banner"> <p class="slds-text-heading_label"> <lightning:icon iconName="utility:error" variant="error"/> The SLA must be extended! </p> </div> <lightning:recordEditForm recordId="{!v.recordId}" objectApiName="Account"> <div class="slds-box slds-theme_default"> <lightning:messages /> <lightning:inputField fieldName="SLA__c"/> <lightning:inputField fieldName="SLASerialNumber__c"/> <lightning:inputField fieldName="SLAExpirationDate__c" /> <lightning:button class="slds-m-top_small" variant="brand" type="submit" name="update" label="Update" /> </div> </lightning:recordEditForm> </aura:component> |
The Id of the Account is provided by the containing component (sla.cmp). The code is very straightforward. It uses the lightning:recordEditForm to handle the load and save of the Account fields specified in the lightning:inputField components. The lightning:messages component is needed to display an error message. It is important to note that the declarative validations are handled completely without the need to write any JavaScript, just like Visualforce. There are also event attributes for lightning:recordEditForm (e.g., onload, onerror, onsubmit, onsuccess), for which you can provide event handlers to further customize the experience. If there is an error, the record will not be saved and an error will be displayed.
The Component Code – force:recordData
The sla.cmp is the component that is available to drag and drop onto pages in the App Builder, so it needs to be flexible enough to handle any API name for the Account lookup. (Note: this could be simplified greatly if the flexibility of any API name was not needed and it could be hardcoded as AccountId, for example.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId"> <aura:attribute name="accountLookupApiName" type="String" required="true" access="global"/> <aura:attribute name="fieldsList" type="String[]" /> <aura:attribute name="accountId" type="Id" /> <aura:attribute name="sourceRecordFields" type="Object"/> <aura:handler name="init" value="{!this}" action="{!c.doInit}"/> <aura:if isTrue="{!not(empty(v.fieldsList))}"> <force:recordData recordId="{!v.recordId}" fields="{!v.fieldsList}" targetFields="{!v.sourceRecordFields}" recordUpdated="{!c.recordUpdated}" /> <c:slaAcct recordId="{!v.accountId}"/> </aura:if> </aura:component> |
Ideally, I would have liked to just accept the accountLookupApiName and pass it to the targetFields attribute of the force:recordData, but that was not possible. The targetFields requires a String[] and accountLookupApiName is String. Some JavaScript component controller code to work around that was required.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
({ doInit: function(component) { var apiName = component.get("v.accountLookupApiName"); component.set("v.fieldsList", [apiName]); // requires a list }, recordUpdated: function(component, event, helper) { var changeType = event.getParams().changeType; if (changeType === "CHANGED" || changeType === "LOADED") { var apiName = component.get("v.accountLookupApiName"); // provided by data service var recordFields = component.get("v.sourceRecordFields"); var accountId = recordFields[apiName]; // gets passed to slaAcct component.set("v.accountId", accountId); } }, }) |
On the component initialization the fieldList is built to be a list with one element which is the API name of the lookup to the Account. Through the magic of force:recordData the sourceRecordFields object will be populated with the fields specified in fieldList. When that happens, the accountId attribute will also get populated.
The Lightning Component in Action
There is a lot more that can be done without Apex in Lightning Components. Look at the Lightning Developers Guide and check out the Lightning modules on Trailhead for more.
Good Job Peter Knolle,
Inform me
Is there a way to access or set the values of the fields in the recordEditForm?
great work Peter
Thanks for sharing with us
Fair Enough
Nice
Hi peter How can i include/Call a specific report data in lightning component
Hi,
I am using lightning:recordEditForm to create a new record. After successful creation, I want to make use of Id. Can you please help me in this. How to get Id of saved record in lightning:recordEditForm when used for record creation
When there is an error like a validation rule fails even after the error is cleared Submit fails.How do we fix that?
var err=event.getParams(“error”);
console.log(‘err’+JSON.stringify(err));