Recursive contextual components
Let's kick off our nested dropdown by building out a skeleton List and Item component that yield each other.
Summary
To start off our nested dropdown, let's look at an HTML representation of a list with sublists:
<ul class='list-reset flex'>
<li class='relative p-4 bg-grey-light hover:bg-yellow cursor-pointer'>
Menu item A
<ul class='list-reset absolute pin-l mt-4 bg-white shadow'>
<li class='p-4'>Subitem A 1</li>
<li class='p-4'>Subitem A 2</li>
<li class='p-4'>Subitem A 3</li>
</ul>
</li>
<li class='relative p-4 bg-grey-light hover:bg-yellow cursor-pointer'>
Menu item B
<ul class='list-reset absolute pin-l mt-4 bg-white shadow'>
<li class='p-4'>Subitem B 1</li>
<li class='p-4'>Subitem B 2</li>
</ul>
</li>
<li class='p-4 bg-grey-light hover:bg-yellow cursor-pointer'>
Menu item C
</li>
</ul>
In HTML we can render unordered lists within list items, as many levels deep as we like. Our goal will be to end up with something similar for our nested dropdown component.
Let's think about what our API should look like. We want to render a dropdown list that has many items, and each one of those items can optionally render another nested dropdown list, just like in HTML.
It might look something like this:
<DropdownList as |List|>
<List.item as |Item|>
Menu A
<Item.sublist as |List|>
<List.item>
Submenu A 1
</List.item>
<List.item>
Submenu A 2
</List.item>
</Item.sublist>
</List.item>
<List.item>
Menu B
</List.item>
</DropdownList>
This API mirrors HTML's <ul>
and <li>
tags, but uses contextual components to associate each item with its parent list.
Let's create the <DropdownList>
component. It has a tag of ul
and yields out a <DropdownListItem>
:
export default Component.extend({
tagName: 'ul'
});
{{yield (hash
sublist=(component 'dropdown-list')
)}}
Now our <DropdownListItem>
, which has a tag of li
and yields out a sublist, which just renders another instance of <DropdownList>
:
export default Component.extend({
tagName: 'ul'
});
{{yield (hash
item=(component 'dropdown-list-item')
)}}
So List yields Item, and Item yields List, and we can keep rendering these infinitely deep.
Now that we have the skeleton set up, we're ready to start working on the actual behavior.