4

I am new to MVC and I am trying to display information from a nested model collection on the page.

so my model is as follow:

public partial class Parent
{
    public Parent()
    {
        this.Childs = new HashSet<Child>();
     }

    public int ParentID { get; set; } 
     public string Name { get; set; } 

    public virtual ICollection<Child> Childs { get; set; }
 }

To display the information on the Parent view i used :

@foreach (Child c in Model.Childs)
{
    @c.Name 
}

The above works but i would like to use a different view for the childs so i have tried the following:

@Html.DisplayFor(model => model.Childs)

and defined the following view:

@model WebApplication1.Models.Child

<div>
      @Html.DisplayFor(model => model.Name)
</div>

This doesn't work and what I am getting is a System.Data.Entity.DynamicProxies displayed instead of the list of child names. I have read that this is because MVC5 doesn't know what view to use. I have tried specifying the view in the Display for as @Html.DisplayFor(model => model.Childs, "ViewName1.cshtml") but that didn't help at all.

In addition to the above I would like to use something similar for @Html.EditorFor(model => model.Childs).

abatishchev
  • 98,240
  • 88
  • 296
  • 433
panais
  • 81
  • 1
  • 8
  • I can only assume model.Name is a string since you don't show us the class definition for Child – Jakotheshadows Jul 07 '14 at 15:56
  • have you added the child view in proper folders, display view should be in DisplayTemplates folder and edit views should be in EditorTemplates folder. Also, I think you will have to change the collection to a type like List which are index based . – inquisitive Jul 07 '14 at 16:40
  • 1
    @inquisitive No, that was my problem (new to MVC). Now i have a different problem which is when I change the name of the child the change doesn't get preserved when I save the model. Any suggestions? – panais Jul 09 '14 at 09:31

3 Answers3

5

You need to tell Razor which view to use for each child object. Something like this (obviously replace ChildViewName with the name of your child view):

@foreach (Child c in Model.Childs)
{
    @Html.Partial("ChildViewName", c)
}
DavidG
  • 113,891
  • 12
  • 217
  • 223
5

Start by creating a DisplayTemplates sub-folder in your Parent view folder. Then create a view in that folder called Child.cshtml.

Child.cshtml

@model WebApplication1.Models.Child

@Model.Name
...more HTML markup if needed

Then all you do is call @Html.DisplayFor(m => m.Childs) and the framework will do the rest for you. Note that if you use the overload that lets you specify which view to use, the loop will not be done automatically for you.

Repeat the same process for editor templates, with a EditorTemplates folder and following the same conventions (view name = type name) for naming your views.

dom
  • 6,702
  • 3
  • 30
  • 35
  • 1
    This won't work as `m.Childs` is `ICollection`. – DavidG Jul 07 '14 at 15:41
  • Though you can combine your answer with mine for a neat solution. – DavidG Jul 07 '14 at 15:46
  • @DavidG Would it need to be `IEnumerable` to work? Wasn't aware it doesn't work with `ICollection`. – dom Jul 07 '14 at 15:50
  • You need a specific template for `ICollection` just as much as you do for `IEnumerable`. Think like this, your view expects the model to be a `Child` object, but DisplayFor is giving something else. You could put `UIHint` attribute on the `Parent.Childs` property to point it at a view that essentially contains my answer. – DavidG Jul 07 '14 at 15:53
  • Actually, I might be telling a lie, you may be able to do it the way you have suggested! – DavidG Jul 07 '14 at 16:02
  • @DavidG I haven't tested, but I trust Darin with `asp.net mvc` and according to his explanation in [this answer](http://stackoverflow.com/a/8679997/609176), the original answer here should work if you don't specify a template name or `UIHint`. – David Spence Jul 07 '14 at 16:07
  • 1
    @DavidSpence Indeed, I've spent a couple minutes testing and the original answer works fine (full apologies to Dom!) Guess we should revert the edit that Jakotheshadows made. Also a +1 for Dom, handy to know this one. – DavidG Jul 07 '14 at 16:10
  • @DavidG You had me worried there for a bit! I knew this worked with editor templates but I haven't ever really had to use it for display templates and just assumed they worked the same way. Thought maybe there was something different about display templates after your comment. The more you know! – dom Jul 07 '14 at 16:40
  • I have followed what is been said above and it now displays on screen. The problem though is with the editor. When I change the name of the child the change doesn't get preserved. If I view the model in public ActionResult Edit([Bind(Include="..."]) the sub model collection for childs is empty. Why is that? Is there anything else I need to do? – panais Jul 09 '14 at 08:17
  • @panais Sounds like there's something wrong with model binding on POST. Hard to say for sure without seeing your controller methods (GET/POST) though. – dom Jul 09 '14 at 14:10
  • To overcome the problem above I had to loop through the collection and update the state of the subitems to EntityState.Modified. Is that what I should be doing or the work around is wrong? – panais Jul 09 '14 at 14:15
  • @panais I recall having to do the exact same thing in certain situations, so I would say your workaround isn't wrong. Although someone else might have a better solution, but I haven't found any myself. – dom Jul 09 '14 at 15:13
1

Adding my comments as an extension to the answers above because I think many people who are new to asp.net mvc miss this. @panais mentioned that my comment helped him\her. So, I think it might be helpful for others as well.

  1. The editor template views should be created in EditorTemplates folder.
  2. The display template views should be created in DisplayTemplates folder.
  3. Name of the template view should be same as view model name.
inquisitive
  • 549
  • 4
  • 7