Avoid Recursive Trigger Calls In Salesforce

Recursion occurs when same code is executed again and again. It can lead to infinite loop and which can result to governor limit sometime. Sometime it can also result in unexpected output or the error “maximum trigger depth exceeded”. So, we should write code in such a way that it does not result to recursion.

For example, I’ve a trigger on Account object, which will be execute on before Update and after Update. In after Update I’ve some custom logic to update Account records. So, when I’m updating the Account records in After Update, it is throwing error “maximum trigger depth exceeded”. So, it is a recursion in apex trigger.

To avoid the situation of recursive call, we have to write code in such a way that the trigger will execute one time. To do so, we can create a class with a static Boolean variable with default value true. In the trigger, before executing the code keep a check that the variable is true or not.

Here in below trigger, I want to execute both before and after Update trigger only one time. I’m checking the static Boolean variable is true in both before and after Update trigger. In after update trigger changed the variable to false and after updating the account records, again changed the variable to true. In this way you can handle bulk of records in recursive trigger.

Apex Class:

public Class AccountTriggerHelper{
    //Trigger execution check variable
    public static Boolean runOnce = true;
    
    //On before update
    public void onBeforeUpdate(map<Id, Account> mapNewAccount, map<Id, Account> mapOldAccount){
        //Write your logic here
        System.debug('Is Before Update');
    }
    
    //On after update
    public void onAfterUpdate(map<Id, Account> mapNewAccount, map<Id, Account> mapOldAccount){
        Update [SELECT Id, Name FROM Account WHERE Id IN: mapNewAccount.keyset()];
        AccountTriggerHelper.runOnce = true;
        System.debug('Is After Update');
    }
}

Apex Trigger:

trigger AccountTrigger on Account(before Update, after Update) {
    
    AccountTriggerHelper helper = new AccountTriggerHelper();
    
    if(Trigger.isBefore && Trigger.isUpdate && AccountTriggerHelper.runOnce){
        helper.onBeforeUpdate(Trigger.newMap, Trigger.OldMap);     
    }
    
    if(Trigger.isAfter && Trigger.isUpdate && AccountTriggerHelper.runOnce){
        AccountTriggerHelper.runOnce = false;
        helper.onAfterUpdate(Trigger.newMap, Trigger.OldMap);
    }
}

Debug Log:

Check Case Owner is a User or Queue

Check Case Owner in Apex Class.

//Check object Id in Apex 
if(string.valueOf(c.OwnerId).startsWith('005')){
    //Owner is User       
}

if(string.valueOf(c.OwnerId).startsWith('00G')){
    //Owner is Queue
}

Check Case Owner in Apex Trigger.

//In Apex Trigger
for (Case objCase : Trigger.new) { 
    If (objCase.OwnerID.getsobjecttype() == User.sobjecttype) {
        /*Code if Owner is User*/
    }
    else{
        /*Code if Owner is Queue*/ 
    }
}

Check Case Owner by SOQL query.

//By Query Owner.Type Field
List<Case> caseList = [SELECT Id, CaseNumber, OwnerId, Owner.Name, Owner.Type FROM Case];

for (Case objCase : caseList){
    If (objCase.Owner.Type == User.sobjecttype) {
        /*Code if Owner is User*/
    }
    else{
        /*Code if Owner is Queue*/ 
    }
}

Check Case Owner in Process Builder.

//Check in Process Builder
BEGINS([Case].OwnerId, "005") //Check Owner is User
BEGINS([Case].OwnerId, "00G") //Check Owner is Queue

Disable LookUp Input Field on Visualforce Page

Sample Code:

<apex:page standardController="Contact" id="pg">
    <apex:form id="fm">
        <apex:inputField value="{!Contact.AccountId}" id="ifAcc" />
    </apex:form>
    <script>
    var inpAcc = document.getElementById("pg:fm:ifAcc");
    inpAcc.setAttribute("readonly","true");   
    </script>  
</apex:page>

Output:

Inherited Lightning Component Attributes

A Child component that extends a Parent component inherits the attributes of the Parent component.

Let’s start with a simple example. Here in below example c:Parent has a description attribute with a value of “Hello!!”, and c:Child extends c:Parent by setting extends="c:Parent" in its aura:component tag.

Parent Component:

<!--Parent.cmp-->
<aura:component extensible="true">
    <aura:attribute name="description" type="String" default="Hello!!" />
    <p>Description: {!v.description}</p>
</aura:component>

Child Component:

<!--Child.cmp-->
<aura:component extends="c:Parent">
    <aura:set attribute="description" value="Hello World!" />
</aura:component>

Test App:

<!--Test.app-->
<aura:application extends="force:slds">
    <c:Parent />
    <c:Child />    
</aura:application>

Output:
Description: Hello!!
Description: Hello World!

Pagination In Lightning Component

Apex Class:

public class ContactAuraController {
    @AuraEnabled
    public static ContactDataTableWrapper getContactData(Integer pageNumber, Integer pageSize) {
        
        //Offset for SOQL
        Integer offset = (pageNumber - 1) * pageSize;
        
        //Total Records
        Integer totalRecords = [SELECT COUNT() FROM Contact];
        Integer recordEnd = pageSize * pageNumber;

        //Instance of Contact DataTable Wrapper Class
        ContactDataTableWrapper objDT =  new ContactDataTableWrapper();  
        objDT.pageSize = pageSize;
        objDT.pageNumber = pageNumber;
        objDT.recordStart = offset + 1;
        objDT.recordEnd = totalRecords >= recordEnd ? recordEnd : totalRecords;
        objDT.totalRecords = totalRecords;
        objDT.contactList = [SELECT Id, Name, Phone, Email FROM Contact ORDER BY Name LIMIT :pageSize OFFSET :offset];
        return objDT;
    }
    
    //Wrapper Class For Contact DataTable  
    public class ContactDataTableWrapper {
        @AuraEnabled
        public Integer pageSize {get;set;}
        @AuraEnabled
        public Integer pageNumber {get;set;}
        @AuraEnabled
        public Integer totalRecords {get;set;}
        @AuraEnabled
        public Integer recordStart {get;set;}
        @AuraEnabled
        public Integer recordEnd {get;set;}
        @AuraEnabled
        public List<Contact> contactList {get;set;}
    }
}

Pagination Component:

<!--Pagination.cmp-->
<aura:component controller="ContactAuraController">
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    
    <aura:attribute name="ContactList" type="Contact[]"/>
    <aura:attribute name="PageNumber" type="integer" default="1"/>
    <aura:attribute name="TotalPages" type="integer" default="0"/>
    <aura:attribute name="TotalRecords" type="integer" default="0"/>
    <aura:attribute name="RecordStart" type="integer" default="0"/>
    <aura:attribute name="RecordEnd" type="integer" default="0"/>
    
    <div class="slds-m-around_xx-large">
        <h1 class="slds-text-heading--medium">Contacts</h1>
        <br/>
        <div class="slds-float_right">
            <ui:inputSelect aura:id="pageSize" label="Display Records Per Page: " change="{!c.onSelectChange}">
                <ui:inputSelectOption text="10" label="10" value="true"/>
                <ui:inputSelectOption text="15" label="15"/>
                <ui:inputSelectOption text="20" label="20"/>
            </ui:inputSelect>
            <br/>
        </div>
        
        <table class="slds-table slds-table_bordered slds-table_cell-buffer">
            <thead>
                <tr class="slds-text-title_caps">
                    <th scope="col">
                        <strong><div class="slds-truncate" title="Name">Name</div></strong>
                    </th>
                    <th scope="col">
                        <strong><div class="slds-truncate" title="Phone">Phone</div></strong>
                    </th>
                    <th scope="col">
                        <strong><div class="slds-truncate" title="Email">Email</div></strong>
                    </th>
                </tr>
            </thead>
            <tbody>
                <aura:iteration items="{!v.ContactList}" var="con"> 
                    <tr>
                        <th scope="row" data-label="Name">
                            <div class="slds-truncate" title="{!con.Name}">{!con.Name}</div>
                        </th>
                        <th scope="row" data-label="Phone">
                            <div class="slds-truncate" title="{!con.Phone}">{!con.Phone}</div>
                        </th>
                        <th scope="row" data-label="Email">
                            <div class="slds-truncate" title="{!con.Email}">{!con.Email}</div>
                        </th>
                    </tr>
                </aura:iteration>	
            </tbody>
        </table>
        
        <div class="slds-clearfix">
            <div class="slds-page-header" role="banner">
                <div class="slds-float_right">            
                    <lightning:button disabled="{!v.PageNumber == 1}" variant="brand" aura:id="prevPage" label="Prev" onclick="{!c.handlePrev}" />            
                    <lightning:button disabled="{!v.PageNumber == v.TotalPages}" aura:id="nextPage" variant="brand" label="Next" onclick="{!c.handleNext}"/>
                </div>
                <p class="slds-page-header__title">{!v.RecordStart}-{!v.RecordEnd} of {!v.TotalRecords} | Page {!v.PageNumber} of {!v.TotalPages}</p>
            </div>
        </div>
    </div>
</aura:component>

Pagination JavaScript Controller:

({
    doInit: function(component, event, helper) {
        var pageNumber = component.get("v.PageNumber");  
        var pageSize = component.find("pageSize").get("v.value"); 
        helper.getContactList(component, pageNumber, pageSize);
    },
    
    handleNext: function(component, event, helper) {
        var pageNumber = component.get("v.PageNumber");  
        var pageSize = component.find("pageSize").get("v.value");
        pageNumber++;
        helper.getContactList(component, pageNumber, pageSize);
    },
    
    handlePrev: function(component, event, helper) {
        var pageNumber = component.get("v.PageNumber");  
        var pageSize = component.find("pageSize").get("v.value");
        pageNumber--;
        helper.getContactList(component, pageNumber, pageSize);
    },
    
    onSelectChange: function(component, event, helper) {
        var page = 1
        var pageSize = component.find("pageSize").get("v.value");
        helper.getContactList(component, page, pageSize);
    },
})

Pagination JavaScript Controller Helper:

({
    getContactList: function(component, pageNumber, pageSize) {
        var action = component.get("c.getContactData");
        action.setParams({
            "pageNumber": pageNumber,
            "pageSize": pageSize
        });
        action.setCallback(this, function(result) {
            var state = result.getState();
            if (component.isValid() && state === "SUCCESS"){
                var resultData = result.getReturnValue();
                component.set("v.ContactList", resultData.contactList);
                component.set("v.PageNumber", resultData.pageNumber);
                component.set("v.TotalRecords", resultData.totalRecords);
                component.set("v.RecordStart", resultData.recordStart);
                component.set("v.RecordEnd", resultData.recordEnd);
                component.set("v.TotalPages", Math.ceil(resultData.totalRecords / pageSize));
            }
        });
        $A.enqueueAction(action);
    }
})

Output: