1

There are a lot of similar questions, but no good answers. This is my attempt:

I want to use a partial view in _Layout.cshtml for a menu that will appear on every page.

My _Layout.cshtml:

 <div id="navbar" class="navbar-collapse">
    <partial name="_TopLevelMenu" model="Model.MainMenuModel" />
 </div>

My _TopLevelMenu.cshtml partial:

@model List<Data.Models.MainMenuModel>

<ul id="ulMainMenu" class="nav navbar-nav">
    
@foreach (var item in Model){
    <li @item.NavId>
      <a href="@item.NavigationUrl">@item.DisplayName</a>
    </li>
}
</ul>

My error:

RuntimeBinderException: 'MyApp.Pages.IndexModel' does not contain a definition for 'MainMenuModel'

Things I've tried:

"Pass the model" - I get the same error when I do this, and besides, Visual Studio strongly advises me to use the Partial tag helper instead.

Use the partial tag without a named model, which gives me this error: InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'MyApp.Pages.IndexModel', but this ViewDataDictionary instance requires a model item of type 'System.Collections.Generic.List1[MyApp.Data.Models.MainMenuModel]'.`

I tried it with just MainMenuModel but Visual Studio tells me it's a type and I can't use a type for my model.

I looked at this website for guidance, and it unhelpfully uses some of my key search words ("menu", "layout") but doesn't use the _Layout.cshtml page as part of the example.

If I do <partial name="_TopLevelMenu" model=new MainMenuModel()" /> I don't get errors but I also don't get my data from the partial to populate.

What must I do to properly have a partial be the menu items on all pages?

NovaDev
  • 2,737
  • 5
  • 29
  • 43

2 Answers2

2

Your error message shows that you need to define a MainMenuModel property in IndexModel, like this:

IndexModel:

public class IndexModel : PageModel
{
    public List<MainMenuModel> MainMenuModel { get; set; }

    public IActionResult OnGet()
    {
        MainMenuModel = new List<MainMenuModel>()
        {
            new MainMenuModel() { NavId = 1,NavigationUrl = "Url1", DisplayName = "Name1"},
            new MainMenuModel() { NavId = 2,NavigationUrl = "Url2", DisplayName = "Name2"},
            new MainMenuModel() { NavId = 3,NavigationUrl = "Url3", DisplayName = "Name3"},
        };
        return Page();
    }
}

_TopLevelMenu.cshtml:

@model List<Data.Models.MainMenuModel>

<ul id="ulMainMenu" class="nav navbar-nav">

    @foreach (var item in Model)
    {
        <li @item.NavId>
            <a href="@item.NavigationUrl">@item.DisplayName</a>
        </li>
    }
</ul>

_Layout.cshtml:

<div id="navbar" class="navbar-collapse">
    <partial name="_TopLevelMenu" model="Model.MainMenuModel" />
</div>

Test Result:

enter image description here

Please note that if you add this partial view in Layout for all pages, you should define the MainMenuModel property in each page. For example, in PrivacyModel:

public class PrivacyModel : PageModel
{
    public List<MainMenuModel> MainMenuModel { get; set; }

    public IActionResult OnGet()
    {
        MainMenuModel = new List<MainMenuModel>()
        {
            new MainMenuModel() { NavId = 1,NavigationUrl = "Url1", DisplayName = "Name1"},
            new MainMenuModel() { NavId = 2,NavigationUrl = "Url2", DisplayName = "Name2"},
            new MainMenuModel() { NavId = 3,NavigationUrl = "Url3", DisplayName = "Name3"},
        };
        return Page();
    }
}

Test Result:

enter image description here

Chen
  • 4,499
  • 1
  • 2
  • 9
1

Chen has provided a good explanation of the cause of your error message and a resolution, and also notes that when passing models to partials in a layout, you must define the model in every page that references the layout. You could do this by implementing a base class for your pages and define the model there, and then derive all your pages from the base class, but in practice, that can become very cumbersome to manage.

I recommend using View Components for segments of UI that go into layouts, where the UI segment depends on server-side logic for generating the content:

They are a little more complicated to set up than a partial, but they are responsible for generating their own data.

Mike Brind
  • 28,238
  • 6
  • 56
  • 88
  • I was going to post a less good answer than this as the answer - the answer is -"you can't, use view components instead - though you technically can use partials on the layout page, you have to include the model everywhere" which is less than optimal. – NovaDev Jun 23 '23 at 12:18
  • Of course, yours is the best answer :-) – NovaDev Jun 23 '23 at 12:24