1

Twitter bootstrap has a dropdown menu option; where a menu has have multiple layers. See: http://getbootstrap.com/javascript/#dropdowns

How can I use Aurelia.js's routers to recreate this? Routers normally provide 1 level. I need 2 levels.

Phil
  • 2,143
  • 19
  • 44
  • Is this about getting the router to give you an array or other structure defining the navigable routes in terms that allow you to generate a menu? Otherwise the router really doesn't care how many menu levels you use to present choices in the UI and can handle any number of levels. I don't want to answer for Aurelia, since I still use Durandal. I believe Aurelia has the nav:true property as Durandal does to make it easy to get a list of navigable routes. – joshp Dec 28 '15 at 01:46
  • @joshp You are right and your main point is the question I am asking. the property: 'nav' is just for showing or hiding and not related to the question per se. – Phil Dec 28 '15 at 02:06
  • nav: is related in the sense of excluding routes that are not supposed to appear at all, but it says nothing about hierarchy. But I have never expected the router to provide presentation hierarchy. I supply that when needed, and I never wanted the router to know about it. I reuse routes from many places and I don't see the router as a menu hierarchy. I try to keep the routes concise. So your question interests me, a different way of thinking. – joshp Dec 28 '15 at 02:41
  • @joshp I got the answer from: https://gist.github.com/adarshpastakia/5d8462b5bc8d958d5cb3 – Phil Dec 31 '15 at 01:02
  • I did not like the suggestion in the answer. I am not sure whether that will track the active route for sub-menus either. I explain my way around the problem on another post with a similar question. http://stackoverflow.com/questions/37217227/how-to-define-and-render-submenu-items-using-aurelias-router/37378720#37378720 – Pieter May 22 '16 at 20:07

3 Answers3

4

Credit for this goes to: https://github.com/adarshpastakia.

I "borrowed" most of this person's code to answer this question. You can find it at: https://gist.github.com/adarshpastakia/5d8462b5bc8d958d5cb3

Here are steps to answer the question above:
(1) In the router, add a "settings" property. It can be whatever you want. Here is an example:
settings:{ subMenu:[ {href:'#/sub1', title:'Submenu 1'}, {href:'zoldello.wordpress.com', title:'Submenu 2'}, {href:'#/sub3', title:'Submenu 3'} ] }

Note: (a)It must be called "settings" (b) Aurelia currently ignores custom code you write outside "settings" (c)In "settings", you can place any property in it you like

(2)
(a) From (1) above, if you need the submenu to route to a part of the page, in href (or whatever you call it) use "#sub1"; where "sub1" refers to a different route where nav is set to false.
(b) If you want a hyperlink independent of routing, set href (or whatever you call it) to the url you desire (I used "zoldello.wordpress.com" in my example). No need to add a additional route

(3) Follow the basic aurelia rules of building DOM (repeat, template etc)

Here is an example:

HTML file

<li repeat.for="route of router.navigation">
  <!-- if route has no submenu -->
  <a href.bind="route.href" if.bind="!route.settings.subMenu">${route.title}</a>

  <!-- if route has submenu -->
  <a href.bind="javascript:;" class="dropdown-toggle" data-toggle="dropdown" 
  role="button" aria-haspopup="true" aria-expanded="false" if.bind="route.settings.subMenu">
  ${route.title} <span class="caret"></span></a>

  <ul if.bind="route.settings.subMenu">
    <li repeat.for="menu of route.settings.subMenu">
      <a href.bind="menu.href">${menu.title}</a>
    </li>
  </ul>
</li>



Javascript file

configureRouter(config) {
  config.map([{
    route:'home',
    title:'Home',
    nav:true,
    module:'home'
  },{
    route:'top-menu',
    title:'Top Menu',
    nav:true,
    settings:{
      subMenu:[
        {href:'#/sub1', title:'Submenu 1'},
        {href:'zoldello.wordpress.com', title:'Submenu 2'},
        {href:'#/sub3', title:'Submenu 3'}
      ]
    }
  }, {
    route:'sub1',
    title:'Submenu 1',
    nav:false,
    moduleId:'module'
  }, {
    route:'sub2',
    title:'Submenu 2',
    nav:false,
    moduleId:'module'
  }, {
    route:'sub3',
    title:'Submenu 3',
    nav:false,
    moduleId:'module'
  }])
}
Phil
  • 2,143
  • 19
  • 44
  • 1
    Aurelia throws an exception if you not specify a module (or an alternative valid option) on that second route. `You must specify a moduleId, redirect, navigationStrategy, or viewPorts.` – John Jun 09 '16 at 08:38
0

small fix for Phil's answer

html:

<li class=" ${route.settings.subMenu ? '' : 'dropdown'} " repeat.for="route of router.navigation">
                <!-- if route has no submenu -->
                <a href.bind="route.href" if.bind="!route.settings.subMenu">${route.title}</a>

                <!-- if route has submenu -->
                <a href="#" class="dropdown-toggle" data-toggle="dropdown" if.bind="route.settings.subMenu">
                    ${route.title} <span class="caret"></span>
                </a>

                <ul class="dropdown-menu" if.bind="route.settings.subMenu">
                    <li repeat.for="menu of route.settings.subMenu">
                        <a href.bind="menu.href">${menu.title}</a>
                    </li>
                </ul>
            </li>

js

configureRouter(config) { config.map([{
route:'home',
title:'Home',
nav:true,
moduleid:'home'  },{
route:'top-menu',
title:'Top Menu',
nav:true,
moduleid:'module'
settings:{
  subMenu:[
    {href:'#/sub1', title:'Submenu 1'},
    {href:'zoldello.wordpress.com', title:'Submenu 2'},
    {href:'#/sub3', title:'Submenu 3'}
  ]
} }, {
route:'sub1',
title:'Submenu 1',
nav:false,
moduleId:'module'  }, {
route:'sub2',
title:'Submenu 2',
nav:false,
moduleId:'module'  }, {
route:'sub3',
title:'Submenu 3',
nav:false,
moduleId:'module'  }])}
user1536720
  • 93
  • 1
  • 9
0

I was looking for the same thing when I landed here. The above answers where helpful but didn't take me as far as I needed to go. I learned of Bootstrap 3 dropdown sub menu missing and the work-arounds offered there but again needed something more. Then I found http://www.smartmenus.org/ which provides the responsiveness and functionality I need in a drop-down menu for Aurelia for our production app. I worked with their team to get a version of the skeleton app that works well. You can download it at http://www.smartmenus.org/files/demos/aurelia/SmartMenusDemo.zip.

The app.ts file is similar to the example above but adds support for font-awesome icons:

import {RouterConfiguration, Router} from 'aurelia-router';
import 'jquery';
import 'bootstrap';
import 'smartmenus';
import 'smartmenus-bootstrap';

export class App {
    router: Router;

    configureRouter(config: RouterConfiguration, router: Router) {
        config.title = 'Aurelia';
        config.map([
            { route: ['', 'welcome'], name: 'welcome', moduleId: './welcome', nav: true, title: 'Welcome' },
            {
                route: 'users', name: 'users', moduleId: './users', nav: true, title: 'Show Users',
                settings: {
                    subMenu: [{ href: '#/users', title: 'Users', iconClass: 'fa fa-slideshare' },
                        { href: '#/usersTest', title: 'Test Users', iconClass: 'fa fa-street-view' },
                                {
                                    href: '#', title: 'Sub Sub', iconClass: 'fa fa-medium', settings: {
                                        subMenu: [{ href: '#/child-router', title: 'Child Router', iconClass: 'fa fa-registered' },
                                            { href: '#/usersTest', title: 'Test Users', iconClass: 'fa fa-street-view' }]
                                    }
                                },
                                { href: '#/child-router', title: 'Child Router', iconClass: 'fa fa-registered' }]
                }
            },
            { route: 'usersTest', name: 'usersTest', moduleId: './usersTest', nav: false, title: 'Test Users' },
            { route: 'child-router', name: 'childRouter', moduleId: 'child-router', nav: false, title: 'Child Router' },
            { route: 'users', name: 'users', moduleId: './users', nav: true, title: 'Github Users' }
        ]);

        this.router = router;
    }

    attached(): void {
        $.SmartMenus.Bootstrap.init();
    }

}

I implemented support for 4 levels of sub-menus in nav-bar.html:

<template bindable="router">
    <nav class="navbar navbar-default navbar-fixed-top" role="navigation">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#top-level-nav">
                <span class="sr-only">Toggle Navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">
                <i class="fa fa-home"></i>
                <span>${router.title}</span>
            </a>
        </div>

        <div class="collapse navbar-collapse" id="top-level-nav">
            <ul id="main-menu" class="nav navbar-nav ">
                <li repeat.for="route of router.navigation" class="${row.isActive ? 'active' : ''} ${route.settings.subMenu ? 'dropdown' : ''}">
                    <!-- if route has no submenu -->
                    <a href.bind="route.href" if.bind="!route.settings.subMenu">${route.title}</a>

                    <!-- if route has submenu -->
                    <a if.bind="route.settings.subMenu" class="dropdown-toggle" data-toggle="dropdown"
                       role="button" aria-haspopup="true" aria-expanded="true">
                        ${route.title} <span class="caret"></span>
                    </a>
                    <ul if.bind="route.settings.subMenu" class="dropdown-menu">
                        <li repeat.for="l2SubMenu of route.settings.subMenu">
                            <a href.bind="l2SubMenu.href" if.bind="!l2SubMenu.settings.subMenu"><span class.bind="l2SubMenu.iconClass"> </span>&nbsp&nbsp${l2SubMenu.title}</a>
                            <a href.bind="l2SubMenu.href" if.bind="l2SubMenu.settings.subMenu" class="dropdown-toggle" data-toggle="dropdown"
                               role="button" aria-haspopup="true" aria-expanded="true">
                                <span class.bind="l2SubMenu.iconClass"> </span>&nbsp&nbsp${l2SubMenu.title} <span class="caret"></span>
                            </a>
                            <ul if.bind="l2SubMenu.settings.subMenu" class="dropdown-menu">
                                <li repeat.for="l3SubMenu of l2SubMenu.settings.subMenu">
                                    <a href.bind="l3SubMenu.href" if.bind="!l3SubMenu.settings.subMenu"><span class.bind="l3SubMenu.iconClass"> </span>&nbsp&nbsp${l3SubMenu.title}</a>
                                    <a href.bind="l3SubMenu.href" if.bind="l3SubMenu.settings.subMenu" class="dropdown-toggle" data-toggle="dropdown"
                                       role="button" aria-haspopup="true" aria-expanded="true">
                                        <span class.bind="l3SubMenu.iconClass"> </span>
                                        &nbsp&nbsp${l3SubMenu.title} <span class="caret"></span>
                                    </a>
                                    <ul if.bind="l3SubMenu.settings.subMenu" class="dropdown-menu">
                                        <li repeat.for="l4SubMenu of l3SubMenu.settings.subMenu">
                                            <a href.bind="l4SubMenu.href"><span class.bind="l4SubMenu.iconClass"> </span>&nbsp&nbsp${l4SubMenu.title}</a>
                                        </li>
                                    </ul>
                                </li>
                            </ul>
                        </li>
                    </ul>
                </li>
            </ul>

            <ul class="nav navbar-nav navbar-right">
                <li class="loader" if.bind="router.isNavigating">
                    <i class="fa fa-spinner fa-spin fa-2x"></i>
                </li>
            </ul>
        </div>
    </nav>
</template>

As you can see, SmartMenus didn't require much change to the app but provides us with great responsive multi-level drop down menus in Aurelia with the power of Bootstrap regardless of Bootstrap depreciating support for that.

Hope this helps someone !

Community
  • 1
  • 1
David Bowser
  • 107
  • 2
  • 10