AngularJS "Accordion" style menu + dynamic data + submenus + using li ---- working example

15,616

Solution 1

Here is an angular directive that is based on bootstrap's collapse plugin. It uses zero jQuery and you can customize it further if you want.

Demo Plunker

The accordion behavior relies on the following template from Bootstrap:

<div id="parent">
    <div class="panel">
        <div data-toggle="collapse" data-parent="#parent" data-target="#child">test</div>
        <div id="child" class="collapse in">
            This is the content
        </div>
    </div>
    <div class="panel">
        <div data-toggle="collapse" data-parent="#parent" data-target="#child2">test</div>
        <div id="child2" class="collapse">
            This is the content
        </div>
    </div>
</div>

Solution 2

If you are already using Bootstrap for styling I highly recommend to use Angular-UI Bootstrap. Using this module you don't have to reinvent the wheel. Just take a look at this example, which is based on the Accordion example in the documentation.

Share:
15,616
efraimip
Author by

efraimip

Updated on June 04, 2022

Comments

  • efraimip
    efraimip almost 2 years

    What I am trying to do is make an all purpose accordion-style navigation menu, with submenus, in AngularJS. It must dynamically load menu data, dynamically assign which menu items have submenus and which do not, and so forth - full list below.

    I have tried with the angular-ui/bootstrap accordion elements but dont quite like how it allows me to handle their behaviour. It seems counterintuitive if I'm using <\accordion> and want to remove the accordion behaviour. I've resulted back to using list-items (<\li>) as it allows the most flexibility / styling.

    What I want will do the following:

    • Dynamic loading of menu-data
    • Dynamic loading of sub-menu-data
    • Dynamic loading of which menus have submenus, and which dont
    • Accordion-style: clicking one menu item with a submenu, will close the other submenus.
    • Each end-point menu is a link: if a menu has no submenus, it is a link->navigation -- if a menu has submenus, it opens its submenu on-click, and does not navigate.
    • Clicking away from the menu area all-together, will collapse all submenus.
    • Status-aware: it should be aware of which menu is active, allowing us to set class="active({{menuItem.url}})" type thing on the active menu item.
    • Submenu-aware: we should be able to add chevrons up/down/left/right to indicate which menu element has submenus - and in which state the submenu is in (open/closed).
    • As much as can be done in CSS/HTML, should be.


    Based on this post here: Closing open submenu - jQuery accordion
    I have a working version of this in jQuery - fiddle here: http://jsfiddle.net/52EH8/20/

    function initMenu() {
         $('#nav ul').hide();
         $('#nav li a').click(
    
         function () {
    
             var checkElement = $(this).next();
             if ((checkElement.is('ul')) && (checkElement.is(':visible'))) {
                 $('#nav ul:visible').slideToggle('normal');
             }
             if ((checkElement.is('ul')) && (!checkElement.is(':visible'))) {
                 removeActiveClassFromAll();
                 $(this).addClass("active");
                 $('#nav ul:visible').slideToggle('normal');
                 checkElement.slideToggle('normal');
                 return false;
             }
    
             if($(this).siblings('ul').length==0 && $(this).parent().parent().attr('id')=='nav')
             {
    
                 removeActiveClassFromAll();
                 $(this).addClass("active");
                 $('#nav ul:visible').slideToggle('normal');
    
                 return false;
             }
         });
     }
    
     function removeActiveClassFromAll() {
         $('#nav li a').each(function (index) {
             $(this).removeClass("active");
         });
     }
    
    
     $(document).ready(function () {
         initMenu();
     });
    
     $('#nav').click(function (e)
    
     {
         e.stopPropagation();
    
    
     })
    
    
    
    
     $(document).click(function () {
         $('#nav').children('li').each(function () {
             if ($(this).children('ul').css('display') == 'block') {
                 $(this).children('ul').slideToggle('normal')
                 $(this).children('a').removeClass('active')
             }
         })
     })
    

    My JSON menu (coffee-script) is:

    S.mainMenu = [
            name: 'Home'
            url: 'home'
            icon: 'fa-home'
        ,
            name: 'Events'
            url: 'events'
            icon: 'fa-bullhorn'
        ,
            name: 'Jobs'
            url: 'jobs'
            icon: 'fa-laptop'
        ,
            name: 'Resources'
            icon: 'fa-cloud-download'
            submenu: [
                name: 'Guides & Tutorials'
                url: 'res-guides'
                icon: 'fa-lightbulb-o'
            ,
                name: 'Docs & Templates'
                url: 'res-docs'
                icon: 'fa-file-text'
            ,
                name: 'Photos & Video'
                url: 'res-media'
                icon: 'fa-film'
            ]
        ,
            name: 'TechGrind'
            icon: 'fa-cogs'
            submenu: [
                name: 'Startup Hubs'
                url: 'tg-hubs'
                icon: ''
            ,
                name: 'Membership'
                url: 'tg-members'
                icon: ''
            ,
                name: 'Community'
                url: 'tg-activities'
                icon: ''
            ,
                name: 'Incubator'
                url: 'tg-incubator'
                icon: ''
            ]
        ];
    

    As you can see - there are 3 normal menus, and 2 menus-with-submenus.

    More or less - how would one properly convert this from jQuery to "thinking Angular"?? ATM my main issue is: where to properly put $(document).ready to trigger initMenu()??

    I have a strong feeling that this should all be placed into a directive instead, however - or 2 directives (1=top-level accordion menu, 2=submenu). I am not that good with directives yet though so this is my problem.

    I have seen many many posts about this type of menu - it would be beneficial to allot of people I think to make one that is generic, dynamically loaded, with X number of menus, Y number of submenus, and so forth. I've formatted my question ACCORDINgly (pun intended) so that it can hopefully serve all needs.

    Thanks in advance!

  • efraimip
    efraimip almost 10 years
    thanks - already did try that, and it works fine - but does not achieve the above mentioned goals. it is also bringing with it accordion, which is much harder to change the behaviour on than a ul/li.
  • efraimip
    efraimip almost 10 years
    thanks - I also found this example which is similar to yours but a bit different implementation. plnkr.co/edit/IdrvGdU8DSgn1r0OBCMz?p=preview