MOHAMED ABUELATTA
MOHAMED ABUELATTA

Reputation: 345

asp.net core pass Model to Layout

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">
                &copy; 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

Answers (2)

MOHAMED ABUELATTA
MOHAMED ABUELATTA

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:

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

Tiny Wang
Tiny Wang

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:

enter image description here

Upvotes: 5

Related Questions