1

I have a .cshtml file that needs to render another .cshtml file (menu). This menu uses a model in order to decide what options to show. When I call from the main .cshtml file to the render the menu, it fails saying that there are invalid types. This is the error message:

System.InvalidOperationException: 'The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[XXXXX.ScheduleItem]', but this dictionary requires a model item of type 'XXXXXX.Models.MenuItemsViewModel'.'

I've tried some of the solutions here: The model item passed into the dictionary is of type .. but this dictionary requires a model item of type

I've tried using

@Html.Partial("Menu", new MenuItemsViewModel())

Which does work but it is creating a new ViewModel, not calling my method for reading it from my database. I have a Index.cshtml file where I am setting the value though, but here all I wanna do is read it from my database. I also tried defining a "GetViewModel" method in the MenuItemsController, create a new instance and calling that method. It works but feels kinda haxy: @Html.Partial("Menu", new MenuItemsBackofficeController().GetViewModel())

@using System.Web.Mvc.Html
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">

    <link rel="stylesheet" type="text/css" href="~/Styles/fonts.css" />
    <link rel="stylesheet" type="text/css" href="~/Styles/style.css" />

    <title>@ViewBag.Title -</title>
</head>
<body>
    @Html.Partial("Menu", new MenuItemsViewModel())
    <header>
        <div class="logo text-center">
            <img src="~/Content/logo.png" />
        </div>
    </header>
    <div>
        @RenderBody()
    </div>
</body>




@model XXXX.Models.MenuItemsViewModel

@if (User.Identity.IsAuthenticated)
{
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark custom-menu">
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span> Meny
        </button>

        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav mr-auto">

                @if (Model.Schedule)
                {
                    <li class="nav-item"><a class="nav-link" href="@Url.Action("Schedule", "Home")">Schema</a></li>
                }
            </ul>
        </div>
    </nav>
    }

To read the model from the database and populate the model. Do I need to create a new Controller for the Menu to achive this?

Mike Brind
  • 28,238
  • 6
  • 56
  • 88
kalle1
  • 11
  • 2
  • In order to get the MenuItemsViewModel populated, you will need to pass it to the main view using viewbag and then in @Html.Partial you can pass the viewbag value. Kindly note, you will have to type cast your viewbag value to MenuItemsViewModel in the main view before passing it in partial view. Hope this makes sense. – prinkpan Dec 20 '18 at 07:37
  • Sorry, do you have an example to show? – kalle1 Dec 20 '18 at 07:43
  • How did you get the view model to your view? did you pass it using a viewBag as @PriyankPanchal mentioned or passed it from the controller. The view model must be made available to the view somehow – Olaitan Adeboye Dec 20 '18 at 07:47

3 Answers3

1

You will need to pass the value of MenuItemsViewModel from controller to this view.

public ActionResult Index()
{
    //Populate your MenuItemsViewModel from database here
    MenuItemsViewModel menuItems = DB.GetMenuItems(); //Assuming GetMenuItems() is method which returns an object of type MenuItemsViewModel from database
    ViewBag.MenuItems = menuItems;
    return View();
}

and then in your view

@using System.Web.Mvc.Html
@{
    var menuItems = (MenuItemsViewModel)ViewBag.MenuItems;
}
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">

    <link rel="stylesheet" type="text/css" href="~/Styles/fonts.css" />
    <link rel="stylesheet" type="text/css" href="~/Styles/style.css" />

    <title>@ViewBag.Title -</title>
</head>
<body>
    @Html.Partial("Menu", menuItems)
    <header>
        <div class="logo text-center">
            <img src="~/Content/logo.png" />
        </div>
    </header>
    <div>
        @RenderBody()
    </div>
</body>
prinkpan
  • 2,117
  • 1
  • 19
  • 32
  • I tried this. It doesn't work because the Index method is located in the MenuItemsController. However this is only configured by the admin. So If the admin haven't rendered the menu setting view, the viewbag won't be set. Can I somehow connect my Controller to this user view as well? The Controller's Index methods would be indentical. – kalle1 Dec 20 '18 at 08:08
  • The Index method mentioned here corresponds to your main view which contains @Html.Partial – prinkpan Dec 20 '18 at 08:15
  • Aha, I see. However I would have to add this to every menu option to get it to work properly. Is that good or bad? – kalle1 Dec 20 '18 at 08:52
  • I basically use two controllers. One for loggin in and another for showing content. I would have to set the viewbag for method in the showing content. – kalle1 Dec 20 '18 at 09:06
1

I would use the @Html.Action for that (I'm updating Priyank Panchal's code) :

public ActionResult GetMenu()
{
    //Populate your MenuItemsViewModel from database here
    MenuItemsViewModel menuItems = DB.GetMenuItems(); //Assuming GetMenuItems() is method which returns an object of type MenuItemsViewModel from database
    return PartialView("Menu", menuItems);
}

And in the view, instead of :

@Html.Partial("Menu", menuItems)

Use the following :

@Html.Action("GetMenu", "YourController")

Hope it helps !

Phteven
  • 47
  • 6
  • Hmm, I tried this but it ends up in a infinite loop instead. RenderBody calls back to @Html.Action again Otherwise, good idea. could have worked. :) – kalle1 Dec 20 '18 at 11:36
  • Could you show us the content of your "Menu" view, please ? – Phteven Dec 20 '18 at 13:41
1

You want to call the controller from your view to (a) get the values from the database and (b) render them in your menu partial view so...

Controller:

public ActionResult Menu() {
    //get stuff from the db 

    return PartialView(menuModel);  //this would be the partial view for your menu
}

The 'main' View:

<head>
...
</head>
<body>
    @{ Html.RenderAction("Menu", "YourController"); }
    <header>
        <div class="logo text-center">
            <img src="~/Content/logo.png" />
        </div>
    </header>
    <div>
        @RenderBody()
    </div>
</body>

EDIT: Adjusted to show within your original code snippet...

scgough
  • 5,099
  • 3
  • 30
  • 48
  • For it to return the PartialView I had to specify both the View name and model, like this: " return PartialView("Menu", menuItemsViewModel); " however this code makesit go into an infinite loop causen it to crash due to too many redirects. – kalle1 Dec 20 '18 at 12:05
  • `Html.RenderAction` won't cause an infinite loop. it will make a separate call to `menu` from the main view so you don't necessarily need to pass around menu models in the ViewBag. You can isolate the menu creation to it's own action in the controller. – scgough Dec 20 '18 at 12:19
  • Yes, I did implement a new action in the controller that RenderAction calls (like above), but when debugging it gets called many many times causing it to crash. – kalle1 Dec 20 '18 at 12:22
  • OK, could you update the question above with your exact code now - we might be able to determine why it's calling lots (as it shouldn't do that) – scgough Dec 20 '18 at 12:40