In this blog we take a look at the new Summer’17 ability to override record actions with Lightning Components. We will also see how the architecture of Lightning, is starting to emerge, by performing record read and updates via Lightning Data Service. I have shared here the full example used in this blog so you can use it as a starting point in future.
Overriding the standard actions in Lightning (also Salesforce1 Mobile) is something you might want to consider if you want to provide a more fine grained or guided user experience when users are creating or editing records.
While you can override record view this to me feels less useful given the extensive ability to customise the standard record page. The tab view can also be overridden. However unlike Classic, there is no distinction between list view and tab view, hence overriding the list view action with a Lightning Component is not applicable.
Overriding Actions with Lightning Components
Under your objects actions, where we still see Visualforce Page action overrides, we now see a new option to pick a Lightning Component Bundle. The list shows all components that implement the new interface lightning:actionOverride.
<aura:component implements="lightning:actionOverride, force:hasRecordId, force:hasSObjectName">
Component Design, Context and Navigation
You may have noticed from the first screenshot, the component is not showing in the traditional edit modal popup. Components need to provide the full user experience, thus the WidgetRecordEdit component provides a header and buttons, as well as editing ability.
The following component markup renders the header and buttons shown in the screenshot above. The componentRecord attribute is used with the Lightning Data Service component described later in this blog.
<div class="slds-page-header"> <div class="slds-media"> <div class="slds-media__figure"> <lightning:icon iconName="custom:custom67" /> </div> <div class="slds-media__body"> <p class="slds-text-body_small slds-line-height_reset">WIDGET</p> <h1 class="slds-page-header__title slds-truncate slds-align-middle" title="{!v.componentRecord.Name}">{!v.componentRecord.Name}</h1> </div> <lightning:buttonGroup> <lightning:button label="Save" onclick="{!c.onSave}" /> <lightning:button label="Cancel" onclick="{!c.onCancel}" /> </lightning:buttonGroup> </div> </div>
To get record Id you must implement at least the force:hasRecordId interface to obtain the recordId of the record when overriding the view and edit actions. When overriding other actions you will want to use the force:hasSObjectName interface.
Implementation Note: Unlike Visualforce page overrides that are explicitly linked with their object at development time and only useable with the stated object, your component can actually be assigned to actions on any object. The docs remind you of this fact and offer some best practices. However i do think a trick was missed not to support the sfdc:object qualifier in the .design file, please support this idea
The component is responsible for navigating the user back to the record view. This is done in the component client controller by sending the force:navigateToSObject event. The following code shows the code behind the Cancel button.
onCancel : function(component, event, helper) { // Navigate back to the record view var navigateEvent = $A.get("e.force:navigateToSObject"); navigateEvent.setParams({ "recordId": component.get('v.recordId') }); navigateEvent.fire(); }
Reading and Updated Records with Lightning Data Services
So now we know how to get the Id of the record to be edited and perform navigation back to the record view. How do we retrieve the record data and update it?
While we could use a service side Apex Controller for this, using Lightning Data Services integrates with cached data in Lightning and optimises network traffic. It is however limited to single records at present, which is not an issue here.
The following code uses the lightning:recordData component to query the record and fields (based on the layout definition) and later update the record. You can think of this as essentially a client side version of the Visualforce StandaradController.
<aura:attribute name="record" type="Object" /> <aura:attribute name="componentRecord" type="Object" /> <aura:attribute name="recordError" type="String" /> <force:recordData aura:id="recordLoader" recordId="{!v.recordId}" layoutType="FULL" mode="EDIT" targetRecord="{!v.record}" targetFields="{!v.componentRecord}" targetError="{!v.recordError}" />
When Save is pressed the controller code uses the saveRecord component method.
onSave : function(component, event, helper) { // Ask Lightning Data Service to save the record component.find("recordLoader").saveRecord($A.getCallback(function(saveResult) { if (saveResult.state === "SUCCESS") { // Display popup confirmation to the user var resultsToast = $A.get("e.force:showToast"); resultsToast.setParams({ "title": "Saved", "message": "The record was updated."}); resultsToast.fire(); // Navigate back to the record view var navigateEvent = $A.get("e.force:navigateToSObject"); navigateEvent.setParams({ "recordId": component.get('v.recordId') }); navigateEvent.fire(); } else { // Basic error handling component.set('v.recordError', 'Error: ' + saveResult.state + ', message: ' + JSON.stringify(saveResult.error)); } })); }
Once the record is saved the force:showToast event is used to display a confirmation message and the force:navigateToSObject event to return to the record view. Note that the record view is also internally using Lightning Data Service and is aware of the edit that has just been made by your code without having to round trip to the server!
This component makes use of Lightning Base Components for minimum markup and maximum alignment with Lightning Design System.
<lightning:layout> <lightning:layoutitem padding="around-small"> <lightning:input name="name" label="Name" type="text" value="{!v.componentRecord.Name}"/> </lightning:layoutitem> <lightning:layoutitem padding="around-small"> <lightning:input name="description" label="Description" type="text" value="{!v.componentRecord.Description__c}"/> </lightning:layoutitem> <lightning:layoutitem padding="around-small"> <lightning:input name="color" label="Color" type="color" value="{!v.componentRecord.Color__c}"/> </lightning:layoutitem> </lightning:layout>
Note the use of the HTML5 color picker via the lighnting:input component to enhance this components user experience. Since there is no Color custom field type, this would normally be rendered as a simple text field.
General Guidance
Finally before you get to carried away with overriding, consider the following guidelines.
- Administrators can override your action override! And reinstate the standard native UI. So avoid encoding any validation behaviour in your component that should be enforced via a Validation Rule and/or Apex Trigger logic.
- Consider custom Lightning Actions? Consider if its better to simply provide an alternative or advanced user experience for editing your record? If so, retain the current standard record view UI and simply add a new Lightning Action.
- Consider customising Record View? You can add your own components to the record page UI to add alternative views or calculated information. Its also possible to have components that edit details of the record. This blends the record view and inline edit a bit, so consider this carefully from a design perspective.
May 30, 2017 at 4:37 am
Overriding Standard New button will not work in Salesforce 1. That could be a limitation. What is your thought on this ?
May 30, 2017 at 11:43 pm
Thanks for sharing, yes this appears by design. My thought is per my guidelines don’t depend on overrides for your data validations and integrity enforcement. It’s feasible also to develop a component that is both a global Lightning action and an override.
August 23, 2017 at 5:11 pm
Originally, we implement record type overrides on the user level, but that’s not longer working. We would love some kind of tutorial on how to create a lightning component to override the record type selection page so we can take our users directly to one particular record type without showing them the selection page. We have 20 different record types, but a majority of our users only only need to create with one record type, the selection page is difficult for new users we onboard because we have to train them to select a certain record type. I wish we can skip that page for the most part.
August 24, 2017 at 1:07 am
This is a platform issue really. Can you not manage this via profiles / permission sets and different record type assignments?
May 30, 2017 at 7:07 pm
Really interesting post, amazingly I am also working on something like this, the issue I am facing is that, I am trying to use the properties of the lightning component while using it in the actions, but I can’t, is this a limitation or we have any work around for that ?
June 1, 2017 at 1:24 am
You cannot access the properties of the component when setting up the override. My advice would be to wrap your component and dynamically set the properties in the wrapper component you give as the override
September 12, 2017 at 5:38 pm
Thank you Andrew!
June 1, 2017 at 1:20 am
Great Article! How will it behave in Salesforce classic?
June 1, 2017 at 1:22 am
It shows the native ui in classic. My advice if you need both, is override via vf page and use lighting out to host your component, it will then show in classic and lex. Though it’s more indirect and likely some challenges with handling nav, it should in theory work
Pingback: Showing a context sensitive pop-up in Classic & Lightning Experience detail pages – My Salesforce adventure
August 7, 2017 at 3:41 pm
Is it possible to open the lightning component which are called in VF page as Popup? I’m overriding the standard button in list view. I’m successfully able to open the lightning component but it opens within frame. I need to open it as popup.
August 9, 2017 at 2:21 am
You cannot do this with standard actions, they always open in a new frame. However custom Lightning actions open in pop ups. This might not be a good fit for you though. If you want though take a look at my other blog entry on Lightning Actions.
September 12, 2017 at 2:06 pm
Thank you Andy !!!
December 17, 2017 at 5:24 pm
Hi Andy,
Is there a way I can access Parent sObject ID (Here, Account ID) in the Lightning component invoked by clicking standard “New” Button in *Related list* for Calls (Child Object) ? I would like to show my lightning component as a modal.
I am implementing interfaces force:hasRecordId and force:hasSObjectName in the lightning component.
All I can access is sObjectName for related list — “Calls”.
I could get record Id from Custom Lightning **Action** from Record home page but I am trying to override “New” button with my custom lightning component and I need Parent (Account) ID.
Please help.
Thanks, Sriram
December 28, 2017 at 10:37 pm
This is a good use case, but sadly I do not see a way of supporting this, indeed in Classic UI one had to depend on url hacking to discover it, not ideal then. I suggest we raise an Idea on IdeaExchange, would be happy to support. Sorry it’s not better news. The only workaround I can suggest is adding a component on the page or using Flow Component, to provide a ui for creating the child records. Neither would look right since they would be buttons elsewhere on the page, but better than nothing.
January 4, 2018 at 11:48 pm
As of now I have added lightning component to a VF page where I am retrieving Parent ID from URL, not an ideal approach though. And overridden standard New button with VF Page, so that I can access the page from related list and child object’s tab as well.
Will raise an idea and share the link.
Thanks for your time.
June 6, 2018 at 6:35 pm
Please review and Upvote if it looks reasonable
https://success.salesforce.com/ideaView?id=0873A000000EA1IQAW
January 3, 2018 at 10:24 am
The hack I’ve used for this is to use the id of the last Account viewed, but if this object is ever created from somewhere other than the related list it won’t work.
January 4, 2018 at 11:56 pm
Good idea, I haven’t yet tried this approach. But yes, I have to use this from object’s tab as well, may not be helpful in my case.
Thanks.
December 17, 2017 at 10:19 pm
thank you Andy for this detailed and easily understandable explanation. it really helped me. Kudos!
February 1, 2018 at 12:39 am
When we build Community using Community builder then override Actions are not working. Is it limitation? or do we have any workaround for it.
February 6, 2018 at 5:59 pm
It’s looks like this was a limitation, but… I am sure I noticed in the Spring 18 release notes that this has now been addressed. Please check for yourself, hopefully good news!
March 7, 2018 at 8:57 am
Excellent and very clearly explained article. Accessing parent records id on a related list is really a big deficiency in lightning causing lot of us to jump through hoops. Hopefully salesforce will solve this soon!
May 16, 2018 at 2:10 am
I have the problem is the new button on community relate list become hidden if if override action with lightning component
May 18, 2018 at 4:43 pm
This sounds like it could be desired behavior see docs only mention mobile and desktop https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/lightning_component_actions.htm
Pingback: [Solved] Determine source of New standard action in LEX – Ten-tools.com