Eric Bishard
Eric Bishard

Reputation: 5341

Create ViewModel for Navigation

I have an MVC 4 application with several views. I.e. Products, Recipes, Distrubutors & Stores.

Each view is based around a model.

Let's keep it simple and say that all my controllers pass a similar view-model that looks something like my Product action:

public ActionResult Index()
{
    return View(db.Ingredients.ToList());
}

Ok so this is fine, no problems. But now that all of my pages work I want to change my navigation (which has dropdowns for each view) to load the items in that model.

So I would have a navigation with 4 Buttons (Products, Recipes, Distrubutors & Stores).

When you roll over each button (let's say we roll over the products button) then a dropdown would have the Products listed.

To do this I need to create some type of ViewModel that has all 4 of those models combined. Obviously I can't just cut out a PartialView for each navigation element and use

@model IEnumerable<GranSabanaUS.Models.Products>

And repeat out the Products for that dropdown, because then that navigation would only work in the Product View and nowhere else.

(After the solution) AND YES ROWAN You are correct in the type of nav I am creating, see here: Navigation I am creating

Upvotes: 4

Views: 4887

Answers (2)

Scottie
Scottie

Reputation: 11308

public class MenuContents
{
    public IEnumerable<Products> AllProducts { get; set; }
    public IEnumerable<Recepies> AllRecepies { get; set; }
    public IEnumerable<Distributors> AllDistributors { get; set; }
    public IEnumerable<Stores> AllStores { get; set; }

    private XXXDb db = new XXXUSDb();

    public void PopulateModel()
    {
        AllProducts = db.Products.ToList();
        AllRecepies = db.Recepies.ToList();
        AllDistributors = db.Distributors.ToList();
        AllStores = db.Stores.ToList();
    }
}

Then in your controller

public ActionResult PartialWhatever()
{
    MenuContents model = new MenuContents();
    model.PopulateModel();

    return PartialView("PartialViewName", model);
}

Then in your partial view

@Model MenuContents

... do whatever here

Upvotes: 1

Rowan Freeman
Rowan Freeman

Reputation: 16378

Introduction

I'm going to be making a few assumptions because I don't have all the information.

I suspect you want to create something like this:

Dropdown menu

Separating views

When you run into the issue of "How do I put everything into a single controller/viewmodel" it's possible that it's doing too much and needs to be divided up.

Don't treat your a final page as one big view - divide the views up into smaller views so they are doing 'one thing'.

For example, the navigation is just one part of your layout. You could go even further to say that each dropdown menu is a single view that are part of the navigation, and so on.

Navigation overview

Suppose you have a _Layout.cshtml that looks like this:

<body>
    <div class="navbar">
        <ul class="nav">
            <li><a href="#">Products</a></li>
            <li><a href="#">Recipes</a></li>
        </ul>
    </div>

    @RenderBody()
</body>

As you can see we have a simple navigation system and then the main body is rendered. The problem that we face is: How do we extract this navigation out and give it the models it needs to render everything?

Extracting the navigation

Let's extract the navigation into it's own view. Grab the navigation HTML and paste it into a new view called __Navigation.cshtml_ and put it under ~/Views/Partials.

_Navigation.cshtml

<div class="navbar">
    <ul class="nav">
        <li><a href="#">Products</a></li>
        <li><a href="#">Recipes</a></li>
    </ul>
</div>

Create a new controller called PartialsController. Create a new action to call our navigation.

PartialsController.cs

[ChildActionOnly]
public class PartialsController : Controller
{
    public ActionResult Navigation()
    {
        return PartialView("_Navigation");
    }

}

Update our Layout to call the navigation.

_Layout.cshtml

<body>
    @Html.Action("Navigation", "Partials")

    @RenderBody()
</body>

Now our navigation is separated out into its own partial view. It's more independent and modular and now it's much easier to give it model data to work with.

Injecting model data

Suppose we have a few models such as the ones you mentioned.

public class Product { //... }
public class Recipe { //... }

Let's create a view-model:

NavigationViewModel.cs

public class NavigationViewModel
{
    public IEnumerable<Product> Products { get; set; }
    public IEnumerable<Recipe> Recipes { get; set; }
}

Let's fix up our action:

PartialsController.cs

public ActionResult Navigation()
{
    NavigationViewModel viewModel;

    viewModel = new NavigationViewModel();
    viewModel.Products = db.Products;
    viewModel.Recipes = db.Recipes;

    return PartialView("_Navigation", viewModel);
}

Finally, update our view:

_Navigation.cshtml

@model NavigationViewModel

<div class="navbar">
    <ul class="nav">

        @foreach (Product product in Model.Products)
        {
            @<li>product.Name</li>
        }

        @foreach (Recipe recipe in Model.Recipes)
        {
            @<li>recipe.Name</li>
        }
    </ul>
</div>

Upvotes: 13

Related Questions