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 ? 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
    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? 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
    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
    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
    Jan 17, 2019 at 22:44

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.