2

So please bear with me, I am a Lightning n00b...

So I am trying to build a Lightning component which will implement a two-level foldable tree based on data in my SF instance. The way I have decomposed it, I have a

  • List Component, which holds a number of
  • List Items (generated based on a parent object in sf) which each have 0..n
  • List Sub-items (generated based on the children attached to each individual parent object)

I.e.

  • 1
  • 1.1
  • 1.2
  • 2
  • 2.1
  • 2.2
  • 2.3

etc. - there can be a varying number of items/subitems, so I need to be able to generate both dynamically. Also, I want the tree to start out folded, and fold out an element (showing the subitems) when clicked.

Basically, thinking with my old JavaScript hat on, I am running into the issue that when I generate my

<div class="slds-tree_container slds-p-top--medium slds-m-horizontal--small" role="application">
    <h4 class="slds-text-title--caps" id="treeheading">Content Modules</h4>
    <ul class="slds-tree" role="tree" aria-labelledby="treeheading">
        <aura:iteration items="{!v.modules}" var="module" indexVar="counter">
            <c:Item module="{!module}" node="{!counter}"/>
        </aura:iteration>
    </ul>
</div>

in my top-level component (the List), then do this:

<aura:component >
<aura:attribute name="module" type="Item__c"/>
<aura:attribute name="node" type="Integer" required="true"/>
<aura:attribute name="items" type="SubItem__c[]"/>
<li id="{! 'tree0-node' + node}" role="treeitem" aria-level="1" aria-expanded="true">
    <div class="slds-tree__item">
        <lightning:buttonIcon aura:id="moduleFold" name="myButton" 
         iconName="utility:chevronright" variant="bare" alternativeText="Fold Out" 
         size="small" onclick="{!c.foldOut}"/>
        <a href="javascript:void(0);" tabindex="-1" role="presentation" 
         class="slds-truncate" title="Tree Item">{!v.module.Name}</a>
    </div>
    <ul class="slds-is-collapsed" role="group">
        <aura:iteration items="{!v.items}" var="item" indexVar="counter">
            <c:Subitem module="{!module}" node="{!counter}"/> 
        </aura:iteration>            
    </ul>
</li>
</aura:component>

In my Item component;

I would be looking for a good place to fire the onClick. I have it currently added to the lightning:button, and it works just fine (i.e. fires) with this:

({
foldOut : function(component, event, helper) {
    console.log("Clicked Fold Out on: " + event.getSource());
    alert("Clicked Fold Out on: " + event.getSource());
    }
})

However, what I get in the alert is this

Clicked Fold Out on: markup://lightning:buttonIcon {4:3;a} {moduleFold}

So some internal ID of the lightning button - I can't stick a regular ID on it that I could generate dynamically (aura:id only supports string literals), so I'm kinda stuck.

The only option I've been able to google my way to is generating the components dynamically using

$A.createComponent(String type, Object attributes, function callback)

But am thinking that there has to be a better way of doing this rather than using JavaScript to generate what I need in code. In a way, this last feels much too much like the good ole days of PHP3 to me.

So what am I missing here (and I know there's a lot, so be gentle :))?

Edit:

Suggestion to use event.getSource().target didn't pan out, returns Target: undefined

2
  • How about event.getSource().target Feb 14, 2017 at 11:24
  • Does the modules contains the required child data (i.e array of SubItem__c) to be displayed?
    – Praveen
    Feb 14, 2017 at 14:07

1 Answer 1

2

As far as I could understand, you are trying to open the SubItems view when button is clicked. To do this, you don't need to access the regular id of the button.

Each instance of the component(i.ec:Item) generated by aura:iteration, has its own space(i.e encapsulation) where it can access the DOM within its view.

All you have to do is, add aura:id to the ul and add necessary class to the ul to open the sub-items view:

markup :(add aura:id)

 <ul class="slds-is-collapsed" role="group" aura:id="sub-items">
    <aura:iteration items="{!v.items}" var="item" indexVar="counter">
        <c:Subitem module="{!module}" node="{!counter}"/> 
    </aura:iteration>            
</ul>

controller:(add appropriate class to toggle the sub-items)

({
   foldOut : function(component, event, helper) {
       var childCmp = cmp.find("sub-items");
       var btnclicked = event.getSource();
       $A.util.toggleClass(childCmp,'slds-is-collapsed');
       $A.util.toggleClass(childCmp,'slds-is-expanded'); 

       if($A.util.hasClass(childCmp,'slds-is-expanded')) {
            btnclicked.set("v.iconName","utility:chevrondown");
       } else {
            btnclicked.set("v.iconName","utility:chevronright");
       }
   }
})

Here's a sample Tree view example:

Tree.app

<aura:application extends="force:slds">
    <aura:attribute name="nodes" type="List" />
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    <div class="slds-tree_container slds-p-top--medium slds-m-horizontal--small" role="application">
        <h4 class="slds-text-title--caps">Tree</h4>
        <ul class="slds-tree" role="tree" aria-labelledby="treeheading">
            <aura:iteration items="{!v.nodes}" var="node">
                <c:TreeNode node="{!node}"/>
            </aura:iteration>
        </ul>
    </div>
</aura:application>

TreeAppController.js:

({
    doInit : function(cmp, event, helper) {
        var nodes = [
            {
                'name':'Tree-1',
                'childs':[
                    {
                       'name':'Tree-1-1'
                    },
                    {
                      'name':'Tree-1-2'  
                    }
                ]
            },
            {
                'name':'Tree-2',
                'childs':[
                    {
                       'name':'Tree-2-1'
                    },
                    {
                      'name':'Tree-2-2'  
                    },
                    {
                      'name':'Tree-2-3'  
                    }
                ]
            }
        ];

        cmp.set("v.nodes",nodes);
    },

})

TreeNode.cmp:

<aura:component >
    <aura:attribute name="node" type="Map" />
    <aura:attribute name="items" type="List"/>
    <li role="treeitem"  aria-expanded="true">
        <div class="slds-tree__item" >
            <aura:if isTrue="{!not(empty(v.node.childs))}">
                <lightning:buttonIcon aura:id="moduleFold" name="myButton" 
                                      iconName="utility:chevronright" variant="bare" alternativeText="Fold Out" 
                                      size="small" onclick="{!c.foldOut}"/>
            </aura:if>
            <a href="javascript:void(0);" tabindex="-1" role="presentation" 
               class="slds-truncate" title="Tree Item">{!v.node.name}</a>
        </div>
        <ul class="slds-is-collapsed slds-p-left--medium" role="group" aura:id="child-nodes">
            <aura:iteration items="{!v.node.childs}" var="node">
                <c:TreeNode node="{!node}"/> 
            </aura:iteration>            
        </ul>
    </li>
</aura:component>

TreeNodeController.js:

({
    foldOut : function(cmp, event, helper) {
        var childCmp = cmp.find("child-nodes");
        var btnClicked = event.getSource();
        $A.util.toggleClass(childCmp,'slds-is-collapsed');
        $A.util.toggleClass(childCmp,'slds-is-expanded');

        if($A.util.hasClass(childCmp,'slds-is-expanded')) {
            btnClicked.set("v.iconName","utility:chevrondown");
        } else {
            btnClicked.set("v.iconName","utility:chevronright");
        }

    }
})
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .