7

I am building a menu component.

One menu-item is a sub-menu with list of opportunities, this list is created with aura:iteration over a list of opportunities.

When selecting a menu or a sub-menu, I need to make it look selected (change css attribute). For this to happen, I need to find this menu item in my controller.

If I add id attribute to my item markup and use the event attribute on my controller (event.currentTarget.id), I can catch the item itself and change it's style, this works:

MARKUP

<aura:iteration items="{!v.opportunities}" var="opp">
    <li class="slds-dropdown__item" role="presentation">
        <a href="javascript:void(0);" role="menuitem" tabindex="-1" id="{!opp.Id}" onclick="{!c.openItemFromMenu}">
            <span class="slds-truncate" title="{!opp.Name}">{!opp.Name}</span>
        </a>
    </li>
</aura:iteration>

CONTROLLER

openItemFromMenu: function(component, event, helper) {
    // get the id
    var oppId = event.currentTarget.id;
    // choose it (select menu)
    var selectedItemNew = component.find(oppId);
    selectedItemNew.style = "background: rgb(216, 237, 255);"
}

PROBLEM

when I try to change the menu selected not from the click of the item (event that is triggered from another page) - I have added an event handler to change the menu item, and I send the opportunity id through that event. Then (after adding aura:id to my item, I am trying to find this menu item of this oppId with component.find in my controller - this does not work:

CHANGED MARKUP

add:
<aura:handler name="eventSelectMenuItem" event="c:myEvent" action="{!c.openItemFromMenu}" />
change:
<a href="javascript:void(0);" role="menuitem" tabindex="-1" id="{!opp.Id}" onclick="{!c.openItemFromMenu}" aura:id="{!opp.Id}">

CHANGED CONTROLLER

openItemFromMenu: function(component, event, helper) {
    // get the id from the event
    var oppId = event.getParam("recordId");
    // choose it (select menu)
    var selectedItemNew = component.find(oppId);
    selectedItemNew.style = "background: rgb(216, 237, 255);"
}

QUESTION

Does component.find for aura:id within an aura:iteration suppose to work?

If not - is there a workaround?

If it does suppose to work - what am I doing wrong?

3 Answers 3

6

aura:id must be a literal value. As such, you can't use an expression in aura:id (it will be treated as literal text). Instead, you can use the element's id via document.getElementById:

var selectedItemNew = document.getElementById(oppId);
6
  • Thanks, that's great, but isn't this a bad practice to use document.getElementById ? Commented Jan 2, 2018 at 20:21
  • @ItaiShmida Internally, Lightning actually uses a SecureDocument, so you should be okay using getElementById with your own components. You could also use getElementsByTagName or the like as well.
    – sfdcfox
    Commented Jan 2, 2018 at 20:30
  • 1
    So this is not something that will be unsupported or locked by locker service or some other excuse in the future? Commented Jan 2, 2018 at 20:35
  • @ItaiShmida Correct. Check out the Locker API Test page to see supported calls for SecureWindow, SecureDocument, and SecureElement.
    – sfdcfox
    Commented Jan 2, 2018 at 20:36
  • 1
    @ItaiShmida document.getElementById retrieves DOM elements. It's also inherently limited to getting just one element, so you need to make sure that the Id is unique within the component. aura:id with component.find is used to get components, which you'd use to access and modify its attributes and methods. For example, you couldn't use document.getElementById on a lightning:input.
    – sfdcfox
    Commented Jan 3, 2018 at 13:29
4

First problem is that we cannot use expression while assigning aura:id. Aura:id need to be static.

so your first change is

<a href="javascript:void(0);" role="menuitem" tabindex="-1" id="{!opp.Id}" onclick="{!c.openItemFromMenu}" aura:id="link">

in js you can do

    openItemFromMenu: function(component, event, helper) {
        // get the id from the event
        var oppId = event.getParam("recordId");
        // choose it (select menu)
        var selectedItemsNew = component.find('link').getElements();
        if(Array.isArray(selectedItemsNew)){
            var selectedItemNew = selectedItemsNew.find(function (oneItem){
return oppId === selectedItemsNew.id})
           if(selectedItemNew){
              selectedItemNew.style = "background: rgb(216, 237, 255);"
           }
        } else if(selectedItemsNew){
          if(oppId === selectedItemsNew.id){
             selectedItemNew.style = "background: rgb(216, 237, 255);"
          }
        }

    }

Instead of adding style directly you can create a class and use

$a.util.addclass()

2

I don't know if it will work for you, but what worked for me was just assigning the same static aura:id to each element in the iteration, and then when you use component.find() it will return an array of all the elements.

1
  • Thomas Sesselmann The same is true for me
    – Chris
    Commented Jan 17, 2019 at 22:44

You must log in to answer this question.

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