'Using Asp.Net Core Viewbag

Layout.cshtml

@{ 
    var menus = (IEnumerable<WebApplication.Models.TopMenu>)ViewBag.menus;
    
}
 
<div class="col-md-8">
    <!-- Menu Partial -->
    @await Html.PartialAsync("~/Views/MainPage/TopMenu.cshtml", menus);
</div>

MainPageController.cs:

public IActionResult Mainpage()
{ 
    ViewBag.menus = db.TopMenu.Where(a => a.Active).OrderBy(a => a.Order);
    return View();
}

TopMenu.cshtml:

@{ 
    Layout = null;
}
       
@model IEnumerable<WebApplication.Models.TopMenu>
<ul id="nav" class="sf-menu" >
    <li><a href="#">MainPage</a></li>
    @{ 
        foreach (var item in Model)
        {
            <li>
                @if (item.ParentId == null && Model.Where(a => a.ParentId == item.Id).Any())
                {
                    <a href="/PageDetail/@[email protected]">@item.Name</a>
                }
                else if (item.ParentId == null && Model.Where(a => a.ParentId != item.Id).Any())
                {
                        var link = "";
                        if (!String.IsNullOrEmpty(item.Link)) { link = item.Link; }
                        else { link = "/PageDetail" + item.Name + "-" + item.Id; }
                        <a href="@link">@item.Name</a>

                }
            <ul>
    @foreach (var item1 in Model.Where(a => a.ParentId == item.Id))
    {
        <li><a style="text-transform:none;" href="/PageDetail/@[email protected]" >@item.Name</a></li>

    }
            </ul>               
        </li>
    }
}
</ul>

I'm trying to show the top menu names from using database . Doesn't work and doesn't give an error.I tried to migrate with view bag and I am using ms sql. What should i do to get the menu names?

THE OTHER WAY

MainPageController.cs

public IActionResult MainPage()
{        
    //     ViewBag.cars = db.Cars.OrderByDescending(a => a.date).Take(6);//view bag works for cars         
    var cars = db.Cars.OrderByDescending(a => a.date).Take(11);//it works this way too
            
    //ViewBag.menus = db.TopMenu.Where(a => a.Active).OrderBy(a => a.Order).ToList();        
        
    return View(cars);
}

public IActionResult Menu() {
    var menus = db.TopMenu.OrderByDescending(a => a.Active).OrderBy(a => a.Order);

    return View(menus);
}

note: cars data works fine

_Layout.cshtml:

<div class="col-md-8">
     <!-- Menu Partial -->
     @await Html.PartialAsync("~/Views/MainPage/TopMenu.cshtml");
</div>

TopMenu.cshtml

THE OTHER WAY View COMPONENT

TopMenuViewComponent.cs

   namespace WebApplication.ViewComponents
{
    [ViewComponent(Name = "TopMenu")]
    public class TopMenuViewComponent : ViewComponent
    {
        private readonly WebApplicationContext _context;

        public TopMenuViewComponent(WebApplicationContext context)
        {
            _context = context;
        }
        public async Task<IViewComponentResult> InvokeAsync()
        {

            var menus = await _context.TopMenu.ToListAsync();
            return View("TopMenu", menus);
        }
      
    }
}

Shared/Components/TopMenu/TopMenu.cshtml

@{
    Layout = null;
}

@model IEnumerable<WebApplication.Models.TopMenu>
<ul id="nav" class="sf-menu">
    <li><a href="#">MainPage</a></li>
    @{
        foreach (var item in Model)
        {
            <li>
                @if (item.ParentId == null && Model.Where(a => a.ParentId == item.Id).Any())
                {
                    <a href="/PageDetail/@[email protected]">@item.Name</a>
                }
                else if (item.ParentId == null && Model.Where(a => a.ParentId != item.Id).Any())
                {
                    var link = "";
                    if (!String.IsNullOrEmpty(item.Link)) { link = item.Link; }
                    else { link = "/PageDetail" + item.Name + "-" + item.Id; }
                    <a href="@link">@item.Name</a>

                }
                <ul>
                    @foreach (var item1 in Model.Where(a => a.ParentId == item.Id))
                    {
                        <li><a style="text-transform:none;" href="/PageDetail/@[email protected]">@item.Name</a></li>

                    }
                </ul>
            </li>
        }
    }
</ul>

_Layout.cshtml

  <div class="col-md-8">
                        <!-- Menu Partial -->
                        @await Component.InvokeAsync("TopMenu")
                    </div>

first parent id must be null-->>SOLUTION enter image description here



Solution 1:[1]

1_create _ViewImports.cshtml in Views folder write this line

@addTagHelper *,Microsoft.AspNetCore.Mvc.TagHelpers 

2_use tagHelper in Layout.cshtml

@{
      var menus = (IEnumerable<WebApplication.Models.TopMenu>)ViewBag.menus; 
    }

  <div class="col-md-8">
         <!-- Menu Partial -->
         <partial name="TopMenu.cshtml" model="@menus" />  
  </div>

other

<partial name="~/Views/MainPage/TopMenu.cshtml" model="@menus" />

Solution 2:[2]

It might be because you forgot to add data in a html tag.

<a style="text-transform:none;" href="/PageDetail/@[email protected]" >@item.Title</a>

Solution 3:[3]

I have the same problem as your, rendering a partial view in the layout, if the partial view has a model, in this case, we can use ViewBag to pass the model . But we need to rewrite ViewBag in the corresponding action if we use the Layout page in each page.

Option 1:

In controller:

public IActionResult Index()
        {
           var topMenu = new List<TopMenu>()
         {
        new TopMenu { Name = "Khushbu",Id= 1,Link = "Link1", ParentId = 1 },
        new TopMenu { Name = "Mohan", Id = 2 ,Link = "Link2", ParentId = 2},
        new TopMenu { Name = "John", Id = 3 ,Link = "Link3", ParentId = 3},
        new TopMenu { Name = "Martin", Id= 4,Link = "Link4", ParentId = 4},
        new TopMenu { Name = "Due", Id= 5,Link = "Link5", ParentId = 5 }
        };   // you also can get menus from database
            
            ViewBag.menus = topMenu;
            return View();
        }
public IActionResult Privacy()
        {
            var topMenu = new List<TopMenu>()
         {
        new TopMenu { Name = "Khushbu",Id= 1,Link = "Link1", ParentId = 1 },
        new TopMenu { Name = "Mohan", Id = 2 ,Link = "Link2", ParentId = 2},
        new TopMenu { Name = "John", Id = 3 ,Link = "Link3", ParentId = 3},
        new TopMenu { Name = "Martin", Id= 4,Link = "Link4", ParentId = 4},
        new TopMenu { Name = "Due", Id= 5,Link = "Link5", ParentId = 5 }
        };
            ViewBag.menus = topMenu;
            return View();
        }

My TopMenu.cshtml is in shared folder, in layout add this :

 @{
        var menus = (IEnumerable<TopMenu>)ViewBag.menus;

    }
     <div class="col-md-8">
 <!-- MenĂ¼ Partial -->
   @await Html.PartialAsync("~/Views/Shared/TopMenu.cshtml",menus);

result:

enter image description here

So, in this case, I prefer to use ViewComponent. Read ViewComponent to know more.

Option2:

1.Create new folder named ViewComponents. In this folder, create new class named TopMenuViewComponent.cs as below:

   [ViewComponent(Name = "TopMenu")]
    public class TopMenuViewComponent : ViewComponent
    {
        public async Task<IViewComponentResult> InvokeAsync()
        {
            List<TopMenu> menus = new List<TopMenu>()       {
        new TopMenu { Name = "Khushbu",Id= 1,Link = "Link1", ParentId = 1 },
        new TopMenu { Name = "Mohan", Id = 2 ,Link = "Link2", ParentId = 2},
        new TopMenu { Name = "John", Id = 3 ,Link = "Link3", ParentId = 3},
        new TopMenu { Name = "Martin", Id= 4,Link = "Link4", ParentId = 4},
        new TopMenu { Name = "Due", Id= 5,Link = "Link5", ParentId = 5 }
        };// you can also get from database
            return View("TopMenu", menus);
        }
    }

2.In Views folder, create new folders with path Views\Shared\Components\TopMenu. In TopMenu folder, add TopMenu.cshtml

3.In layout, add below code

@await Component.InvokeAsync("TopMenu")

Result:

enter image description here

Update

[ViewComponent(Name = "TopMenu")]
    public class TopMenuViewComponent : ViewComponent
    {
        private readonly ViewComponentMVCContext _context;

        public TopMenuViewComponent(ViewComponentMVCContext context)
        {
            _context = context;
        }
        public async Task<IViewComponentResult> InvokeAsync()
        {
            
            var menus = await _context.TopMenu.ToListAsync();
            return View("TopMenu", menus);
        }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 h.abdizadeh
Solution 2 Cemil Arancak
Solution 3