Expand And Collapse Panel Using Lightning Component

Accordion Component:

<!--Accordion.cmp-->
<aura:component>
    <div class="slds-p-around--large">
        <div class="slds-page-header" style="cursor: pointer;" onclick="{!c.panelOne}">
            <section class="slds-clearfix">
                <div class="slds-float--left ">
                    <lightning:icon class="slds-show" aura:id="panelOne" iconName="utility:add" size="x-small" alternativeText="Indicates add"/>
                    <lightning:icon class="slds-hide" aura:id="panelOne" iconName="utility:dash" size="x-small" alternativeText="Indicates dash"/>
                </div>
                <div class="slds-m-left--large">Panel 1</div>
            </section>
        </div>
        
        <div class="slds-hide slds-p-around--medium" aura:id="panelOne">
            Panel 1
        </div>
        
        <div class="slds-page-header" style="cursor: pointer;" onclick="{!c.panelTwo}">
            <section class="slds-clearfix">
                <div class="slds-float--left">
                    <lightning:icon class="slds-show" aura:id="panelTwo" iconName="utility:add" size="x-small" alternativeText="Indicates add"/>
                    <lightning:icon class="slds-hide" aura:id="panelTwo" iconName="utility:dash" size="x-small" alternativeText="Indicates dash"/>
                </div>
                <div class="slds-m-left--large">Panel 2</div>
            </section>
        </div>
        
        <div class="slds-hide slds-p-around--medium" aura:id="panelTwo">
            Panel 2
        </div>
        
        <div class="slds-page-header"  style="cursor: pointer;" onclick="{!c.panelThree}">
            <section class="slds-clearfix">
                <div class="slds-float--left"> 
                    <lightning:icon class="slds-show" aura:id="panelThree" iconName="utility:add" size="x-small" alternativeText="Indicates add"/>
                    <lightning:icon class="slds-hide" aura:id="panelThree" iconName="utility:dash" size="x-small" alternativeText="Indicates dash"/>
                </div>
                <div class="slds-m-left--large">Panel 3</div>
            </section>
        </div>
        
        <div aura:id="panelThree" class="slds-hide slds-p-around--medium">
            Panel 3
        </div>
        
        <div class="slds-page-header"  style="cursor: pointer;" onclick="{!c.panelFour}">
            <section class="slds-clearfix">
                <div class="slds-float--left"> 
                    <lightning:icon class="slds-show" aura:id="panelFour" iconName="utility:add" size="x-small" alternativeText="Indicates add"/>
                    <lightning:icon class="slds-hide" aura:id="panelFour" iconName="utility:dash" size="x-small" alternativeText="Indicates dash"/>
                </div>
                <div class="slds-m-left--large">Panel 4</div>
            </section>
        </div>
        
        <div aura:id="panelFour" class="slds-hide slds-p-around--medium">
            Panel 4
        </div>  
        
                <div class="slds-page-header"  style="cursor: pointer;" onclick="{!c.panelFive}">
            <section class="slds-clearfix">
                <div class="slds-float--left"> 
                    <lightning:icon class="slds-show" aura:id="panelFive" iconName="utility:add" size="x-small" alternativeText="Indicates add"/>
                    <lightning:icon class="slds-hide" aura:id="panelFive" iconName="utility:dash" size="x-small" alternativeText="Indicates dash"/>
                </div>
                <div class="slds-m-left--large">Panel 5</div>
            </section>
        </div>
        
        <div aura:id="panelFive" class="slds-hide slds-p-around--medium">
            Panel 5
        </div>  
    </div>
</aura:component>

Accordion Component JS Controller:

({
    panelOne : function(component, event, helper) {
        helper.toggleAction(component, event, 'panelOne');
    },
    
    panelTwo : function(component, event, helper) {
        helper.toggleAction(component, event, 'panelTwo');
    },
    
    panelThree : function(component, event, helper) {
        helper.toggleAction(component, event, 'panelThree');
    },
    
    panelFour : function(component, event, helper) {
        helper.toggleAction(component, event, 'panelFour');
    },
    
    panelFive : function(component, event, helper) {
        helper.toggleAction(component, event, 'panelFive');
    }
})

Accordion Component JS Helper:

({
    toggleAction : function(component, event, secId) {
        var acc = component.find(secId);
        for(var cmp in acc) {
            $A.util.toggleClass(acc[cmp], 'slds-show');  
            $A.util.toggleClass(acc[cmp], 'slds-hide');  
        }
    }
})

Lightning Test App:

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

Output:

Iterate Map values in Lightning Component

Apex Controller:
Create below apex controller to get the Map value.

public class MapAuraController {
    
    @AuraEnabled
    public static map<Integer, String> getMap(){
        Map<Integer, String> companyMap = new Map<Integer, String>();
        //Put value in Map 
        companyMap.put(1,'Salesforce');
        companyMap.put(2,'SAP');  
        companyMap.put(3,'Oracle');  
        companyMap.put(4,'Microsoft');  
        //return map  
        return companyMap;
    }
}

Child Component:
Create below Child.cmp for display the Map key values on component.

<!--Child.cmp-->
<aura:component >
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <aura:attribute name="map" type="map"/>
    <aura:attribute name="key" type="string"/>
    <aura:attribute name="value" type="string"/>
    <p>{!v.key} - {!v.value}</p>
</aura:component>

Child Component JS Controller:
Create below JavaScript Controller for above Child.cmp component.

({
    doInit : function(component, event, helper) {
        var key = component.get("v.key");
        var map = component.get("v.map");
        component.set("v.value" , map[key]);
    },
})

Parent Component:
Create below Parent.cmp, this is the main component. By this component we retrieve the map value from apex controller and pass key and map attribute to Child.cmp component.

<!--Parent.cmp-->
<aura:component controller="MapAuraController">
    <!--declare attributes-->
    <aura:attribute name="keyList" type="List"/>
    <aura:attribute name="companyMap" type="map"/>
    
    <ui:button label="Iterate Map" press="{!c.getMapCompany}"/>
    
    <!--Iterate the mapCompany-->
    <aura:iteration items="{!v.keyList}" var="key" >
        <c:Child map="{!v.companyMap}" key="{!key}"/>
    </aura:iteration>
</aura:component>

Parent Component:
Create below JavaScript Controller for above Parent.cmp component.

({
    getMapCompany: function(component, event, helper) {
        //call apex class method 
        var action = component.get('c.getMap');
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                //Create an empty array to store the map keys 
                var arrayMapKeys = [];
                //Store the response of apex controller (return map)     
                var result = response.getReturnValue();
                //Set the store response[map] to component attribute, which name is map and type is map.   
                component.set('v.companyMap', result);
                
                for (var key in result) {
                    arrayMapKeys.push(key);
                }
                //Set the list of keys.     
                component.set('v.keyList', arrayMapKeys);
            }
        });
        // enqueue the Action   
        $A.enqueueAction(action);
    }
})

Lightning Test App:

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

Output:

Map Attribute in Lightning Component

Sample Component:

<!--Sample.cmp-->
<aura:component>
    <aura:attribute name="map" type="Map" default="{str1:null,str2:null,obj:null}"/>
    <aura:attribute name="str1" type="String"  default="{!v.map.str1}"/>
    <aura:attribute name="str2" type="String" default="{!v.map.str2}"/>
    <aura:attribute name="obj" type="Contact" default="{!v.map.obj}"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <p><b>{!v.map.str1}</b></p>
    <p><b>{!v.map.obj.FirstName}</b></p>
    <p><b>{!v.map.str2}</b></p>
    <p><b>{!v.map.obj.LastName}</b></p>
</aura:component>

Sample Component JS Controller:

({
    doInit: function(cmp, event, helper) {
        //Set the attribute value.
        var map = cmp.get("v.map");
        map['str1'] = 'FirstName';
        map['str2'] = 'Lastname';
        var obj = {"FirstName":"Biswajeet","LastName":'Samal'};
        map['obj'] = obj;
        cmp.set("v.map", map);
    }
})

Lightning App:

<aura:application extends="force:slds">
    <c:Example />
</aura:application>

Output:

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: