Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use two IENumerable models in one view

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.

like image 958
user3163730 Avatar asked Jan 06 '14 15:01

user3163730


1 Answers

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; }
}
like image 108
John H Avatar answered Oct 09 '22 10:10

John H