Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I have two view models in a single controller?

Tags:

c#

asp.net-mvc

I am having trouble understanding if this is possible or not. I have a stored procedures and a view model classes and one controller. Is it possible that I could add another view model class with a different stored procedure to use one controller and display the the information from both stored procedures into one view?

This is my current Model:

public class People
{
    public class Jobs
    {
        public int jobID { get; set; }
        public string jobName { get; set; }
    }

    public class Names
    {
        public int userId{get;set}
        public string userName {get; set;}
    }
}
like image 300
Masriyah Avatar asked Oct 01 '13 00:10

Masriyah


People also ask

Can one controller have multiple models?

In MVC we cannot pass multiple models from a controller to the single view.

Can a controller have multiple views?

Controller is the boss, so a Controller decides which View to be rendered and Views does not / cannot care which Controller requested the View. You can / will absolutely have multiple Views from a Controller.

Can we have 2 models in a single view?

You can use multiple models in a single view by creating a common model for all the models that are to be used in a single view. To achieve this, refer to the following steps. First, create a new model (common for all models) and refer all other models that are to be used in the same view.

Can a view have multiple models?

Yes, you can use Tuple (brings magic in view having multiple model).


1 Answers

Yes, this is totally possible. Simply create a view model that contains the properties that would be populated by your stored procedures and then pass that to your view:

public class PeopleViewModel
{
    public List<Job> Jobs { get; set; }
    public List<Name> Names { get; set; }
}

Then the action method:

public ActionResult Index()
{
    var model = new PeopleViewModel();
    model.Jobs = // ... fetch from database
    model.Names = // ... fetch from database

    return View(model);
}

Update per comments

As you've stated Jobs and Names are collections, the model code you've shown won't solve your problem. It sounds like this is what you really want:

public class Job // Notice I've removed the pluralisation
{
    public int jobID { get; set; }
    public string jobName { get; set; }
}

public class Name // Notice I've removed the pluralisation
{
    public int userId { get; set; }
    public string userName { get; set; }
}

Now, your People class can contain lists of those:

public class People
{
    public List<Job> Jobs { get; set; }
    public List<Name> Names { get; set; }
}

Now you can wrap this in a view model:

public class PeopleViewModel
{
    public People People { get; set; }
}

Then you would populate it from your action:

public ActionResult Index()
{
    var model = new PeopleViewModel();
    model.People = // ... fetch from database

    return View(model);
}

The thing is, if Jobs and Names are unrelated, there's no point in wrapping them in a People class first. You'd simply do the following (as discussed above):

public class PeopleViewModel
{
    public List<Job> Jobs { get; set; }
    public List<Name> Names { get; set; }
}

Think of a view model as a way of representing just a slice of the data you want to display in your view, nothing more, nothing less. All the code above does is define what data you'd like to see in that view. As the properties on the view model are public, you can populate them in your controller's action using whatever method you currently use.

Second Update

I think it would probably be helpful to show you a full example as to how fits together for your views. Firstly, I'll show you the quick and dirty way of accessing the data in your view model and then afterwards I'll show you how to do the same with DisplayTemplates.

Quick and Dirty Approach

So first up, let's assume we have an action called Index like the following:

public ActionResult Index()
{
    var model = new PeopleViewModel();
    model.Jobs = // ... fetch from database
    model.Names = // ... fetch from database

    return View(model);
}

Here's the Index view:

@model PeopleViewModel

@foreach (var job in Model.Jobs)
{
    @Html.DisplayFor(m => job.JobId)
    @Html.DisplayFor(m => job.JobName)
}

@foreach (var name in Model.Names)
{
    @Html.DisplayFor(m => name.UserId)
    @Html.DisplayFor(m => name.UserName)
}

Notice how the view expects an instance of PeopleViewModel and it's simply looping over each individual item in each collection, printing the contents. However, whilst this is fine for very simple views, mixing logic with your HTML becomes a maintenance headache with more complicated views and larger projects. With that in mind, we can make use of DisplayTemplates instead.

Second Approach - Using Templates

The action in your controller remains the same. However, we need to make some other changes. What we're working towards is turning the Index view into this:

@model PeopleViewModel

@Html.DisplayFor(m => m.Jobs)
@Html.DisplayFor(m => m.Names)

By calling Html.DisplayFor in this way, it will try to locate a DisplayTemplate which corresponds to the data type it's being passed. In this case, the helper is smart enough to know that we're passing collections to it and so it will look for a template that matches the data type the collection contains. That means we need to make templates for the types Job and Name. To do that, follow these steps:

  1. Create a DisplayTemplates folder inside your view's current folder (e.g. if your view is Home\Index.cshtml, create the folder Home\DisplayTemplates).
  2. Create a strongly-typed view in that directory with the name that matches your model (i.e. in this case you would make two views, which would be called Job.cshtml and Name.cshtml, respectively).

Now you can move all of the display logic into those two templates. So they would look like the following:

Job.cshtml

@model Job

@Html.DisplayFor(m => m.JobId)
@Html.DisplayFor(m => m.JobName)

Name.cshtml

@model Name

@Html.DisplayFor(m => m.UserId)
@Html.DisplayFor(m => m.UserName)

Hopefully that clears things up a little.

like image 95
John H Avatar answered Sep 21 '22 13:09

John H