0

Hi I am working on a problem where I have multiple records of an Object to be shown and each of these records, will have many child records of It's Child Object.

I have chosen AccordianSection to show parent record and A Table to show Children of each.

Also, I need to provide New, Clone and Delete Functionality for each record. Also, an "Expand All | Collapse All" feature.

So, I am using aura:iteration to dynamically generate lightning:accordionSection. I have tried to assign a unique Name to each AccordianSection by using, Id/Name in conjunction with index of iteration, so that I can easily collect the names of AccordianSections while iterating through record list and could assign it to "activeSections" attribute, which will be used by lightning:accordian to expand listed AccordianSections.

But, My observation is, once AccordianSections are rendered, It's name doesn't change, when I Clone or Delete a Record, Even though AccordianSections are rendered properly. event.getParam("openSections") always gives old value.

Because of this Expand All | Collapse All functionality is not working.

Component markup

<aura:component controller="CampaignCtrl" implements="force:appHostable">
    <aura:attribute name="campaigns" type="CampaignCtrl.CampaignWithLineItems[]"/>
    <aura:attribute name="activeSections" type="List" />
    <aura:attribute name="activeSectionsMessage" type="String" default="" />
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />

    <p>{! v.activeSectionsMessage }</p><br/>
    <p><a onclick="{!c.handleExpandAll}">Expand All</a>&nbsp;|&nbsp;<a onclick="{!c.handleCollapseAll}">Collapse All </a></p>
    <lightning:accordion allowMultipleSectionsOpen="true"
                         onsectiontoggle="{! c.handleSectionToggle }"
                         activeSectionName="{! v.activeSections }"
                         >

        <aura:iteration items="{!v.campaigns}" var="campaignWrp" indexVar="index">
            <lightning:accordionSection aura:id="accor" name="{!(index+1)+''}"
                                        label="{!(index+1)+'. '+campaignWrp.campaign.Name}">
                <br/>

                <div class="slds-grid slds-gutters slds-wrap slds-grid_align-space">
                    <div class="slds-col slds-align_absolute-center form-group">

                        <span>
                            <lightning:input aura:id="CampaignName" label="Campaign Name"
                                             name="CampaignName" value="{!campaignWrp.campaign.Name}" />

                    <div class="slds-col slds-align_absolute-center form-group">
                        <span>
                            <lightning:button value="{!index}" label="New" onclick="{!c.appendNewCampaign}" 
                                              class="slds-button_brand"/>
                            <lightning:button value="{!index}" label="Clone" onclick="{!c.cloneCampaign}" 
                                              class="slds-button_brand"/>
                            <lightning:button value="{!index}" label="Delete" onclick="{!c.deleteCampaign}" 
                                              class="slds-button_neutral" disabled="{!v.campaigns.length == 1}"/>
                        </span>
                    </div>
                </div>

                <br/>
            </lightning:accordionSection>
        </aura:iteration>
    </lightning:accordion>

</aura:component>

JS Controller

({
    doInit : function(component, event, helper){
        var campaigns = component.get("v.campaigns");
        console.log(component.get("v.caseId"));
        if(typeof(campaigns) !== "undefined" && !$A.util.isEmpty(campaigns)){
            campaigns[0].campaign.Id = '';
            campaigns[0].campaign.Name = "New Campaign";
            component.set("v.campaigns", campaigns);

            component.set("v.activeSections", "1");
            console.log(component.get("v.activeSections"));
        }
        console.log(campaigns[0].campaign);
    },

    appendNewCampaign : function(component, event, helper){
        helper.appendNewCampaign(component, event, helper);
    },

    cloneCampaign : function(component, event, helper){
        helper.cloneCampaign(component, event, helper);
    },

    deleteCampaign : function(component, event, helper){
        helper.deleteCampaign(component, event, helper);
    },


    handleExpandAll : function(component, event, helper){
        var activeSections = component.get("v.activeSections");
        activeSections = [];
        var campaigns = component.get("v.campaigns");
        if(typeof(campaigns) == "object"){
            for(var i in campaigns){
                activeSections.push((parseInt(i)+1).toString());
            }
        }
        component.set("v.activeSections", openSections);
    },

    handleCollapseAll : function(component, event, helper){
        var activeSections = component.get("v.activeSections");
        console.log(activeSections);
        activeSections = [];

        component.set("v.activeSections", activeSections);
    },

    handleSectionToggle : function(component, event, helper){
        var openSections = event.getParam("openSections");
        console.log(openSections);
        if(openSections.length === 0){
            component.set("v.activeSectionsMessage", "All sections are closed");
        }else{
            component.set("v.activeSectionsMessage", "Open sections: " + openSections.join(', '));
        }
    }
})
JS Helper


({
    cloneObjectDeep : function (obj) {
        var clone = {};
        for(var i in obj) {
            if(obj[i] != null &&  typeof(obj[i])=="object"){
                clone[i] = this.cloneObjectDeep(obj[i]);
            }
            else{
                clone[i] = obj[i];
            }
        }
        console.log(clone);
        return clone;
    },
    appendNewCampaign : function(component, event, helper){
        var index = event.getSource().get("v.value");
        var campaigns = component.get("v.campaigns");
        var newElement = this.cloneObjectSh(campaigns[index]);
        newElement.campaign = {};
        newElement.campaignLineItems = [{}];
        newElement.campaign.Id = '';
        newElement.campaign.Name = "New Campaign "+(index+1);
        campaigns.splice(index+1, 0, newElement);
        component.set("v.campaigns", campaigns);
        var indx = parseInt(index)+2;
        var activeSections = component.get("v.activeSections");
        activeSections.push(indx.toString());
        component.set("v.activeSections", activeSections);
        console.log(activeSections);
    },
    cloneCampaign : function(component, event, helper){
        var index = event.getSource().get("v.value");
        var campaigns = component.get("v.campaigns");
        var cloneElement = this.cloneObjectDeep(campaigns[index]);
        cloneElement.campaign.Id = '';
        campaigns.splice(index+1, 0, cloneElement);
        component.set("v.campaigns",campaigns);

        var indx = parseInt(index)+2;
        var activeSections = component.get("v.activeSections");
        activeSections.push(indx.toString());
        component.set("v.activeSections",activeSections);
        console.log(activeSections);
    },
    deleteCampaign : function(component, event, helper){
        var response = confirm("This Record will be deleted permanently. Do you Really want to Proceed?");
        if(response){
            var index = event.getSource().get("v.value");
            var campaigns = component.get("v.campaigns");
            console.log(index);
            var deleteElement = campaigns[index];
            if(deleteElement.Id == null){
                //campaigns.pop(deleteElement);
                campaigns.splice(index, 1);
                component.set("v.campaigns",campaigns);
            }else{

            }
        }else{
            return;
        }
    }
})
Apex Controller Wrapper


public class CampaignWithLineItems{
        @AuraEnabled
        public Campaign__c campaign {get;set;}
        @AuraEnabled
        public List<Campaign_Line_Item__c> campaignLineItems {get;set;}
        @AuraEnabled
        public String campaignFlag {get;set;}

        public CampaignWithLineItems(Campaign__c campaign, List<Campaign_Line_Item__c> campaignLineItems, String campaignFlag){
                this.campaign = campaign;
                this.campaignLineItems = campaignLineItems;
                this.campaignFlag = campaignFlag;
        }
        public CampaignWithLineItems(){
            this.campaign = new Campaign__c();
            this.campaignLineItems = new List<Campaign_Line_Item__c>{new Campaign_Line_Item__c()};
            this.campaignFlag = '';
        }
    }

Only solution I got till now is to always append new or cloned records at the end of the existing List of parent records, but I don't want to restrict users like this.

0

In order to make this work, I recommend setting accordion section’s name attribute to record’s id and the section label to record’s name.

There is no need to set the activeSectionName attribute manually. Aura set the value automatically based on which sections are open.

To get value from activeSectionName lightning:accordion aura:id attribute need to be set first

Component:

<lightning:accordion aura:id="accordion" onsectiontoggle="{!c.onSectionToggle}" allowMultipleSectionsOpen="true">
    <aura:iteration items="{!v.parents}" var="parent">
        <lightning:accordionSection name="{!parent.Id}" label="{!parent.Name}">
            <aura:iteration items="{!parent.childs}" var="child" >
                <!--child details-->
            </aura:iteration>
        </lightning:accordionSection>
    </aura:iteration>
</lightning:accordion>

Controller:

onSectionToggle: function(component, event, helper){
    console.log("active sections name: ", component.find("accordion").get('v.activeSectionName'));
    }

And then for the Expand All functionality, set the value of activeSectionName attribute to parent record ids

  • Thanks Zuzana. I figured out a workaround. After I am resetting and reassigning my iteration object, I am calling setTimeout for 50 miliSec to set activeSections. A bit flick is observed in UI, but it works. setTimeout(function(){ component.set("v.activeSections", activeSectionsNew); }, 50); – Omkar K Jun 4 at 7:13
  • Well my point was, that you don't have to use any workarounds as the accordion works perfectly fine by default. But I'm glad you made it work your way! :) – Zuzana Jun 15 at 20:21
  • Hi Zuzana, Thanks for the comment, but if you try above code, you will see that accordian has problems in rendering when created dynamically inside aura iteration. That time if you try to set activeSections, just after resetting object list, it won't work, probably because at the time this statement being executed immediately after re-rendering of accordian sections, re-rendering is still in progress. But when you set a timeout of say 30-50 ms, it works perfectly fine. if you set timeout to 10ms, you can see re-rendering in a flash. – Omkar K Jun 20 at 5:42
0

Thanks for the help Zuzana.

I figured out a workaround.

It works when we assign blank object to iterating object, and then assign new object. like below,

component.set("v.campaigns", {});
component.set("v.campaigns", campaigns);

But setting activeSection won't work just after this statement. For that I figured out another workaround. After I am resetting and reassigning my iteration object, I am calling setTimeout for 50 miliSec to set activeSections. A bit flick is observed in UI, but it works.

setTimeout(function(){ component.set("v.activeSections", activeSectionsNew); }, 50);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.