1

I want to generate navigation menu dynamically which follows default routing of my application with specific actions and controllers. I figured it out in MVC5 but I don't know how to do same in .Net Core MVC application.

What I've tried in MVC5 is as below:

View Model :

public class MenuViewModel
{
    // other props
    public string Link
    {
        get
        {
            UrlHelper url = new UrlHelper(HttpContext.Current.Request.RequestContext);
            return url.Action(this.Action != null ? this.Action : "StaticPage", this.Controller != null ? this.Controller : "StaticPage", new { pageName = this.PageTitle });
        }
    }
}

Global.asax :

public static List<MenuViewModel> SetMenuHeads()
{
    return
        General.DataModel.Menus
        .Where(m => m.IsHead)
        .Select(MenuViewModel.Set)
        .ToList();
}

General.cs :

public static string SubMenuGenerator(MenuViewModel menu)
{
    string subMenus = "";

    var subMenusList =
        DataModel.Menus
        .Where(m => m.ParentId == menu.ID)
        .Select(MenuViewModel.Set)
        .ToList();

    if (subMenusList.Count == 0)
        subMenus += "<li><a href=\"" + menu.Link + "\" class=\"external\"><span>" + menu.RenderTitle + "</span></a></li>";
    else
    {
        subMenus += "<li><a href=\"#\" class=\"external\"><span>" + menu.Title + "</span></a>";
        subMenus += "<ul>";
        foreach (var mnu in subMenusList)
        {
            subMenus += SubMenuGenerator(mnu);
        }
        subMenus += "</ul>";
        subMenus += "</li>";
    }

    return subMenus;
}

_Layout.cshtml :

<nav id="main-nav">
    <ul id="main-menu" class="sm-clean">
        <li><a href="@Url.Action("Index", "Home")" class="external"><span>Home</span></a></li>
        @{
            string menu = "";
            foreach (var menuItem in global_asax.SetMenuHeads())
            {
                menu += General.SubMenuGenerator(menuItem);
            }
        }
        
        @Html.Raw(menu)
    </ul>
</nav>

Now I want to try to do same in .net core but there is no class like HttpContext.Current.Request.RequestContext in .net core to make a url with its action and controller.

Any idea?

1

1 Answer 1

2

In core, you can get all the paths through IActionDescriptorCollectionProvider.

If you want to make all routing into a navigation bar, then you need to add a custom middleware to the startup of the core project to pass the corresponding html to the _layout page.

Here is the complete example:

Custom Middleware:

  public class GetRoutingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;
        public GetRoutingMiddleware(RequestDelegate next, IActionDescriptorCollectionProvider actionDescriptorCollectionProvider)
        {
            _next = next;
            _actionDescriptorCollectionProvider = actionDescriptorCollectionProvider;
        }

        public async Task InvokeAsync(HttpContext context)
        {
             
            var allMenuList = _actionDescriptorCollectionProvider.ActionDescriptors.Items.Select(x => new
            {
                Action = x.RouteValues["Action"],
                Controller = x.RouteValues["Controller"],
                Name = x.AttributeRouteInfo != null ? x.AttributeRouteInfo.Name : "",
                Template = x.AttributeRouteInfo != null ? x.AttributeRouteInfo.Template : x.RouteValues["Controller"] + "/" + x.RouteValues["Action"],
            }).ToList(); 

            var menu = allMenuList.GroupBy(x => x.Controller).ToList();
            var subMenus = "";
            foreach (var subMenuList in menu)
            {
                if (subMenuList.Count() > 0)
                {
                    subMenus += "<li class=\"nav-item dropdown\"><a href=\"#\" class=\"nav-link dropdown-toggle\" role=\"button\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\"><span>" + subMenuList.Key + "</span></a>";
                    subMenus += "<div class=\"dropdown-menu\" arialabelledby=\"navbarDropdown\">";
                    foreach (var mnu in subMenuList)
                    {
                        subMenus += "<a class=\"dropdown-item\" href=\"/" + mnu.Template + "\">" + mnu.Action + "</a>";
                    }
                    subMenus += "</div>";
                    subMenus += "</li>";
                }
                else
                {
                    subMenus += "<li class=\"nav-item\"><a href=\"/" + subMenuList.FirstOrDefault().Template + "\" class=\"nav-link\"><span>" + subMenuList.Key + "</span></a></li>";
                }
            }
            context.Items.Add("routeMenu", subMenus);
            await _next.Invoke(context); 
        } 
    }

Add this middleware to Configure method :

   public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //...

            app.UseStaticFiles();

            app.UseMiddleware<GetRoutingMiddleware>(); 

            app.UseRouting();

            //....

        }

In _Layout.cshtml:

@{
    var menus = Context.Items["routeMenu"];
}

 <nav class="navbar navbar-expand-lg navbar-light bg-light">
            <div class="collapse navbar-collapse" id="navbarSupportedContent">
                <ul class="navbar-nav mr-auto">
                    @Html.Raw(menus)
                </ul>
            </div>
        </nav>

I made some changes to the style, you can change it according to your needs.

Here is the test result:

enter image description here

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.