简体   繁体   中英

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>

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.

Instead of

model.Nails[0].Length

You could use

model.Nails.First().Length

Without having to convert to a List .

I think you can just use item variable of loop

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

UPDATE another option for you is creating partial view for Nails displaying. It will be strongly typed to IEnumerable<NailClippings> and all properties will be available:

@Html.Partial("_Nails", Model.Nails)

Nails partial view:

@model IEnumerable<NailClippings>

<table>
    <tr>
        <th>@Html.DisplayNameFor(model => model.Length)</th>
        .
        .
    </tr>
    @foreach(var item in Model){
    <tr>
        <td>@Html.DisplayFor(modelItem => item.Length)</td>
        .
        .
    </tr>
    }
</table>

I think this sample code is useful for nested model class

@model ViewModels.MyViewModels.Theme

@Html.LabelFor(Model.Theme.name)
@foreach (var category in Model.Theme)
{
   @Html.LabelFor(category.name)
   @foreach(var product in theme.Products)
   {
      @Html.LabelFor(product.name)
      @foreach(var order in product.Orders)
      {
          @Html.TextBoxFor(order.Quantity)
          @Html.TextAreaFor(order.Note)
          @Html.EditorFor(order.DateRequestedDeliveryFor)
      }
   }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM