Reputation: 345
I have a layout page with the structure where I use navbar alongside @RenderBody()
.
later I made a method to generate pages and consequently navbar itself. thus, the navbar is not fixed anymore.
Then I made a partial view to loop through navbar items and display them in the same place in the layout. but I don't know how to import a model in the layout.
I'm wondering is there any tag helper that can trigger an action? to return the needed model
my layout looks like this
<html lang="en">
<head>
...
</head>
<body>
<header>
<partial name="_NavBar" model="????" />
</header>
<main class="mb-3">
<div class="container-fluid">
@RenderBody()
</div>
</main>
<footer id="footer" class="border-1 border-top border-primary">
<div class="container-fluid py-4">
<div class="copyright">
© Copyright
</div>
</div>
</footer>
@RenderSection("Scripts", required: false)
</body>
</html>
Any ideas to display this nav partial? or maybe using another tag helper? or any possible solution?
Upvotes: 2
Views: 1655
Reputation: 345
Thank you so much, guys, @Tiny Wang @Roar S., and @Cake or Death
I found good solution for that by adding something called ViewComponent
. And I thought it's better to share it with you all.
It helps to include whatever logic you need in its class and it allows to include a Model in its view.
Here are 3 steps - but first here is the Model
public class Page
{
[Key]
public Guid Id { get; set; }
public int PageOrder { get; set; } = 0;
public string pageName { get; set; }
public string pageDescreption { get; set; }
public string pageHtmlContent { get; set; }
public string NavIcon { get; set; } // svg icon
public bool IsNavBarItem { get; set; }
}
(1) ViewComponent
architecture:
(2) ViewComponent
class:
public class NavBarViewComponent : ViewComponent
{
private readonly ApplicationDbContext context;
public NavBarViewComponent(ApplicationDbContext _context)
{
context = _context;
}
// https://www.youtube.com/watch?v=exokw7WQQ-A
public IViewComponentResult Invoke()
{
// this is how to avoid error of can't convert IQueryable to IEnumerable
IEnumerable<Page> pages = context.pages.Where(P => P.IsNavBarItem == true).AsEnumerable<Page>();
return View(pages);
}
}
(3) ViewComponent
view:
@model IEnumerable<Page>
... whatever
<ul class="navbar-nav">
@foreach (var item in Model)
{
<li class="nav-item">
<a asp-action="PageView" asp-controller="Home" asp-route-PageID="@item.Id" class="nav-link">
@Html.Raw(@item.NavIcon)
@item.pageName
</a>
</li>
}
</ul>
... whatever
And finally to display this nav anywhere just need to include component in the layout
@await Component.InvokeAsync("NavBar")
Upvotes: 0
Reputation: 15906
I agree with @Roars, and pls allow me to show some sample code here.
First, let's create a service which could gather target data for showing nav, e.g. the menus contained in the nav bar are stored in database and we need to query out first. I use mock data instead:
using System.Collections.Generic;
using WebApplication1.Models;
namespace WebApplication1.Services
{
public class ChildService : IChildService
{
public List<Menu> GetNav() {
return new List<Menu> {
new Menu{ MenuId = 1, MenuName = "name1" },
new Menu{ MenuId = 2, MenuName = "name2" }
};
}
}
}
The interface:
using System.Collections.Generic;
using WebApplication1.Models;
namespace WebApplication1.Services
{
public interface IChildService
{
public List<Menu> GetNav();
}
}
Menu Entity:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebApplication1.Models
{
public class Menu
{
public int MenuId { get; set; }
public string MenuName { get; set; }
}
}
Startup.cs -> ConfigureSerivces to config injection:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSingleton<IChildService, ChildService>();
}
After setting the dependency in startup.cs, we can use DI in views. I created a Razor view and inject my query result.
@using WebApplication1.Services
@inject IChildService childService
@{
var menus = childService.GetNav();
}
<h2>hello world</h2>
<div>
@foreach (var item in menus)
{
<div>@item.MenuName</div>
}
</div>
At last, using partial tag to embed this view into other views.
@page
@model WebApplication1.Pages.Childs.IndexModel
@{
}
<h2>index for child</h2>
<a href="/Childs/Edit?id=1">Edit</a>
<partial name="~/Pages/Childs/test.cshtml" />
Here's testing result:
Upvotes: 5