Two models in one view in ASP MVC 3
Asked Answered
N

12

93

I have 2 models:

public class Person
{
    public int PersonID { get; set; }
    public string PersonName { get; set; }
}
public class Order
{
    public int OrderID { get; set; }
    public int TotalSum { get; set; }
}

I want edit objects of BOTH classes in SINGLE view, so I need something like:

@model _try2models.Models.Person
@model _try2models.Models.Order

@using(Html.BeginForm())
{
    @Html.EditorFor(x => x.PersonID)
    @Html.EditorFor(x => x.PersonName)
    @Html.EditorFor(x=>x.OrderID)
    @Html.EditorFor(x => x.TotalSum)
}

This, of course, don't work: Only one 'model' statement is allowed in a .cshtml file. May be there is some workaround?

Nigercongo answered 5/4, 2011 at 10:49 Comment(1)
i used ViewBag with for each in view worked for me, check this to know multiple options, saved little time for me instead of creating a view model or partial viewAlpers
U
123

Create a parent view model that contains both models.

public class MainPageModel{
    public Model1 Model1{get; set;}
    public Model2 Model2{get; set;}
}

This way you can add additional models at a later date with very minimum effort.

Umbilication answered 5/4, 2011 at 10:51 Comment(7)
When using this solution, please bear in mind that changes to Model1 or Model2 may have a knock on effect on your MainPageModel. Also they main contain more data than the view really needs. If your MainPage is a combination of things that are already in other controllers, switching from RenderPartial to RenderAction will allow you to keep things neatly separated. Consider reading up on the Law of Demeter: en.wikipedia.org/wiki/Law_of_DemeterParanoid
@Andi - I created the Model as you suggested above. But when i right click on the controller and try to create Crud controller, it is not working? Any suggestions? How i can create crud controller with automatic view for above Model?Terreverte
Of all the approaches I've seen to solve this problem, this is the one that worked best for me. It is by far the simplest solution that works.Orose
The momment when you ask yourself "Why Didn't I Think of That Before?" Great solutionSarad
@Andi How would I expose methods of respective model in controller?Transcript
@Html.DisplayFor(model => model.Model1.Bikes) at the top, @model references MainPageModel to fill the model: //TeamBoards teamBoards = new TeamBoards(); //teamBoards.Boards = (from b in db.Boards // where b.TeamId == id // select b).ToList(); //teamBoards.Team = (from t in db.Teams // where t.TeamId == id // select t).FirstOrDefault();Brandy
But what happens if one model needs to be gone with a IEnumerable .... ?Tell
S
56

To use the tuple you need to do the following, in the view change the model to:

@model Tuple<Person,Order>

to use @html methods you need to do the following i.e:

@Html.DisplayNameFor(tuple => tuple.Item1.PersonId)

or

@Html.ActionLink("Edit", "Edit", new { id=Model.Item1.Id }) |

Item1 indicates the first parameter passed to the Tuple method and you can use Item2 to access the second model and so on.

in your controller you need to create a variable of type Tuple and then pass it to the view:

    public ActionResult Details(int id = 0)
    {
        Person person = db.Persons.Find(id);
        if (person == null)
        {
            return HttpNotFound();
        }
        var tuple = new Tuple<Person, Order>(person,new Order());

        return View(tuple);
    }

Another example : Multiple models in a view

Sparse answered 11/12, 2012 at 4:7 Comment(2)
Never use a Tuple in this case - OP is wanting to edit data, and a Tuple has no default constructor so the model cannot be bound when the form is submittedKiruna
Never say never. That's a little strong here.Premillennial
P
47

Another option which doesn't have the need to create a custom Model is to use a Tuple<>.

@model Tuple<Person,Order>

It's not as clean as creating a new class which contains both, as per Andi's answer, but it is viable.

Photocompose answered 26/9, 2011 at 16:58 Comment(6)
in mvc 3 it gives error Only one 'model' statement is allowed in a file. Any idea how to solve it?Longanimity
@Longanimity - I'm using it in MVC3 just fine. Make sure you don't have two separate @model lines?Photocompose
You can get the properties of each Model with: @Model.Item1.Property and @Model.Item2.Property for Person and Order respectivelyDicentra
I have used Tuple in my View. But, i cannot able to get the model values in my controller. But, while I using the Parent view model as @Andi recommended, i got the model values in my controller.Hopple
A Tuple will not work when editing - you cannot bind back to it in the POST method (it has no parameter-less constructor)Kiruna
@StephenMuecke - Hmm. I'd never considered that, but you're right. Another reason that Andrew's answer is the better general-purpose one. When you go with a light-weight alternative (like this), you do give up functionality.Photocompose
P
10

If you are a fan of having very flat models, just to support the view, you should create a model specific to this particular view...

public class EditViewModel
    public int PersonID { get; set; }
    public string PersonName { get; set; }
    public int OrderID { get; set; }
    public int TotalSum { get; set; }
}

Many people use AutoMapper to map from their domain objects to their flat views.

The idea of the view model is that it just supports the view - nothing else. You have one per view to ensure that it only contains what is required for that view - not loads of properties that you want for other views.

Paranoid answered 5/4, 2011 at 10:59 Comment(0)
B
5

ok, everyone is making sense and I took all the pieces and put them here to help newbies like myself that need beginning to end explanation.

You make your big class that holds 2 classes, as per @Andrew's answer.

public class teamBoards{
    public Boards Boards{get; set;}
    public Team Team{get; set;}
}

Then in your controller you fill the 2 models. Sometimes you only need to fill one. Then in the return, you reference the big model and it will take the 2 inside with it to the View.

            TeamBoards teamBoards = new TeamBoards();


        teamBoards.Boards = (from b in db.Boards
                               where b.TeamId == id
                               select b).ToList();
        teamBoards.Team = (from t in db.Teams
                              where t.TeamId == id
                          select t).FirstOrDefault();

 return View(teamBoards);

At the top of the View

@model yourNamespace.Models.teamBoards

Then load your inputs or displays referencing the big Models contents:

 @Html.EditorFor(m => Model.Board.yourField)
 @Html.ValidationMessageFor(m => Model.Board.yourField, "", new { @class = "text-danger-yellow" })

 @Html.EditorFor(m => Model.Team.yourField)
 @Html.ValidationMessageFor(m => Model.Team.yourField, "", new { @class = "text-danger-yellow" })

And. . . .back at the ranch, when the Post comes in, reference the Big Class:

 public ActionResult ContactNewspaper(teamBoards teamboards)

and make use of what the model(s) returned:

string yourVariable = teamboards.Team.yourField;

Probably have some DataAnnotation Validation stuff in the class and probably put if(ModelState.IsValid) at the top of the save/edit block. . .

Brandy answered 20/4, 2016 at 1:32 Comment(0)
P
4

In fact there is a way to use two or more models on one view without wrapping them in a class that contains both.

Using Employee as an example model:

@model Employee

Is actually treated like.

@{ var Model = ViewBag.model as Employee; }

So the View(employee) method is setting your model to the ViewBag and then the ViewEngine is casting it.

This means that,

ViewBag.departments = GetListOfDepartments();
    return View(employee);

Can be used like,

            @model  Employee
        @{
                var DepartmentModel = ViewBag.departments as List<Department>;
        }

Essentially, you can use whatever is in your ViewBag as a "Model" because that's how it works anyway. I'm not saying that this is architecturally ideal, but it is possible.

Paraffinic answered 9/6, 2015 at 14:18 Comment(0)
U
3

Just create a single view Model with all the needed information in it, normaly what I do is create a model for every view so I can be specific on every view, either that or make a parent model and inherit it. OR make a model which includes both the views.

Personally I would just add them into one model but thats the way I do it:

public class xViewModel
{
    public int PersonID { get; set; }
    public string PersonName { get; set; }
    public int OrderID { get; set; }
    public int TotalSum { get; set; }
}

@model project.Models.Home.xViewModel

@using(Html.BeginForm())
{
    @Html.EditorFor(x => x.PersonID)
    @Html.EditorFor(x => x.PersonName)
    @Html.EditorFor(x => x.OrderID)
    @Html.EditorFor(x => x.TotalSum)
}
Uke answered 12/8, 2014 at 10:28 Comment(0)
I
1

You can use the presentation pattern http://martinfowler.com/eaaDev/PresentationModel.html

This presentation "View" model can contain both Person and Order, this new
class can be the model your view references.

Imbroglio answered 5/4, 2011 at 10:53 Comment(1)
The presentation model should actually contain the data required, which may originate from a Person and an Order. It shouldn't really contain the Person or Order domain objects. The view decouples the display from the underlying domain.Paranoid
C
0

Another way that is never talked about is Create a view in MSSQL with all the data you want to present. Then use LINQ to SQL or whatever to map it. In your controller return it to the view. Done.

Condottiere answered 13/9, 2011 at 21:16 Comment(0)
M
0

you can't declare two model on one view, try to use Html.Action("Person", "[YourController]") & Html.Action("Order", "[YourController]").

Good luck.

Mendiola answered 13/8, 2013 at 7:20 Comment(1)
What does this do?Premillennial
G
0

Beside of one view model in asp.net you can also make multiple partial views and assign different model view to every view, for example:

   @{
        Layout = null;
    }

    @model Person;

    <input type="text" asp-for="PersonID" />
    <input type="text" asp-for="PersonName" />

then another partial view Model for order model

    @{
        Layout = null;
     }

    @model Order;

    <input type="text" asp-for="OrderID" />
    <input type="text" asp-for="TotalSum" />

then in your main view load both partial view by

<partial name="PersonPartialView" />
<partial name="OrderPartialView" />
Grantley answered 25/6, 2019 at 12:3 Comment(0)
G
-1

I hope you find it helpfull !!

i use ViewBag For Project and Model for task so in this way i am using two model in single view and in controller i defined viewbag's value or data

List<tblproject> Plist = new List<tblproject>();
            Plist = ps.getmanagerproject(c, id);

            ViewBag.projectList = Plist.Select(x => new SelectListItem
            {
                Value = x.ProjectId.ToString(),
                Text = x.Title
            });

and in view tbltask and projectlist are my two diff models

@{

IEnumerable<SelectListItem> plist = ViewBag.projectList;

} @model List

Gilly answered 4/4, 2016 at 10:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.