Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Razor view with nested model class

I have a nested class such as :

public class Jar
{
    public IEnumerable<NailClippings> Nails { set; get; }
    public IEnumerable<People> Donors { set; get; }
}

My controller passes a single Jar to my view, string typed as

@model Test.ViewModels.Jar

I can loop through the contents of Nails and Donors easily with something like this

@foreach(var item in Model.Nails)

My issue is with using HTML helpers to generate Display names for me

@Html.DisplayNameFor(model => model.Nails.Length)

I have come up with a solution by changing the type of the inner nested classes to List, appending .ToList() at the end of my queries and changing the line to

@Html.DisplayNameFor(model => model.Nails[0].Length)

Being forced to use List and have an index [0] to display a name is goofy to me. Is there an alternative method for referencing inner nested class attributes?

Edit: View

<table>
    <tr>
        <th>@Html.DisplayNameFor(model => model.Nails[0].Length)</th>
        .
        .
    </tr>
    @foreach(var item in Model.Nails){
    <tr>
        <td>@Html.DisplayFor(modelItem => item.Length</td>
        .
        .
    </tr>
    }
</table>
like image 701
Jeff Avatar asked Jan 11 '13 20:01

Jeff


1 Answers

You don't need to be writing any loops in your views. It makes them so ugly.

Simply define a corresponding display template for the collection type. So for example you would create ~/Views/Shared/DisplayTemplates/NailClippings.cshtml partial view that will be strongly typed to a single element of the collection:

@model NailClippings
<tr>
    <td>
        @Html.DisplayFor(x => x.Length)
    </td>
    .
    .
</tr>

and now inside your main view it's as simple as:

@model Jar
<table>
    <thead>
        <tr>
            <th>Length</th>
            .
            .
        </tr>
    </thead>
    <tbody>
        @Html.DisplayFor(x => x.Nails)
    </tbody>
</table>

Display/editor templates work by convention. So here when inside your main view you use @Html.DisplayFor(x => x.Nails), ASP.NET MVC will analyze the type of the Nails property and will see that it is an IEnumerable<NailClippings> collection. So it will start looking for a corresponding display template first inside ~/Views/CurrentController/DisplayTemplates/NailClippings.cshtml, then inside ~/Views/Shared/DisplayTemplates/NailClippings.cshtml and finally it will fallback to the default display template. When a template is selected this template will automatically be executed for you by ASP.NET MVC for each element of the collection property so that you never have to worry about things like writing some loops in your views, worry about indexes, ...

This also works with editor templates: @Html.EditorFor(x => x.Nails) will look for ~/Views/Shared/EditorTemplates/NailClippings.cshtml. The bonus you get here is that the generated input fields will have correct names and you when you submit the form, the default model binder will automatically rehydrate your view model that the HttpPost action is taking as argument from the request.

Basically if you respect the conventions that the framework is following you will make your life much easier.

like image 79
Darin Dimitrov Avatar answered Nov 15 '22 05:11

Darin Dimitrov