Need help in Razor Pages in ASP.NET Core ( Loop )

Jerry Lipan 916 Reputation points
2022-06-18T23:51:02.667+00:00

Hi. I've Static Menu as following. I can do in html

  1. Parent Menu
  2. Child Menu

212726-20220619-070543.gif

<li class="nav-item">  
                <a class="nav-link " asp-controller="marketing" asp-action="index">  
                    <i class="bi bi-grid"></i>  
                    <span>Dashboard</span>  
                </a>  
            </li>  
  
            <li class="nav-heading"><hr /></li>  
  
            <li class="nav-item">  
                <a class="nav-link collapsed" data-bs-target="#components-nav2" data-bs-toggle="collapse" href="#">  
                    <i class="bi bi-menu-button-wide"></i><span>Sales Contract Setup</span><i class="bi bi-chevron-down ms-auto"></i>  
                </a>  
                <ul id="components-nav2" class="nav-content collapse " data-bs-parent="#sidebar-nav">  
                    <li>  
                        <a asp-controller="Marketing" asp-action="EstateCommodity">  
                            <i class="bi bi-circle"></i><span>Estates Commodity</span>  
                        </a>  
                    </li>  
                </ul>  
            </li>  
  
            <li class="nav-heading"><hr /></li>  
  
            <li class="nav-item">  
                <a class="nav-link collapsed" data-bs-target="#components-nav" data-bs-toggle="collapse" href="#">  
                    <i class="bi bi-menu-button-wide"></i><span>Daily Data Entry</span><i class="bi bi-chevron-down ms-auto"></i>  
                </a>  
                <ul id="components-nav" class="nav-content collapse " data-bs-parent="#sidebar-nav">  
                    <li>  
                        <a asp-controller="marketing" asp-action="DailySalesTransaction">  
                            <i class="bi bi-circle"></i><span>Daily Sales Transaction</span>  
                        </a>  
                    </li>  
                    <li>  
                        <a href="/dashboard/PostingToAR">  
                            <i class="bi bi-circle"></i><span>Posting To Account Receivable</span>  
                        </a>  
                    </li>  
                    <li>  
                        <a href="/dashboard/PaymentAndContra">  
                            <i class="bi bi-circle"></i><span>Payment And Contra</span>  
                        </a>  
                    </li>  
                </ul>  
            </li>  
  
            <li class="nav-heading"><hr /></li>  

Next level, I want to develop Dynamic Menu with List<T>. Below is the Model

 public class Menu  
    {  
        [Key]  
        [Required]  
        public int Id { get; set; }  
         
        public Nullable<int> MenuId { get; set; }  
        public string ControllerName { get; set; }  
        public string ActionName { get; set; }  
        public Nullable<int> ParentId { get; set; }  
        public string ParentName { get; set; }  
  
        [Required]  
        public string MenuName { get; set; }        
        public Nullable<int> MenuOrder { get; set; }  
  
        [Required]  
        public string MenuIcon { get; set; }  
    }  

So far, this is my achievement. I can display

  1. Parent Menu
  2. Child Menu

Unfortunately, I did not have enough knowledge using Razor in ASP.NET Core to writing

  1. Dynamic href
  2. Dynamic asp-controller tag
  3. Dynamic asp-action tag

212636-20220619-072839.gif

I can generate data in List<T> as following,

212686-001.png

212693-002.png

212678-003.png

This is my Razor so far,

@model List<IPMIS.ViewModels.Menu>  
  
@{  
    int items = Model.Count();  
    int counter = 0;  
  
}  
  
  
@if (Model.Count > 0)  
{  
    @for (int i = 0; i < items; i++)  
    {  
  
  
  
        @if (Model[i].ParentId == null)  
        {  
            
            <li class="nav-item">  
                <a   
                   class="nav-link @(Model[i].ActionName != null ? "":"collapsed")"  
  
                   data-bs-target="#components-@Model[i].Id"  
                   data-bs-toggle="@(Model[i].ActionName != null ? "":"collapsed")"  
  
                  
                   href="/@Model[i].ControllerName/@Model[i].ActionName/@ViewBag.moduleId"  
                     
                   >  
                   
                    <i class="@Model[i].MenuIcon"></i>  
                    <span>@Model[i].MenuName</span>  
                    <i class="bi bi-chevron-down ms-auto"></i>  
                </a>  
                @for (int j = 0; j < items; j++)  
                {  
                    <ul id="components-@Model[i].Id" class="nav-content collapse " data-bs-parent="#sidebar-nav">  
                        @if (Model[i].MenuId == Model[j].ParentId)  
                        {  
  
                            <li>  
                                <a asp-controller="@Model[j].ControllerName" asp-action="@Model[j].ActionName">  
                                    <i class="@Model[j].MenuIcon"></i>  
                                    <span>@Model[j].MenuName</span>  
                                </a>  
                            </li>  
  
                        }  
                    </ul>  
                }  
            </li>  
        }  
  
  
  
  
        @*<li class="nav-heading"><hr /></li>*@  
  
        counter++;  
  
    }  
                 
  
}  

I'm stuck. Please help

ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,199 questions
{count} votes

Accepted answer
  1. Zhi Lv - MSFT 32,016 Reputation points Microsoft Vendor
    2022-06-24T02:31:50.877+00:00

    Hi @Jerry Lipan ,

         <a class="nav-link @(Model[i].ActionName != null ? "":"collapsed")"    
    
             data-bs-target="#components-[@](/users/na/?userId=0f55de7e-bffd-0003-0000-000000000000)[i].Id"  
             data-bs-toggle="@(Model[i].ActionName != null ? "":"collapsed")"  
             href="/[@](/users/na/?userId=0f55de7e-bffd-0003-0000-000000000000)[i].ControllerName/[@](/users/na/?userId=0f55de7e-bffd-0003-0000-000000000000)[i].ActionName/@ViewBag.moduleId">  
     
              <i class="@Model[i].MenuIcon"></i>  
              <span>@Model[i].MenuName</span>  
              <i class="bi bi-chevron-down ms-auto"></i>  
          </a>  
    

    The issue relates to the above first level menu <a> tag. If you use F12 developer tools to check the Html elements and CSS style, you can see it not rendered as expect.

    To dynamically add the attributes based on the model's property value, you could build an html string for the <a> tag and its attributes, then use Html.Raw() method to render the <a> tag.

    After modifying, the Default.cshtml page is below:

    @model List<WebApplication1.ViewModels.Menu>  
      
    @{  
        //int items = Model.Count();  
        int navcounter = 0; //to set the data-bs-target attribute.  
    }   
    @if (Model.Count > 0)  
    {  
        //use LINQ where clause to find all first level menu (parentid is null).  
        var firstlevelmenu = Model.Where(c => c.ParentId == null).ToList();  
        if (firstlevelmenu.Count > 0)  
        {  
            //use for statement to loop through the first level menu.  
            @for (int i = 0; i < firstlevelmenu.Count; i++)  
            {  
                //check whether current menu has the child menu.  
                var secondlevelmenu = Model.Where(c => c.ParentId != null && c.ParentId == firstlevelmenu[i].MenuId).ToList();  
                //define a string variable to builder the <a> tag.  
                var newatag = "<a class = 'nav-link ";  
                //add the href attribute.  
                if(firstlevelmenu[i].ControllerName !=null && firstlevelmenu[i].ActionName != null)  
                {  
                    newatag += "collapsed'";  
                    var hrefvalue = String.Format("href='/{0}/{1}'", firstlevelmenu[i].ControllerName, firstlevelmenu[i].ActionName);  
                    newatag += hrefvalue;  
                    if (secondlevelmenu.Count > 0)  
                    {  
                        newatag += "data-bs-target='#components-nav_" + navcounter + "' data-bs-toggle='collapse'";  
                    }     
                }  
                else  
                {  
                    newatag += "'";  
                    newatag += "href='#'";  
                }  
                newatag += ">";  
                //add the menu icon.  
                newatag += "<i class='" + firstlevelmenu[i].MenuIcon + "'></i>";  
                //add the menu name  
                newatag += "<span>" + firstlevelmenu[i].MenuName + "</span>";  
                newatag += "<i class='bi bi-chevron-down ms-auto'></i>";  
                newatag += "</a>";  
      
                <li class="nav-item">   
                    @Html.Raw(newatag)          @*Use Html.Raw method to render the <a> tag.*@  
      
                    <!-- Add the second level menu-->  
                    @if (secondlevelmenu.Count > 0)   
                    {  
                        <ul id="components-nav_@navcounter" class="nav-content collapse " data-bs-parent="#sidebar-nav">   
                            @for (int j = 0; j < secondlevelmenu.Count; j++)  
                            {  
                                <li>  
                                    <a asp-controller="@secondlevelmenu[j].ControllerName" asp-action="@secondlevelmenu[j].ActionName">  
                                        <i class="@secondlevelmenu[j].MenuIcon"></i>  
                                        <span>@secondlevelmenu[j].MenuName</span>  
                                    </a>  
                                </li>  
                            }  
                        </ul>  
                        navcounter++;  
                    }   
                </li>    
                <li class="nav-heading"><hr /></li>  
            }   
        }  
    }  
    

    Then, the result is like this:

    214544-1.gif


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    Best regards,
    Dillion

    0 comments No comments

1 additional answer

Sort by: Most helpful
  1. Jerry Lipan 916 Reputation points
    2022-06-22T20:23:07.877+00:00

    Hello @Zhi Lv - MSFT ,

    I compile the code for you. Hope you can help me. Here the GitHub, WebApplication1

    Static Menu as following,

    213940-20220623-030835.gif

    Here the html,

       <li class="nav-item">  
                    <a class="nav-link " asp-controller="marketing" asp-action="index">  
                        <i class="bi bi-grid"></i>  
                        <span>Dashboard</span>  
                    </a>  
                </li>  
      
                <li class="nav-heading"><hr /></li>  
      
                <li class="nav-item">  
                    <a class="nav-link collapsed" data-bs-target="#components-nav2" data-bs-toggle="collapse" href="#">  
                        <i class="bi bi-menu-button-wide"></i><span>Sales Contract Setup</span><i class="bi bi-chevron-down ms-auto"></i>  
                    </a>  
                    <ul id="components-nav2" class="nav-content collapse " data-bs-parent="#sidebar-nav">  
                        <li>  
                            <a asp-controller="Marketing" asp-action="EstateCommodity">  
                                <i class="bi bi-circle"></i><span>Estates Commodity</span>  
                            </a>  
                        </li>  
                    </ul>  
                </li>  
      
                <li class="nav-heading"><hr /></li>  
      
                <li class="nav-item">  
                    <a class="nav-link collapsed" data-bs-target="#components-nav" data-bs-toggle="collapse" href="#">  
                        <i class="bi bi-menu-button-wide"></i><span>Daily Data Entry</span><i class="bi bi-chevron-down ms-auto"></i>  
                    </a>  
                    <ul id="components-nav" class="nav-content collapse " data-bs-parent="#sidebar-nav">  
                        <li>  
                            <a asp-controller="marketing" asp-action="DailySalesTransaction">  
                                <i class="bi bi-circle"></i><span>Daily Sales Transaction</span>  
                            </a>  
                        </li>  
                        <li>  
                            <a href="/dashboard/PostingToAR">  
                                <i class="bi bi-circle"></i><span>Posting To Account Receivable</span>  
                            </a>  
                        </li>  
                        <li>  
                            <a href="/dashboard/PaymentAndContra">  
                                <i class="bi bi-circle"></i><span>Payment And Contra</span>  
                            </a>  
                        </li>  
                    </ul>  
                </li>  
      
                <li class="nav-heading"><hr /></li>  
      
                <li class="nav-item">  
                    <a class="nav-link collapsed" data-bs-target="#forms-nav" data-bs-toggle="collapse" href="#">  
                        <i class="bi bi-journal-text"></i><span>Inquiry</span><i class="bi bi-chevron-down ms-auto"></i>  
                    </a>  
                    <ul id="forms-nav" class="nav-content collapse " data-bs-parent="#sidebar-nav">  
                        <li>  
                            <a href="/dashboard/Debtor">  
                                <i class="bi bi-circle"></i><span>Debtor</span>  
                            </a>  
                        </li>  
                        <li>  
                            <a href="/dashboard/Contract">  
                                <i class="bi bi-circle"></i><span>Contract</span>  
                            </a>  
                        </li>  
                        <li>  
                            <a href="/dashboard/TotalSalesForTheYear">  
                                <i class="bi bi-circle"></i><span>Total Sales For The Year</span>  
                            </a>  
                        </li>  
                        <li>  
                            <a href="/dashboard/Payment">  
                                <i class="bi bi-circle"></i><span>Payment</span>  
                            </a>  
                        </li>  
                    </ul>  
                </li>  
      
                <li class="nav-heading"><hr /></li>  
      
                <li class="nav-item">  
                    <a class="nav-link collapsed" data-bs-target="#tables-nav" data-bs-toggle="collapse" href="#">  
                        <i class="bi bi-layout-text-window-reverse"></i><span>Report</span><i class="bi bi-chevron-down ms-auto"></i>  
                    </a>  
                    <ul id="tables-nav" class="nav-content collapse " data-bs-parent="#sidebar-nav">  
                        <li>  
                            <a href="/dashboard/detailedcontractlisting">  
                                <i class="bi bi-circle"></i><span>Detailed Contract Listing</span>  
                            </a>  
                        </li>  
                        <li>  
                            <a href="/dashboard/buyercontractsummary">  
                                <i class="bi bi-circle"></i><span>Buyer's Contract Summary</span>  
                            </a>  
                        </li>  
                        <li>  
                            <a href="/dashboard/buyeroutstandingbalance">  
                                <i class="bi bi-circle"></i><span>Buyer's Outstanding Balance</span>  
                            </a>  
                        </li>  
                        <li>  
                            <a href="/dashboard/salesoftheyear">  
                                <i class="bi bi-circle"></i><span>Sales Of The Year</span>  
                            </a>  
                        </li>  
                        <li>  
                            <a href="/dashboard/chequelisting">  
                                <i class="bi bi-circle"></i><span>Cheque Listing</span>  
                            </a>  
                        </li>  
                        <li>  
                            <a href="/dashboard/contractlistingbycommodity">  
                                <i class="bi bi-circle"></i><span>Contract Listing By Commodity</span>  
                            </a>  
                        </li>  
                        <li>  
                            <a href="/dashboard/freshfruitbunch">  
                                <i class="bi bi-circle"></i><span>Fresh Fruit Bunch</span>  
                            </a>  
                        </li>  
                    </ul>  
                </li>  
      
                <li class="nav-heading"><hr /></li>  
      
                <li class="nav-item">  
                    <a class="nav-link collapsed" data-bs-target="#tables-master" data-bs-toggle="collapse" href="#">  
                        <i class="bi bi-layout-text-window-reverse"></i><span>Master File Setup</span><i class="bi bi-chevron-down ms-auto"></i>  
                    </a>  
                    <ul id="tables-master" class="nav-content collapse " data-bs-parent="#sidebar-nav">  
                        <li>  
                            <a href="/dashboard/AccountType">  
                                <i class="bi bi-circle"></i><span>Account Type</span>  
                            </a>  
                        </li>  
                        <li>  
                            <a href="/dashboard/Company">  
                                <i class="bi bi-circle"></i><span>Company</span>  
                            </a>  
                        </li>  
                        <li>  
                            <a href="/dashboard/EstateProfile">  
                                <i class="bi bi-circle"></i><span>Estate Profile</span>  
                            </a>  
                        </li>  
                        <li>  
                            <a href="/dashboard/Commodity">  
                                <i class="bi bi-circle"></i><span>Commodity</span>  
                            </a>  
                        </li>  
                        <li>  
                            <a href="/dashboard/ReferenceNumber">  
                                <i class="bi bi-circle"></i><span>Reference Number</span>  
                            </a>  
                        </li>  
                    </ul>  
                </li>  
    

    I program this static html menu using Components. The objective to generate dynamic menu based on data. Below is my Model

    Menu.cs

    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Threading.Tasks;  
    using System.ComponentModel.DataAnnotations;  
    using System.ComponentModel.DataAnnotations.Schema;  
      
    namespace WebApplication1.ViewModels  
    {  
        public class Menu  
        {  
            [Key]  
            [Required]  
            public int Id { get; set; }  
      
            public Nullable<int> MenuId { get; set; }  
            public string ControllerName { get; set; }  
            public string ActionName { get; set; }  
            public Nullable<int> ParentId { get; set; }  
            public string ParentName { get; set; }  
      
            [Required]  
            public string MenuName { get; set; }  
            public Nullable<int> MenuOrder { get; set; }  
      
            [Required]  
            public string MenuIcon { get; set; }  
      
        }  
    }  
    

    Let say, I've data in List<T> as following,

     List<Menu> menus = new List<Menu>  
                    {  
                        new Menu { ActionName = "index", ControllerName = "marketing", Id = 1, MenuIcon = "bi bi-grid",   
                        MenuId = 1, MenuName = "Dashboard", MenuOrder = null, ParentId = null, ParentName = null },  
                        new Menu { ActionName = "", ControllerName = "", Id = 3, MenuIcon = "bi bi-menu-button-wide",   
                        MenuId = 2, MenuName = "Sales Contract Setup", MenuOrder = null, ParentId = null, ParentName = null },  
                        new Menu { ActionName = "EstateCommodity", ControllerName = "marketing", Id = 2, MenuIcon = "bi bi-circle",   
                        MenuId = 3, MenuName = "Estate Commodity", MenuOrder = 1, ParentId = 2, ParentName = null }  
                    };  
    

    You can see,

    1. Dashboard is a Parent Menu without Child Menu. So, user can click and page will be redirected based on ControllerName and ActionName
    2. Sales Contract Setup is a Parent Menu. But having a Child Menu. You can see - ControllerName = "" & ActionName = "". User can click. But not redirect. It click then expand the Child Menu
    3. Estate Commodity is a Child Menu. Parent Menu is Sales Contract Setup ( ParentId = 2 ). User can click and page will be redirected based on ControllerName and ActionName

    The challenge is

    1. Populate menu based on Parent Menu and Child Menu
    2. Click to redirect or Click the expand the Child Menu

    So far, I've this

    ViewComponents/DynamicMenuViewComponent.cs

    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Threading.Tasks;  
    using Microsoft.AspNetCore.Mvc;  
    using WebApplication1.ViewModels;  
      
    namespace WebApplication1.ViewComponents  
    {  
        public class DynamicMenuViewComponent : ViewComponent  
        {  
            public IViewComponentResult Invoke()  
            {  
                List<Menu> menus = new List<Menu>  
                    {  
                        new Menu { ActionName = "index", ControllerName = "marketing", Id = 1, MenuIcon = "bi bi-grid",  
                        MenuId = 1, MenuName = "Dashboard", MenuOrder = null, ParentId = null, ParentName = null },  
                        new Menu { ActionName = "", ControllerName = "", Id = 3, MenuIcon = "bi bi-menu-button-wide",  
                        MenuId = 2, MenuName = "Sales Contract Setup", MenuOrder = null, ParentId = null, ParentName = null },  
                        new Menu { ActionName = "EstateCommodity", ControllerName = "marketing", Id = 2, MenuIcon = "bi bi-circle",  
                        MenuId = 3, MenuName = "Estate Commodity", MenuOrder = 1, ParentId = 2, ParentName = null }  
                    };  
      
             
      
                return View(menus);  
            }  
      
        }  
    }  
    

    Views/Shared/Components/DynamicMenu/Default.cshtml

    @model List<WebApplication1.ViewModels.Menu>  
      
    @{  
        int items = Model.Count();  
        int counter = 0;  
      
    }  
      
      
    @if (Model.Count > 0)  
    {  
        @for (int i = 0; i < items; i++)  
        {  
      
      
      
            @if (Model[i].ParentId == null)  
            {  
      
                <li class="nav-item">  
                    <a class="nav-link @(Model[i].ActionName != null ? "":"collapsed")"  
                       data-bs-target="#components-@Model[i].Id"  
                       data-bs-toggle="@(Model[i].ActionName != null ? "":"collapsed")"  
                       href="/@Model[i].ControllerName/@Model[i].ActionName/@ViewBag.moduleId">  
      
                        <i class="@Model[i].MenuIcon"></i>  
                        <span>@Model[i].MenuName</span>  
                        <i class="bi bi-chevron-down ms-auto"></i>  
                    </a>  
                    @for (int j = 0; j < items; j++)  
                    {  
                        <ul id="components-@Model[i].Id" class="nav-content collapse " data-bs-parent="#sidebar-nav">  
                            @if (Model[i].MenuId == Model[j].ParentId)  
                            {  
      
                                <li>  
                                    <a asp-controller="@Model[j].ControllerName" asp-action="@Model[j].ActionName">  
                                        <i class="@Model[j].MenuIcon"></i>  
                                        <span>@Model[j].MenuName</span>  
                                    </a>  
                                </li>  
      
                            }  
                        </ul>  
                    }  
                </li>  
            }  
      
      
      
      
            @*<li class="nav-heading"><hr /></li>*@  
      
            counter++;  
      
        }  
      
      
    }  
    

    Component

       @await Component.InvokeAsync("DynamicMenu")  
    

    Then, I only got this

    214023-component.gif

    I really need help. Please help

    0 comments No comments