orchardcmsorchardcms-1.9

Orchard CMS - create vertical Navigation Menu next to Home


I am new to Orchard and trying to figure out how it works code-wise. So, I have created a custom Content Type through the code and i am able to create Content Items under this Content Type. i have the 'Show on the Menu' checkbox on the editor page of the Content item. But when I check it and select the menu to which i want this newly created custom item to be added, it gets added as a vertical menu item whereas i need it to be added as a vertical submenu to one of the root items. Please find images which describe what is happening now and what I need. Current behavior Expected behavior

Product2 is a custom content item and should be added as a entry in the vertical menu as shown in the 2nd image


Solution

  • This task is rather complex. There are several steps involved.

    1. Understanding how to create a child theme

      See the official documentation, create a child theme and enable it

    2. Understanding the concept of shape alternates

      See the official documentation

    3. Configure the menu in admin area

      Go to the admin area, click on Navigation in the menu and add some menu items and sub items, for example

      [ Home (Content Menu Item) ]
      [ Service (Content Menu Item) ]
        [ Document Storage (Custom Link) ]
      

      Once you have this structure Orchard renders this structure via a @Zone(Model.Navigation) call in a theme. You have to search where this call is located for yourself, it depends on the theme.

      My child theme uses a Layout.cshtml alternate which calls @Zone(Model.Navigation) where needed like so

      @{
        Func<dynamic, dynamic> Zone = x => Display(x); // Zone as an alias for Display to help make it obvious when we're displaying zones
      }
      
      <div class="wrapper">
          @* Navigation bar *@
          @if (Model.Navigation != null)
          {
            <div id="layout-navigation" class="group navbar navbar-default" role="navigation">
              @Zone(Model.Navigation)
            </div>
          }
      
          ...
      </div>
      

      Now, if Orchard renders the menu itself, it uses the Menu.cshtml shape template, thus the next step will be to provide a shape alternate for Menu.cshtml.

    4. Creating a shape alternate for the menu in your child theme

      Go to you child theme folder and add a file Views\Menu.cshtml and start rendering the menu there, for example

      <ul class="nav navbar-nav">
        @DisplayChildren(Model)
      </ul>
      

      The @DisplayChildren(Model) call will start rendering the menu items via the MenuItem.cshtml shape template, thus the next step will be to provide a shape alternate for MenuItem.cshtml.

    5. Creating a shape alternate for menu items in your child theme

      Go to you child theme folder and add a file Views\MenuItem.cshtml and start rendering the menu items. Here is the content of my MenuItem.cshtml file, which renders the menu items as <li> structure according to the bootstrap specs:

      @*
        this shape alternate is displayed when a <li> element is rendered
        whereas the following code is based on Orchard.Core\Shapes\Views\Menu.cshtml
      *@
      
      @{
          // odd formatting in this file is to cause more attractive results in the output.
          var items = Enumerable.Cast<dynamic>((System.Collections.IEnumerable)Model);
      }
      @{
          if (!HasText(Model.Text)) {
              @DisplayChildren(Model)
          }
          else {
              if ((bool) Model.Selected) {
                  Model.Classes.Add("current");
              }
      
              if (items.Any()) {
                  Model.Classes.Add("dropdown");
              }
      
              @* morphing the shape to keep Model untouched*@
              Model.Metadata.Alternates.Clear();
              Model.Metadata.Type = "MenuItemLink";
      
              @* render the menu item only if it has some content *@
              var renderedMenuItemLink = Display(Model);
              if (HasText(renderedMenuItemLink)) {
                  var tag = Tag(Model, "li");
                  @tag.StartElement
                  @renderedMenuItemLink
      
                  if (items.Any()) {
                      <ul class="dropdown-menu">
                          @DisplayChildren(Model)
                      </ul>
                  }
      
                  @tag.EndElement
              }
          }
      }
      

      You can also provide alternates to override specific menu item types like the Custom Link. The file would then be MenuItemLink.cshtml with a content like

      @* 
        this shape alternate is displayed when menu link is _not_ of type "Content Menu Item" otherwise MenuItemLink-ContentMenuItem.cshtml is used
        whereas the following code is based on Orchard.Core\Shapes\Views\MenuItemLink.cshtml
      *@
      <a href="@Model.Href" @if (Model.Item.Items.Length > 0) { <text>class="dropdown-toggle" data-toggle="dropdown"</text> }>@Model.Text</a>
      

      As you can see, a lot of work but pretty flexible.