Category Archives: Salesforce

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.

Inherited Sharing in Apex Class

Now you can specify the Inherited Sharing keyword on an Apex class, which allows the class to run your apex code with or without sharing settings, depending on the class that called it.

  • An Apex class with Inherited Sharing enables you to pass security review and ensure that your privileged Apex code is not used in unexpected or insecure ways.
  • An Apex class with Inherited Sharing runs as with sharing when used as a Visualforce page controller, Apex REST service, or an entry point to an Apex transaction.
  • An Apex class with Inherited Sharing is being called from some other class which is having without sharing setting, then it will run in without sharing mode.

Here is an example of an Apex class with Inherited Sharing and a Visualforce invocation of that Apex class. Here the running user sharing access contacts will be displayed. If the declaration Inherited Sharing will be omitted, even contacts that the user has no rights to view will be displayed due to the insecure default behavior of omitting the declaration.

Apex Class:

public inherited sharing class InheritedSharingClass{
    public List<Contact> getAllContacts(){
        return [SELECT Name FROM Contact];
    }
}

Visualforce Page:

<apex:page controller="InheritedSharingClass">
    <apex:repeat value="{!AllContacts}" var="record">
        {!record.Name}
    </apex:repeat>
</apex:page>

Salesforce Mass Quick Actions in Lightning Experience

In Spring’18 release, Salesforce introduced Mass Quick Actions in Lightning Experience on ListView. Using Mass quick action users can do mass action on the selected records (Up to 100 records) in a list view, except for recently viewed lists. We can use mass quick action with cases, leads, accounts, campaigns, contacts, opportunities, work orders, and custom objects in Lightning Experience.

Here is an example of Mass Quick Action. In below example I’ve craeted a Mass Quick Action on Account object to update mass records Account Type field from List View.

1. Create New Quick Action Button

Setup | Object Manager Tab | Navigate to Account object | Click Buttons, Links and Actions menu | Click New Action button

Create New Quick Action with following values:

2. Add Quick Action Button to Layout

Go to Account Object | Click Search Layouts menu | Select Edit under ListView

Got to “List View Actions in Lightning Experience” section, and add “Update Customer Type” Quick Action and save it.

3. Open Account Tab

Click the Account tab | Select a view except Recently Viewed | You can see “Update Customer Type” button.

4. Select Account Rcords to Update

Select multiple Account records, up to 100 records from the list view. Click “Update Customer Type” button, and select a Type from picklist values, and click Save button, and then OK button to confirm.

Now, all selected account records Type field will be updated with new value.

Get Picklist Values Dynamically In Lightning Radio Group Component

In below example I’m loading Account object Industry picklist field values in Lightning Radio Group.

Apex Controller:

public class SampleController {
    
    @AuraEnabled //Save Account Data
    Public static void createAccount(Account objacc){
        try{
            //Insert Account Record
            insert objacc; 
            
        }catch(Exception e){
            //throw exception message
            throw new AuraHandledException(e.getMessage());
        }
        finally {
        }
    }
    
    @AuraEnabled //get Account Industry Picklist Values
    public static Map<String, String> getIndustry(){
        Map<String, String> options = new Map<String, String>();
        //get Account Industry Field Describe
        Schema.DescribeFieldResult fieldResult = Account.Industry.getDescribe();
        //get Account Industry Picklist Values
        List<Schema.PicklistEntry> pList = fieldResult.getPicklistValues();
        for (Schema.PicklistEntry p: pList) {
            //Put Picklist Value & Label in Map
            options.put(p.getValue(), p.getLabel());
        }
        return options;
    }
}

Lightning Component:

<!--Sample.cmp-->
<aura:component controller="SampleController" implements="flexipage:availableForAllPageTypes,force:appHostable">
    
    <!--Declare Attributes-->
    <aura:attribute name="industryMap" type="Map"/>
    <aura:attribute name="acc" type="Account" default="{'sobjectType':'Account', 
                                                       'Name': '',
                                                       'AccountNumber': '',
                                                       'Email': '',
                                                       'Phone': '', 
                                                       'Industry': ''}"/>
    
    <!--Declare Handler-->
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>  
    
    <!--Component Start-->
    <div class="slds-m-around--xx-large">
        <div class="container-fluid">
            <div class="form-group">
                <lightning:input name="accName" type="text" required="true" maxlength="50" label="Account Name" value="{!v.acc.Name}" />
            </div>
            <div class="form-group">
                <lightning:input name="accNumber" type="text" required="true" maxlength="10" label="Account Number" value="{!v.acc.AccountNumber}" />
            </div>
            <div class="form-group">
                <lightning:input name="accEmail" type="email" required="true" maxlength="100" label="Email" value="{!v.acc.Email}" />
            </div>
            <div class="form-group">
                <lightning:input name="accPhone" type="phone" required="true" maxlength="10" label="Phone" value="{!v.acc.Phone}" />
            </div>
            <div class="form-group">
                <!--Lightning radio group component-->
                <lightning:radioGroup name="radioGroup"
                                      label="Industry"
                                      required="true"
                                      options="{!v.industryMap}"
                                      value="{!v.acc.Industry}"
                                      type="radio"/>
            </div>
        </div>
        <br/>
        <lightning:button variant="brand" label="Submit" onclick="{!c.handleAccountSave}" />              
    </div>
    <!--Component End-->
</aura:component>

Lightning Component Controller:

({
    //Load Account Industry Picklist
    doInit: function(component, event, helper) {        
        helper.getIndustryPicklist(component, event);
    },
    
    //handle Account Save
    handleAccountSave : function(component, event, helper) {
        helper.saveAccount(component, event);
    },
    
    //handle Industry Picklist Selection
    handleCompanyOnChange : function(component, event, helper) {
        var indutry = component.get("v.acc.Industry");
        alert(indutry);
    }
})

Lightning Component Helper:

({
    //get Industry Picklist Value
    getIndustryPicklist: function(component, event) {
        var action = component.get("c.getIndustry");
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                var result = response.getReturnValue();
                var industryMap = [];
                for(var key in result){
                    industryMap.push({label: result[key], value: key});
                }
                component.set("v.industryMap", industryMap);
            }
        });
        $A.enqueueAction(action);
    },
    
    //handle Account Save
    saveAccount : function(component, event) {
        var acc = component.get("v.acc");
        var action = component.get("c.createAccount");
        action.setParams({
            objacc : acc
        });
        action.setCallback(this,function(response){
            var state = response.getState();
            if(state === "SUCCESS"){
                alert('Record is Created Successfully');
            } else if(state === "ERROR"){
                var errors = action.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        alert(errors[0].message);
                    }
                }
            }else if (status === "INCOMPLETE") {
                alert('No response from server or client is offline.');
            }
        });       
        $A.enqueueAction(action);
    }
})

Output: