How to create NavMenu with collapsible submenu in .Net Core Blazor app

24,874

Solution 1

Do not use the data-toggle and data-target for it.

These are used by boostrap.js however you do not want to modify the DOM in that way.

What you do instead is to use an if statement and thus let Blazor take care of the rendering:

    <NavLink class="nav-link" @onclick="()=>expandSubNav = !expandSubNav">
        <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
    </NavLink>
    @if (expandSubNav)
    {
        <NavLink class="expand-menu" href="">
            <span>Sub1</span>
        </NavLink>
        <NavLink class="" href="">
            <span>Sub2</span>
        </NavLink>
    }

And put the expandSubNav field into your code section:

@code {

    private bool expandSubNav;

}

Solution 2

<div class="nav-link" @onclick="()=>expandSubNav = !expandSubNav">
    <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</div>
@if (expandSubNav)
{
    <NavLink class="expand-menu" href="">
        <span>Sub1</span>
    </NavLink>
    <NavLink class="" href="">
        <span>Sub2</span>
    </NavLink>
}

use div instead of NAVLINK. Navlink reloads the page and reset the expandSubNav.

Solution 3

My Solution, after problems with not closing on click the submenu:

enter image description here

works on Mobile:

enter image description here

 <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
    <div class="container">
        <a class="navbar-brand" href="">MyPrgramm</a>
        <button class="navbar-toggler" type="button" @onclick="ToggleNavMenu">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item">
                    <NavLink class="nav-link text-dark" href="" Match="NavLinkMatch.All">
                        <span class="oi oi-home" aria-hidden="true"></span> Home
                    </NavLink>
                </li>
                <li class="nav-item">
                    <NavLink class="nav-link text-dark" href="counter">
                        <span class="oi oi-plus" aria-hidden="true"></span> Menu-II
                    </NavLink>
                </li>
                <li class="nav-item dropdown show">
                    <NavLink class="nav-link dropdown-toggle" @onclick="() => expandSubNavSettings = !expandSubNavSettings" id="navbarDropdown" >
                        <span class="oi oi-list-rich" aria-hidden="true"></span> Menu III
                    </NavLink>
                    @if (expandSubNavSettings)
                    {
                        <li class="dropdown-menu show" aria-labelledby="navbarDropdown" @onclick="() => expandSubNavSettings = !expandSubNavSettings">
                            <li class="nav-item">
                                <NavLink class="nav-link text-dark" href="fetchdata">
                                    <span class="oi oi-fork" aria-hidden="true"></span> Fetch
                                </NavLink>
                            </li>
                            <li class="nav-item">
                                <NavLink class="nav-link text-dark" href="counter">
                                    <span class="oi oi-command" aria-hidden="true"></span> Counter
                                </NavLink>
                            </li>
                            <li class="nav-item">
                                <NavLink class="nav-link text-dark" href="home">
                                    <span class="oi oi-home" aria-hidden="true"></span> Home
                                </NavLink>
                            </li>
                        </li>
                    }
                </li>



            </ul>
        </div>
    </div>
</nav>

@code {

bool collapseNavMenu = true;

private bool expandSubNavSettings;


string baseMenuClass = "navbar-collapse d-sm-inline-flex flex-sm-row-reverse";

string NavMenuCssClass => baseMenuClass + (collapseNavMenu ? " collapse" : "");

void ToggleNavMenu()
{
    if(!expandSubNavSettings)
    {
        collapseNavMenu = !collapseNavMenu;
    }


}

}

Solution 4

You can try this solution if you have more submenus

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">System</a>
    <button class="navbar-toggler" @onclick="() => ToggleNavMenu(navSubmenu)">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" @onclick="() => ToggleNavMenu(NavSubmenu.None)" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" @onclick="() => TogleSubmenu(NavSubmenu.First)">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Submenu 1
            </NavLink>
        </li>

        @if (navSubmenu == NavSubmenu.First)
        {
            <li class="nav-item px-5">
                <NavLink class="nav-link" @onclick="() => ToggleNavMenu()" href="counter">
                    <span class="oi oi-plus" aria-hidden="true"></span> Counter
                </NavLink>
            </li>
            <li class="nav-item px-5">
                <NavLink class="nav-link" @onclick="() => ToggleNavMenu()" href="fetchdata">
                    <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch Data
                </NavLink>
            </li>
        }

        <li class="nav-item px-3">
            <NavLink class="nav-link" @onclick="() => TogleSubmenu(NavSubmenu.Second)">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Submenu 2
            </NavLink>
        </li>

        @if (navSubmenu == NavSubmenu.Second)
        {
            <li class="nav-item px-5">
                <NavLink class="nav-link" @onclick="() => ToggleNavMenu()" href="counter">
                    <span class="oi oi-plus" aria-hidden="true"></span> Counter
                </NavLink>
            </li>
            <li class="nav-item px-5">
                <NavLink class="nav-link" @onclick="() => ToggleNavMenu()" href="fetchdata">
                    <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch Data
                </NavLink>
            </li>
        }
    </ul>
</div>

@code {
    private enum NavSubmenu
    {
        None,
        First,
        Second
    }

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
    private NavSubmenu navSubmenu =  NavSubmenu.None;
    private bool collapseNavMenu = true;


    private void ToggleNavMenu(NavSubmenu? submenu = null)
    {
        collapseNavMenu = !collapseNavMenu;
        navSubmenu = submenu ?? navSubmenu;
    }

    private void TogleSubmenu(NavSubmenu submenu)
    {
        if (navSubmenu == submenu)
            navSubmenu = NavSubmenu.None;
        else
            navSubmenu = submenu;
    }
}

Solution 5

(End result) I struggled quite a bit with submenus and ended up using this in my project:

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">Submenu master</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" @onclick="()=>ToggleNavMenu()" href=""     Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>

        <li class="nav-item px-3">
            <NavLink class="nav-link" @onclick="()=>ToggleSubmenu()">
                <span class="oi oi-menu" aria-hidden="true"></span> Menu
            </NavLink>
        </li>
        @if (expandMenu)
        {
            <li class="nav-item px-4" @onclick="ToggleNavMenu">
                <NavLink class="nav-link" href="counter">
                    <span class="oi oi-plus" aria-hidden="true"></span> Counter
                </NavLink>
            </li>
            <li class="nav-item px-4" @onclick="ToggleNavMenu">
                <NavLink class="nav-link" href="fetchdata">
                    <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
                </NavLink>
            </li>
        }
    </ul>
</div>

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private bool expandMenu;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
        expandMenu = false;
    }

    private void ToggleSubmenu()
    {
        expandMenu = !expandMenu;
    }
}
Share:
24,874
Sorush
Author by

Sorush

I am a PhD in Computational Fluid Dynamics (Maths + Physics + Coding). My passion is numerical programming but I always try new technologies. I started coding with QBasic at high school then picked Fortran and C/C++ for university projects. High-performance computing is necessary for CFD codes that's why I know OpenMP and MPI. Every now and then I visit Python to use its math libraries. I have developed many numerical apps with WPF(C#+XAML MVVM pattern) and, recently, several projects with Razor pages and Blazor WebAssembly. Therefore, I always have questions and answers about those topics.

Updated on July 09, 2022

Comments

  • Sorush
    Sorush almost 2 years

    I am trying to create a blazor navmenu which has a shape like this

    • item a

    • item b

    when I click on item b it expands with sub menu like this and clicking on subitems, new pages open

    • item a

    • item b

      • subitem 1

      • subitem 2

    I just edited the original blazor app but no success. The button appears but it doesn't collapse submenu. any idea?

    <div class="@NavMenuCssClass" @onclick="@ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="oi oi-plus" aria-hidden="true"></span> Counter
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="fetchdata">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch Data
            </NavLink>
        </li>
        <li class="dropdown">
    
            <button data-toggle="collapse" data-target="#demo">Collapsible</button>
    
            <div id="demo" class="collapse">
                <ul>
                    <li class="nav-item px-3">
                        <NavLink class="nav-link" href="meeting">
                            <span class="oi oi-plus" aria-hidden="true"></span> Meetings
                        </NavLink>
                    </li>
                    <li class="nav-item px-3">
                        <NavLink class="nav-link" href="conference">
                            <span class="oi oi-list-rich" aria-hidden="true"></span> Conferences
                        </NavLink>
                    </li>
                    <li class="nav-item px-3">
                        <NavLink class="nav-link" href="event">
                            <span class="oi oi-list-rich" aria-hidden="true"></span> Events
                        </NavLink>
                    </li>
    
                </ul>
            </div>
    
        </li>
    </ul>
    

  • Sorush
    Sorush over 4 years
    This works well, it moves the other items away to fit the new sub menu items, and if clicked again, removes sub menu and fills its place with other menu items. When I used syle="visibility:@visibilityString" where visibilityString="Hidden"/"Visible". The sub menus become appear/disappear but the menu items do not move.
  • John
    John over 4 years
    This is nice, but after load, expandSubNav again is false and its collapsed :/
  • Darth Scitus
    Darth Scitus over 4 years
    This answer is good, but it doesnt account for multiple submenus.
  • Diego Venâncio
    Diego Venâncio almost 4 years
    remove href="" for not reload page.
  • Wanabrutbeer
    Wanabrutbeer over 3 years
    Just another note, I used this method and worked great. However, if you'd like your node to expand if a user navigates to a page manually, say from an external link, or typing into the address bar, you can inject NavigationManager, use it to check the Uri of the current page, and if it matches a page in your sub menu then set the expandSubNav to true, do this in the OnInitializedAsync override
  • Joe
    Joe over 2 years
    This code doesn't work when pasted into VS Blazor Server template app. Sloppy.
  • Joe
    Joe over 2 years
    This code worked fine when pasted into VS Blazor Server template app. Nice work.
  • Jasiel Torres
    Jasiel Torres about 2 years
    This works perfectly but if you put more navlink in the menu the css of the side bar moves and the <li> go out of the margin of the sidebar, how could the problem be solved? I think it is caused by this: height: 100vh;
  • ZedZip
    ZedZip almost 2 years
    I have tried you suggestion. It is fine but: I click on menu, it expands and immediately collapsed. What to fix?
  • Twisted89
    Twisted89 almost 2 years
    By far the best answer here, works great on .NET 6 Blazor project.