Deep Clone using Headless Actions in LWC

A headless quick action executes custom code in a Lightning web component. Unlike a screen action, a headless action doesn’t open a modal window. So in short, if you want to do some custom logic via apex to run by click of a quick action button we can use headless actions in LWC.

Below are a few examples/use cases where we can implement headless actions.
1. A custom clone button (With Deep Clone)
2. A custom approval button.
3. Submit data to an external system.
4. Enrich the record with details from an external system etc.

Configure a Component for Quick Actions

To use a Lightning web component as a quick action, define the component’s metadata. Specifically to use an LWC as headless action use the below XML for the meta XML file. Note that the <actiontype is Action for a headless quick action and if you would like to enable the component as a screen popup (regular) include actionType as ScreenAction.

<?xml version="1.0" encoding="UTF-8" ?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
   <apiVersion>52.0</apiVersion>
   <isExposed>true</isExposed>
   <targets>
       <target>lightning__RecordAction</target>
   </targets>
    <targetConfigs>
   <targetConfig targets="lightning__RecordAction">
     <actionType>Action</actionType>
   </targetConfig>
 </targetConfigs>
</LightningComponentBundle>

Implementation

Now that we have the setup ready, the custom logic that you need to implement must be provided with the js file of the LWC. The HTML file could be empty with just the template tags.
In your Lightning web component, expose invoke() as a public method. The invoke() method executes every time the quick action is triggered.

import { LightningElement, api } from "lwc";

export default class HeadlessSimple extends LightningElement {
  @api invoke() {
    console.log("This is a headless action.");
  }
}

Example Scenario

So now if you want to use a deep clone logic from the quick action button the code looks like this.

import { LightningElement, api } from "lwc";
import { ShowToastEvent } from 'lightning/platformShowToastEvent'
import { NavigationMixin } from 'lightning/navigation';
import startDeepClone from '@salesforce/apex/ROR_CloneController.startDeepClone';

export default class Ror_deepcloneaction extends NavigationMixin(LightningElement) {

    @api invoke() {
        this.startToast('Deep Clone!','Starting cloning process...');
        //Call the cloning imperative apex js method
        this.startCloning();
    }
    
    startCloning(){
        startDeepClone({recordId: this.recordId})
        .then(result => {
            this.startToast('Deep Clone!','Cloning Process Completed');
            this.navigateToRecord(result);
         })
         .catch(error => {
            this.startToast('Deep Clone!','An Error occured during cloning'+error);
         });
    }

    startToast(title,msg){
        let event = new ShowToastEvent({
            title: title,
            message: msg,
        });
        this.dispatchEvent(event);
    }

    navigateToRecord(clonedRecId){
        this[NavigationMixin.Navigate]({
            type: 'standard__recordPage',
            attributes: {
                recordId: clonedRecId,
                actionName: 'view',
            },
        });
    }

}

You can see that on click of the quick action button, the invoke method gets executed, that in turn calls the imperative apex call; does the clone; returns the new clone record’s id and on a successful return from the apex, use an event to alert the user and use NavigationMixin to redirect the user to the cloned record.

Please access the code from the Github repo here.

Open Lightning Component as Tab from Quick Action

Most of us might have opened a lightning component from a quick action button by embedding the component in the quick action. Its a nice feature that helped us to pop up UI elements from a record page. However the component was appearing in a modal. In this blog, lets try and see how we can manage to show the component in a new tab.

Text Book Lessons

We would be using a quick action from case object. The reason I’ve chosen case object is because, few objects viz., case, user profiles and work order objects, if feed tracking is enabled, quick actions appear as chatter tab. So the first task is to disable the feed tracking on case object.

The next theory that we try to understand is regarding the isUrlAddressable component. This helps you to enables you to generate a user-friendly URL for a Lightning component with the pattern /cmp/componentName instead of the base-64 encoded URL you get with the deprecated force:navigateToComponent event. If you’re currently using the force:navigateToComponent event, you can provide backward compatibility for bookmarked links by redirecting requests to a component that uses lightning:isUrlAddressable.

Finally we need to understand lightning:navigation. This component helps to navigate to a given pageReference or to generate a URL from a pageReference.

The solution

Lets look at how this works. We create a Lightning Component (LC) that uses the lightning:navigation command to create a url from the pageReference variable. The pageReference variable defined on the controller will hold the name of the LC that needs to be opened in a new tab and also any parameters that we need to append to the URL (usually record Id). We need to use pageReference Type as ‘Lightning Component’.

QuickActionComponent.cmp

<!-- Component used on the Quick Action -->
<aura:component implements="force:lightningQuickAction, force:hasRecordId" >
    <lightning:navigation aura:id="navService"/>
    <aura:attribute name="pageReference" type="Object"/>
	<aura:handler name="init" action="{!c.navigateToLC}" value="{!this}" />
    Record Id:::: {!v.recordId}
</aura:component>

QuickActionComponent.js

({
    navigateToLC : function(component, event, helper) {
        var pageReference = {
            type: 'standard__component',
            attributes: {
                componentName: 'c__TabComponent'
            },
            state: {
                c__refRecordId: component.get("v.recordId")
            }
        };
        component.set("v.pageReference", pageReference);
        const navService = component.find('navService');
        const pageRef = component.get('v.pageReference');
        const handleUrl = (url) => {
            window.open(url);
        };
        const handleError = (error) => {
            console.log(error);
        };
        navService.generateUrl(pageRef).then(handleUrl, handleError);
    } 
})

TabComponent.cmp

<!-- Component that is opened in a new tab.-->
<aura:component implements="lightning:isUrlAddressable">
    <aura:attribute name="refRecordId" type="String" />
    <aura:handler name="init" value="{!this}" action="{!c.init}" />
    
    <div class="slds-box slds-theme_default">
        <p>This component has been opened from QuickAction button from a record with Id : {!v.refRecordId} as a tab.</p>
    </div>
    
</aura:component>

TabComponent.js

({
	init : function(component, event, helper) {
		var pageReference = component.get("v.pageReference");
        component.set("v.refRecordId", pageReference.state.c__refRecordId);
	}
})

All set. Lets click on the quick action. You can see the lightning component opened in a new tab, the url has parameter of the case record Id and its displayed on the component.

Dynamic Actions – A Low Code Lightning Approach

Introduction

In Salesforce ecosystem, we all love the word ‘dynamic’ as this brings in a lot of flexibility to the business to reuse anything that is dynamic. Quick Actions in salesforce is a great feature after we were forced to replace the JavaScript buttons when we all migrated from classic to lightning. Let it be the object specific quick actions or be it the global quick actions. Quick actions enable users to do more in Salesforce and in the Salesforce mobile app. With custom quick actions, we can make our users’ navigation and workflow as smooth as possible by giving them convenient access to information that is most important.

Scenario

Lets dive deeper with a use case. Consider we have a record page for Discount Request object with a quick action that initiates an approval process in a backend system. This action was meant for sales agents with a specific profile. The same layout was being shared with all the agents as well and they were also able to see and click the quick action. Now we have been tackling this with some validation rule on the Lightning component that integrates the approval logic. Alternatively, we were using a different record page for those profiles with and without the quick action. So how do we approach it with minimum component and a low code design?

The Solution

With Dynamic Actions, starting from Summer’20, we can prevent the other set of users not to see that action on their layout even if both set of users use the same layout/record page. So how do we do this? Let us follow the below steps:

  1. Navigate to any record page and choose the highlights panel. Select the option “Enable Dynamic Actions”.
  2. Choose ‘Add Action button’
  3. From the Search field choose your Action.
  4. Add the filter.

I have chosen the quick action to be only visible by the Sales Agent Lead Profile.

With this setup, now we display the quick action only to the Sales Agent Lead Profile The other sales agent profile who share the same record page however does not see the button.

Final Thoughts

With the rise of Citizen Developers and a low code approach across industries and customers, this feature adds a lot of flexibility to reuse the existing layouts and record page without the need to further add more component or to have logic in custom components. One limitation with Dynamic Action is that currently this is supported on record pages for custom objects alone.

Blog at WordPress.com.

Up ↑