How to use two IENumerable models in one view
Looping over models can be a little confusing to begin with in MVC, only because the templated helpers (i.e. Html.DisplayFor
and Html.EditorFor
) can be provided templates which the helper will automatically invoke for every element in a collection. That means if you're new to MVC, and you don't realise a DisplayTemplate
or an EditorTemplate
has not been provided for the collection already, it looks as though a simple:
@Html.DisplayFor(m => m.SomePropertyThatHoldsACollection)
is all you need. So if you've seen something like that already, that might be why you made the assumption it would work. However, let's assume for a moment that a template has not been provided. You have two options.
Firstly, and most simply, would be to use foreach
over the collection:
@foreach (var post in Model.PostsObject)
{
@Html.DisplayFor(m => post.PostTitle)
// display other properties
}
You could also use a for
loop, but with IEnumerable<T>
, there is no indexer, so this won't work:
@for (int i = 0; i < Model.PostsObject.Count(); i++)
{
// This generates a compile-time error because
// the index post[i] does not exist.
// This syntax would work for a List<T> though.
@Html.DisplayFor(m => post[i].PostTitle)
// display other properties
}
If you did want to use the for
loop still, you can use it like so:
@for (int i = 0; i < Model.PostsObject.Count(); i++)
{
// This works correctly
@Html.DisplayFor(m => post.ElementAt(i).PostTitle)
// display other properties
}
So use whichever you prefer. However, at some point it would be a good idea to look into providing templates for your types. (Note: Although this article was written for MVC 2, the advice still applies.) They allow you to remove looping logic from your views, keeping them cleaner. When combined with Html.DisplayFor
, or Html.EditorFor
, they will also generate correct element naming for model binding (which is great). They also allow you to reuse presentation for a type.
One final comment I'd make is that the naming of your properties is a little verbose:
public class ModelMix
{
public IEnumerable<Posts> PostsObject { get; set; }
public IEnumerable<Album> ThreadsObject { get; set; }
}
We already know they're objects, so there's no need to add that on the end. This is more readable:
public class ModelMix
{
public IEnumerable<Posts> Posts { get; set; }
public IEnumerable<Album> Threads { get; set; }
}
user3163730
Updated on July 18, 2022Comments
-
user3163730 almost 2 years
I am trying to use two models in one view, but from what I understand my program just don't see any objects within models.
Here is my code.
Models:
public class Album { [Key] public int ThreadId { get; set; } public int GenreId { get; set; } public string Title { get; set; } public string ThreadByUser { get; set; } public string ThreadCreationDate { get; set; } public string ThreadContent { get; set; } public Genre Genre { get; set; } public List<Posts> Posty { get; set; } } public class Posts { [Key] public int PostId { get; set; } public int ThreadId { get; set; } public string PostTitle { get; set; } public string PostContent { get; set; } public string PostDate { get; set; } public string PosterName { get; set; } public Album Album { get; set; } } public class ModelMix { public IEnumerable<Posts> PostsObject { get; set; } public IEnumerable<Album> ThreadsObject { get; set; } }
Index controller code:
public ActionResult Index(int id) { ViewBag.ThreadId = id; var posts = db.Posts.Include(p => p.Album).ToList(); var albums = db.Albums.Include(a => a.Genre).ToList(); var mixmodel = new ModelMix { PostsObject = posts, ThreadsObject = albums }; return View(mixmodel); }
View code:
@model MvcMusicStore.Models.ModelMix <h2>Index</h2> @Html.DisplayNameFor(model => model.PostsObject.PostContent)
And when I try to execute my program I am getting this error:
CS1061: The " System.Collections.Generic.IEnumerable 'does not contain a definition of" PostContent "not found method of expanding" PostContent ", which takes a first argument of type' System.Collections.Generic.IEnumerable "
How I can make it work as intended? There are a lot of questions like mine on the internet but I couldn't find any matching my case.
-
Jon Douglas over 10 yearsDisplayNameFor will only display titles and not actual data. These are usually for headers of tables.
-
Sergey Litvinov over 10 yearsyep, i know. but original question was about DisplayNameFor method , so made sample with it
-
user3163730 over 10 yearsThis applies when I have only one model, I do know how that works already (I think), I already completed tutorial from the that site (but for mvc4 - the one where you make movies database), but they didn't included any examples of having two models in one view.
-
user3163730 over 10 yearsThank you very much, I went with foreach loop. I had no idea that solution for my problem will be that simple!
-
Jon Douglas over 10 yearsYou can simply just decide which Model you want from the complex object. I.E. Model.Object1.Whatever / Model.Object2.Whatever