Add Menu Items to Customer Navigation Including Sub menus

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
3 years ago


Im new to NOP. How cant i do something like this using only a plugin. if not easy just using plugin then theme + plugin approach also acceptable.
Using v4.3
3 years ago
Hello Chamila1026
Go with below link, it is help for you.
https://www.nopcommerce.com/en/boards/topic/84118/override-preparecustomernavigationmodel#268879
3 years ago
SagarKayasth wrote:

Thanks a lot for quick reply.

Sorry for this stupid question but where do i place those codes?

If i understood correctly, adding an item to CustomerNavigationItems add a menu item. But i want sub menus under it
3 years ago
Chamila1026 wrote:
Hello Chamila1026
Go with below link, it is help for you.
https://www.nopcommerce.com/en/boards/topic/84118/override-preparecustomernavigationmodel#268879

Thanks a lot for quick reply.

Sorry for this stupid question but where do i place those codes?

If i understood correctly, adding an item to CustomerNavigationItems add a menu item. But i want sub menus under it


Create model prepared event in plugin
PreparedModelConsumer.cs file


public class PreparedModelConsumer : IConsumer<ModelPreparedEvent<BaseNopModel>>
{
   public void HandleEvent(ModelPreparedEvent<BaseNopModel> eventMessage)
  {
    if (eventMessage?.Model is CustomerNavigationModel customerNavigationModel)
    {
      customerNavigationModel.CustomerNavigationItems.Add(new CustomerNavigationItemModel
        {
          Title = "Parent",
          Tab = (CustomerNavigationEnum)140,
          ItemClass = "parent"
        });

        var child1= new CustomerNavigationItemModel
        {
          RouteName = "url",
          Title = "plugin option 1",
          Tab = (CustomerNavigationEnum)140,
          ItemClass = "child1",
        };
        child1.CustomProperties.Add("ParentMenu", "Parent");

        customerNavigationModel.CustomerNavigationItems.Add(child1);
    }
  }
}


View file changes
Please override CustomerNavigation view plugin side and write below code


@model CustomerNavigationModel
@{
    var childMenu = Model.CustomerNavigationItems.Where(x => x.CustomProperties.ContainsKey("ParentMenu") && x.CustomProperties["ParentMenu"] == "Parent").ToList();
    for (int i = 0; i < childMenu.Count; i++)
    {
        Model.CustomerNavigationItems.Remove(childMenu[i]);
    }
}
<div class="block block-account-navigation">
    <div class="title">
        <strong>@T("Account.Navigation")</strong>
    </div>
    <div class="listbox">
        <ul class="list">
            @await Component.InvokeAsync("Widget", new { widgetZone = PublicWidgetZones.AccountNavigationBefore })
            @foreach (var item in Model.CustomerNavigationItems)
            {
                if (item.Title == "Parent")
                {
                    <li class="@item.ItemClass">
                        @(item.Title)
                        @if (childMenu.Count > 0)
                        {
                            for (int i = 0; i < childMenu.Count; i++)
                            {
                                <ul>
                                    <li class="@childMenu[i].ItemClass" style="margin-left: 10px;">
                                        <a href="@Url.RouteUrl(childMenu[i].RouteName)">@(childMenu[i].Title)</a>
                                    </li>
                                </ul>
                            }
                        }
                    </li>
                }
                else
                {
                    <li class="@item.ItemClass @(Model.SelectedTab == item.Tab ? "active" : "inactive")">
                        <a href="@Url.RouteUrl(item.RouteName)">@(item.Title)</a>
                    </li>
                }
            }
            @await Component.InvokeAsync("Widget", new { widgetZone = PublicWidgetZones.AccountNavigationAfter })
        </ul>
    </div>
</div>



Hope so you are understand with it.
3 years ago
Had some trouble overriding the view but finally got it working.
Thank you so much
3 years ago
Just in case anyone need the whole code
Customized to have any amount of menus and sub menus

NOP.Plugin.yourplugin/Infrastructure/CustomerViewEngine.cs

using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Razor;

namespace Nop.Plugin.yourplugin.Infrastructure
{
    class CustomerViewEngine: IViewLocationExpander
    {
        public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
        {
            if (context.AreaName == null && context.ViewName == "Components/CustomerNavigation/Default")
            {
                viewLocations = new[] { $"/Plugins/yourplugin/Views/{{0}}.cshtml" }.Concat(viewLocations);
            }

            return viewLocations;
        }

        public void PopulateValues(ViewLocationExpanderContext context)
        {
            //throw new NotImplementedException();
        }
    }
}


NOP.Plugin.yourplugin/Infrastructure/NopStartup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Nop.Core.Infrastructure;

namespace Nop.Plugin.yourplugin.Infrastructure
{
    class NopStartup : INopStartup
    {
        public int Order => int.MaxValue;

        public void Configure(IApplicationBuilder application)
        {
            //throw new NotImplementedException();
        }

        public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
        {
            services.Configure<RazorViewEngineOptions>(options =>
            {
                options.ViewLocationExpanders.Add(new CustomerViewEngine());
            });
        }
    }
}


NOP.Plugin.yourplugin/Models/PreparedModelConsumer.cs

using Nop.Services.Events;
using Nop.Web.Framework.Events;
using Nop.Web.Framework.Models;
using Nop.Web.Models.Customer;

namespace Nop.Plugin.yourplugin.Models
{
    class PreparedModelConsumer : IConsumer<ModelPreparedEvent<BaseNopModel>>
    {
        public void HandleEvent(ModelPreparedEvent<BaseNopModel> eventMessage)
        {
            if (eventMessage?.Model is CustomerNavigationModel customerNavigationModel)
            {
                var parent1 = new CustomerNavigationItemModel
                {
                    RouteName = "NOROUTE",
                    Title = "Parent 1",
                    Tab = (CustomerNavigationEnum)140,
                    ItemClass = "parent1"
                };
                customerNavigationModel.CustomerNavigationItems.Add(parent1);

                var child1 = new CustomerNavigationItemModel
                {
                    RouteName = "routename-or-url",
                    Title = "Child 1",
                    Tab = (CustomerNavigationEnum)140,
                    ItemClass = "child1",
                };
                child1.CustomProperties.Add("ParentMenu", "Parent 1"); // define the parent. use title of the parent
                customerNavigationModel.CustomerNavigationItems.Add(child1);

                var child2 = new CustomerNavigationItemModel
                {
                    RouteName = "routename-or-url",
                    Title = "Child 2",
                    Tab = (CustomerNavigationEnum)140,
                    ItemClass = "child2",
                };
                child2.CustomProperties.Add("ParentMenu", "Parent 1"); // define the parent. use title of the parent
                customerNavigationModel.CustomerNavigationItems.Add(child2);

                var parent2 = new CustomerNavigationItemModel
                {
                    RouteName = "NOROUTE",
                    Title = "Parent 2",
                    Tab = (CustomerNavigationEnum)140,
                    ItemClass = "parent2"
                };
                customerNavigationModel.CustomerNavigationItems.Add(parent2);

                var child3 = new CustomerNavigationItemModel
                {
                    RouteName = "routename-or-url",
                    Title = "Child 3",
                    Tab = (CustomerNavigationEnum)140,
                    ItemClass = "child3",
                };
                child3.CustomProperties.Add("ParentMenu", "Parent 2"); // define the parent. use title of the parent
                customerNavigationModel.CustomerNavigationItems.Add(child3);

                var child4 = new CustomerNavigationItemModel
                {
                    RouteName = "routename-or-url",
                    Title = "Child 4",
                    Tab = (CustomerNavigationEnum)140,
                    ItemClass = "child4",
                };
                child4.CustomProperties.Add("ParentMenu", "Parent 2"); // define the parent. use title of the parent
                customerNavigationModel.CustomerNavigationItems.Add(child4);
            }
        }
    }
}


NOP.Plugin.yourplugin/Views/Components/CustomerNavigation/Default.cshtml

@using Nop.Web.Models.Customer;
@using Nop.Web.Framework.Infrastructure;

@model CustomerNavigationModel
@{
    var childMenu = new Dictionary<string, List<CustomerNavigationItemModel>>();
    foreach (var item in Model.CustomerNavigationItems.ToList())
    {
        if (item.CustomProperties.ContainsKey("ParentMenu"))
        {
            if (!childMenu.ContainsKey(item.CustomProperties["ParentMenu"].ToString()))
            {
                childMenu[item.CustomProperties["ParentMenu"].ToString()] = new List<CustomerNavigationItemModel>();
            }
            childMenu[item.CustomProperties["ParentMenu"].ToString()].Add(item);
            Model.CustomerNavigationItems.Remove(item);
        }
    }
}
<div class="block block-account-navigation">
    <div class="title">
        <strong>@T("Account.Navigation")</strong>
    </div>
    <div class="listbox">
        <ul class="list">
            @await Component.InvokeAsync("Widget", new { widgetZone = PublicWidgetZones.AccountNavigationBefore })
            @foreach (var item in Model.CustomerNavigationItems)
            {
                <li class="@item.ItemClass @(Model.SelectedTab == item.Tab ? "active" : "inactive")">
                    <a href="@Url.RouteUrl(item.RouteName)">@(item.Title)</a>
                    @if (childMenu.ContainsKey(item.Title))
                    {
                        foreach (var child in childMenu[item.Title])
                        {
                            <ul>
                                <li class="@child.ItemClass" style="margin-left: 10px;">
                                    <a href="@Url.RouteUrl(child.RouteName)">@(child.Title)</a>
                                </li>
                            </ul>
                        }
                    }
                </li>
            }
            @await Component.InvokeAsync("Widget", new { widgetZone = PublicWidgetZones.AccountNavigationAfter })
        </ul>
    </div>
</div>


NOP.Plugin.yourplugin/Nop.Plugin.yourplugin.csproj

...
<ItemGroup>
    <ProjectReference Include="..\..\Presentation\Nop.Web.Framework\Nop.Web.Framework.csproj" />
    <ProjectReference Include="..\..\Presentation\Nop.Web\Nop.Web.csproj" /> <!--add this line-->
    <ClearPluginAssemblies Include="$(MSBuildProjectDirectory)\..\..\Build\ClearPluginAssemblies.proj" />
  </ItemGroup>
...
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.