Apex Callable Interface

In Winter’19 release Salesforce introduced Callable interface in apex. The interface enables developers to use a common interface to build loosely coupled integrations between Apex classes or triggers, even for code in separate packages.

This interface has one method call(String action, Map args). It helps developer to call a method dynamically with the help of “Type” class.

Example:

public class SampleCallable implements Callable {
    
    //Method 1
    public String method1(String stringValue) {
        return 'Welcome ' + stringValue;
    }
    
    //Method 2
    public Decimal method2(Integer integerValue) {
        return integerValue + 100;
    }
    
    //Callable interface method
    public Object call(String action, Map<String, Object> args) {
        switch on action {
            when 'method1' {
                return this.method1((String)args.get('stringValue'));
            }
            when 'method2' {
                return this.method2((Integer)args.get('integerValue'));
            }
            when else {
                return null;
            }
        }
    }
}

Execute:

Callable c = (Callable)Type.forName('SampleCallable').newInstance();
system.debug(c.call('method1', new map<String,Object>{'stringValue' => 'Biswajeet'}));
system.debug(c.call('method2', new map<String,Object>{'integerValue' => 5}));

Cacheable Apex Method

To improve the performance of the lightning component cache Salesforce introduced storable (cacheable) in Winter’19 release. In API version 44.0 and later, you can mark an Apex method as storable (cacheable) instead of using setStorable() on every JavaScript action that calls the Apex method to centralize your caching notation for a method in the Apex class.

Here is an example to cache data returned from an Apex method for lightning component, annotate the Apex method with @AuraEnabled(cacheable=true).

Apex Class:

public class SampleAuraController {
    
    @AuraEnabled(cacheable = true)
    public static List<Account> getDirectCustomerAccount() {
        List<Account> accList = new List<Account>();
        accList = [SELECT Id, Name, Type, Industry, Phone
                   FROM Account WHERE Type = 'Customer - Direct'];
        return accList;
    }
}

Lightning Component:

<aura:component controller="SampleAuraController" implements="flexipage:availableForAllPageTypes,force:appHostable">
    
    <!--Declare Event Handlers-->
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    
    <!--Declare Attributes-->
    <aura:attribute name="accList" type="Account[]"/>
    
    <!--Component Start-->
    <div class="slds-m-around_xx-large">
        <table class="slds-table slds-table_cell-buffer slds-table_bordered slds-table_col-bordered">
            <thead>
                <tr class="slds-line-height_reset">
                    <th class="slds-text-title_caps" scope="col">
                        <div class="slds-truncate" title="Account Name">Account Name</div>
                    </th>
                    <th class="slds-text-title_caps" scope="col">
                        <div class="slds-truncate" title="Account Type">Account Type</div>
                    </th>
                    <th class="slds-text-title_caps" scope="col">
                        <div class="slds-truncate" title="Industry">Industry</div>
                    </th>
                    <th class="slds-text-title_caps" scope="col">
                        <div class="slds-truncate" title="Phone">Phone</div>
                    </th>
                </tr>
            </thead>
            <tbody>
                <aura:iteration items="{!v.accList}" var="acc">
                    <tr class="slds-hint-parent">
                        <th data-label="Account Name" scope="row">
                            <div class="slds-truncate" title="{!acc.Name}"><a href="javascript:void(0);" tabindex="-1">{!acc.Name}</a></div>
                        </th>
                        <td data-label="Account Type">
                            <div class="slds-truncate" title="{!acc.Type}">{!acc.Type}</div>
                        </td>
                        <td data-label="Industry">
                            <div class="slds-truncate" title="{!acc.Industry}">{!acc.Industry}</div>
                        </td>
                        <td data-label="Phone">
                            <div class="slds-truncate" title="{!acc.Phone}">{!acc.Phone}</div>
                        </td>
                    </tr>
                </aura:iteration>
            </tbody>
        </table>
    </div>
    <!--Component End-->
</aura:component>

Lightning JS Controller:

({
    doInit : function(component, event) {
        var action = component.get("c.getDirectCustomerAccount");
        action.setCallback(this, function(response) {
            var state = response.getState();
            if(state === "SUCCESS"){
                var result = response.getReturnValue();
                component.set("v.accList", result);
            }
        });
        $A.enqueueAction(action);
    }
})

Output:

Set Lightning Tab Label Dynamically

Using lightning:workspaceAPI we can change Tab Label dynamically. Basically this component allows to access methods for programmatically controlling workspace tabs and subtabs in a Lightning console app. Here is an example to change Account record Tab Label “Account Name” to “Account Type” with “Account Name”.

Account Standard Tab Label:

Lightning Component:

<aura:component implements="flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId" access="global">
    
    <!--Attributes-->
    <aura:attribute name="accFields" type="Account"/>
    
    <!--Component Start-->
    
    <!--Lightning Workspace API-->
    <lightning:workspaceAPI aura:id="workspace"/>
    
    <!--Lightning Force Data to get Account record-->
    <force:recordData aura:id="accRecordData"
                      layoutType="FULL"
                      recordId="{!v.recordId}"
                      targetFields="{!v.accFields}"
                      recordUpdated="{!c.handleRecordUpdate}"/>
    <!--Component End-->
</aura:component>

Lightning JS Controller:

({
    handleRecordUpdate: function(component, event, helper) {
        var accountFields = component.get("v.accFields");
        var workspaceAPI = component.find("workspace");
        workspaceAPI.getFocusedTabInfo().then(function(response) {
            var focusedTabId = response.tabId;
            workspaceAPI.setTabLabel({
                tabId: focusedTabId,
                label: accountFields.Type + ' - ' + accountFields.Name
            });
        })
    }
})

Add above created lightning component on Account Lightning Record Page.

Output:

Invoke Flow From Lightning Component With Input Variables

Using lightning:flow component, we can invoke a flow in Lightning runtime. This component requires API version 41.0 and later. Here is an example to delete all Contact records from an Account using a flow.

Here I’ve created a flow and the flow is invoked by a lightning component, and that lightning component is configured with a QuickAction on Account object.

1. Go to Setup | Quick Find – Search Flows | New Flow

2. Create a variable as “AccountId”

3. Create a sObject Collection Variable as “CollectionOfContacts”

4. Create a Fast Lookup to get Contacts with Account Id

5. Create a Decision to check Account has contacts.

6. Default Outcome in Decision

7. Create Fast Delete to delete Contacts

8. Set the Fast Lookup as a starting element, indicated by the green down arrow. Then, connect the flow element “Fast Lookup” to “Decision” and “Decision” to “Fast Delete”.

9. Create a Lightning Component to invoke the flow.

Lightning component:

<aura:component implements="force:lightningQuickActionWithoutHeader,flexipage:availableForRecordHome,force:hasRecordId">
    
    <!--Custom Styles for Modal Header and Footer--> 
    <aura:html tag="style">
        .cuf-content {
        padding: 0 0rem !important;
        }
        .slds-p-around--medium {
        padding: 0rem !important;
        }       
        .slds-modal__content{
        overflow-y:hidden !important;
        height:unset !important;
        max-height:unset !important;
        }
    </aura:html>
    
    <!--Declare Attributes--> 
    <aura:attribute name="hasError" type="Boolean" default="false"/>
    
    <!--Modal Header-->  
    <div class="modal-header slds-modal__header slds-size_1-of-1">
        <h4 class="title slds-text-heading--medium">Delete Contacts</h4>
    </div>
    <!--End Modal Header-->  
    
    <!--Modal Body-->   
    <div class="slds-modal__content slds-p-around--x-small slds-align_absolute-center slds-size_1-of-1 slds-is-relative">
        <form class="slds-form--stacked">
            <aura:if isTrue="{!!v.hasError}">
                <div class="slds-align_absolute-center">
                    Do you want to delete all contacts?
                </div>
                <aura:set attribute="else">
                    <div>
                        <div class="slds-text-color_error">Error on Contact record deletion.
                            Please forward following error message to your admin:</div>
                    </div>
                </aura:set>
            </aura:if>
            <div>
                <!--Lightning Flow Attribute-->
                <lightning:flow aura:id="deleteContactFlow" onstatuschange="{!c.statusChange}"/>
            </div>
        </form> 
    </div>
    <!--End of Modal Body--> 
    
    <!--Modal Footer-->
    <div class="modal-footer slds-modal__footer slds-size_1-of-1">
        <lightning:button variant="Brand" class="slds-button" label="Confirm" onclick="{!c.handleConfirm}"/>
        <lightning:button variant="Neutral" class="slds-button" label="Cancel" onclick="{!c.handleClose}"/>
    </div>
    <!--End of Modal Footer-->
</aura:component>

Lightning JS Controller:

({
    //Confirm 
    handleConfirm : function(component, event, helper) {
        //Find lightning flow from component
        var flow = component.find("deleteContactFlow");
        //Put input variable values
        var inputVariables = [
            {
                name : "AccountId",
                type : "String",
                value : component.get("v.recordId")
            }
        ];
        //Reference flow's Unique Name
        flow.startFlow("Delete_Contact_From_Account", inputVariables);
    },
    
    //Close the quick action
    handleClose : function(component, event, helper) {
        $A.get("e.force:closeQuickAction").fire();
    },
    
    //Flow Status Change
    statusChange : function (component, event, helper) {
        //Check Flow Status
        if (event.getParam('status') === "FINISHED_SCREEN" || event.getParam('status') === "FINISHED") {
            var toastEvent = $A.get("e.force:showToast");
            toastEvent.setParams({
                title: "Success!",
                message: "Contacts deleted successfully!",
                type: "success"
            });
            toastEvent.fire();
            $A.get("e.force:closeQuickAction").fire();
            $A.get('e.force:refreshView').fire();
        } else if (event.getParam('status') === "ERROR") {
            component.set("v.hasError", true);
        }
    }
})

10. Configure above created lightning component on Account object Quick Action.

11. Add created quick action on Account object page layout.

12. Here is the quick action to delete all contacts.