Search code examples
javascriptdrop-down-menusubmenuaurelia

Conditionally show submenu items for dropdown menu in Aurelia


For the dropdown menu I'm creating in Aurelia, I want to check if any of the li items being displayed have a submenu item and if so, display that submenu item and make that clickable instead of the main item. Since I'm not using a router, many of the questions/answers don't necessarily fit the situation.

Here's my current html:

<ul class="myDropdown" id="dropdownMenu">
  <li class="myDropdown_menu-option" repeat.for="item of config.items" click.trigger="clickHandler(item.action)">
    <i class="fa fa-user"></i> ${item.label}
    <ul class="myDropdown__submenu-container">
      <li class="myDropdown__menu-option">
        <span>
          <i class="fa fa-user"></i> Subitem Label
        </span>
      </li>
    </ul>
  </li>
</ul>

And here's my current JS (minus a few unrelated items):

export class Dropdown {
  parentContext;
  //config.items = [{ label:String, click:Function }]
  @bindable config = {
    items: [],
};

clickHandler( action ) {
    action.call(super.parentContext);
}

I guess the two things that I'm wanting to figure out are:

  • Since the items will be defined by the person using this component, routes can't be defined. So I'm wondering if/how I need to define subitems so that they can be accessed, repeated through and displayed if they exist? Would I just be adding to my comment how they should include subitems? Something like: config.items = [{ label:String, click: Function, subitem:[{label:String, click: Function}] }]
  • If I want to setup up a repeat for all the subitems (if they exist), how can I access those items? Would it be something like repeat.for="subitem.item of config.items.subitem"? I'm pretty sure that's incorrect so any pointers on that would be great.

Thanks for any guidance you can provide!

UPDATE: With the help of another person, I was able to make some changes to get the subitems to show up if they exist:

<ul class="myDropdown" id="dropdownMenu">
  <li class="myDropdown__menu-option" repeat.for="item of config.items" click.trigger="clickHandler(item.action)">
    <i class="fa fa-user"></i> ${item.label}
    <ul class="myDropdown__submenu-container">
      <li class="myDropdown__menu-option" repeat.for="subitem of items.subitems" click.trigger="clickHandler(subitems.action)">
        <span>
          <i class="fa fa-user"></i> ${subitem.label}
        </span>
      </li>
    </ul>
  </li>
</ul>

And I am filling the config object like this in my app.js file:

config = {
  items: [
    { label: 'Item 1', click: ()=>{}},
    { label: 'Item 2', click: ()=>{},
      subitems: [{ label: 'Subitem 2', click: ()=>{} }]
    }
  ]
};

So I guess the final thing I'm wondering then is how to make the click option conditional on if there is a subitem or not. For example, if there is no subitem, the item should be clickable but if there is a subitem, only the subitem should be clickable. Does the click.trigger need to called differently or on a different element so a condition can be applied?


Solution

  • I would recommend against a click function in your object, so that you can separate the "model" from the logic.

    Use clickHandler in the Dropdown class to decide what to do based on the clicked element. e.g.

    html

    <li class="myDropdown__menu-option" repeat.for="item of config.items" click.delegate="clickHandler(item)">
    

    viewModel:

    clickHandler(item) {
        if (item.subitems && item.subitems.length > 0) {
            // do something
        }
        // by default if there are no subitems, nothing is done
    }
    

    You could have a separate click handler for the subitems or use the same if the action is similar - that depends on your specifics.