Andy in the Cloud

From BBC Basic to Force.com and beyond…

Overriding Standard Actions with Lightning Components

26 Comments

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.

LightningActionsOverview.png

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.

Screen Shot 2017-05-29 at 14.41.09.png

<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.

WidgetEdit.png

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!

SaveToast.png

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.

LightningColorPicker.png

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.

26 thoughts on “Overriding Standard Actions with Lightning Components

  1. Overriding Standard New button will not work in Salesforce 1. That could be a limitation. What is your thought on this ?

    • 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.

      • 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.

      • This is a platform issue really. Can you not manage this via profiles / permission sets and different record type assignments?

  2. 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 ?

  3. Great Article! How will it behave in Salesforce classic?

    • 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

  4. Pingback: Showing a context sensitive pop-up in Classic & Lightning Experience detail pages – My Salesforce adventure

  5. 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.

    • 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.

  6. 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

    • 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.

    • 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.

      • 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.

  7. thank you Andy for this detailed and easily understandable explanation. it really helped me. Kudos!

  8. When we build Community using Community builder then override Actions are not working. Is it limitation? or do we have any workaround for it.

    • 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!

  9. 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!

  10. I have the problem is the new button on community relate list become hidden if if override action with lightning component

  11. Pingback: [Solved] Determine source of New standard action in LEX – Ten-tools.com

Leave a Reply to Jayesh Bhatnagar Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s