Is it possible (and is it appropriate) to use nested/child targets of parent target?
For example i have “N+“ menu items, each item (wrapper) contain link and list.
I could add data-main-menu-target="menuItem" to each of parent items and then iterate over them in controller loop using this.menuItemTargets.forEach(...)
But what's the best practice to find menu-item-link and menu-item-list for each menu-item target on each loop iteration?
In general i could add targets also for those elements, e.g. menuItemLink & menuItemList, but how then i could select them from parent menuItem target, Is it possible to do something like menuItemTarget.find(this.this.menuListTarget)?
To visualise the structure is the following:
data-controller="main-menu"
data-main-menu-target="menuItem"
data-main-menu-target="menuLink"
data-main-menu-target="menuList"
....
data-main-menu-target="menuItem"
data-main-menu-target="menuLink"
data-main-menu-target="menuList"
....
How then select "menuLink" for certain "menuItem" target on each loop?
You could structure your controller so that you have one menu controller that gets used on both the root menu and also the sub-menus within them. This could be recursively accessed from whatever is deemed to to be the root.
ul for a menu, each child should be an item target.item we may have a link target OR another sub-menu which itself is another menu controller and the pattern continues.<nav>
<ul class="menu-list" data-controller="menu" data-menu-target="root">
<li data-menu-target="item">
<a data-menu-target="link">Team Settings</a>
</li>
<li data-menu-target="item">
<ul data-controller="menu">
<li data-menu-target="item">
<a data-menu-target="link">Members</a>
</li>
<li data-menu-target="item">
<a data-menu-target="link">Plugins</a>
</li>
<li data-menu-target="item">
<a data-menu-target="link">Add a member</a>
</li>
</ul>
</li>
<li data-menu-target="item">
<a data-menu-target="link">Invitations</a>
</li>
<li data-menu-target="item">
<a data-menu-target="link">Cloud Storage Environment Settings</a>
</li>
</ul>
</nav>
this.hasRootTarget.data-controller='menu'.setTimeout to wait for any sub-controllers to connect, there may be a nicer event propagation way to do this.getControllerForElementAndIdentifier method.link target OR a nested array which itself will contain the sub link targets.item and see what links are 'contained' within it.class MenuController extends Controller {
static targets = ['item', 'link', 'root'];
connect() {
if (this.hasRootTarget) {
setTimeout(() => {
// must use setTimeout to ensure any sub-menus are connected
// alternative approach would be to fire a 'ready' like event on submenus
console.log('main menu', this.getMenuStructure());
});
}
}
getMenuStructure() {
const links = this.linkTargets;
return this.itemTargets.map((item) => {
const child = item.firstElementChild;
const subMenu = this.application.getControllerForElementAndIdentifier(
child,
this.identifier
);
const menuLinks = links.filter((link) => item.contains(link));
return subMenu ? subMenu.getMenuStructure() : menuLinks;
});
}
}
firstElementChild and this may not be the way we want to do things in Stimulus, but you could simply add another target type of 'sub-menu' to be more explicit and follow the pattern of finding the 'link' within each item this way.data-controller="menu" on a data-menu-target="item" as this will remove the item from the parent scope. As per the docs on scopes.that element and all of its children make up the controller’s scope.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With