How do you access the DisplayNameFor
in a nested model - ie.:
public class Invoice
{
public int InvoiceId { get; set; }
public string CustomerName { get; set; }
public string Contact { get; set; }
public IList<InvoiceItem> InvoiceItems { get; set; }
}
public class InvoiceItem
{
public int InvoiceItemId { get; set; }
public int InvoiceId { get; set; }
[Display(Name = "Item Heading")]
public string Item { get; set; }
[Display(Name = "Item Description")]
public string Description { get; set; }
public virtual Invoice Invoice { get; set; }
}
I can loop through the nested items as follows:
@foreach (var item in Model.InvoiceItems)
{
@Html.DisplayFor(modelItem => item.Item)
<br />
@Html.DisplayFor(modelItem => item.Description)
<br />
}
But I how do I get to: [Display(Name = "Item Heading")]
@Html.DisplayNameFor(modelItem => modelItem.Item)
I get the error: 'inv5.Models.Invoice' does not contain a definition for 'Item' and no extension method 'Item' accepting a first argument of type 'inv5.Models.Invoice' could be found
Thanks, Mark
Here you're accessing the item
in your loop:
@Html.DisplayFor(modelItem => item.Item)
But here you're not:
@Html.DisplayNameFor(modelItem => modelItem.Item)
Change the latter to this and it should work:
@Html.DisplayNameFor(modelItem => item.Item)
modelItem
is of type Invoice
(the model passed to the view), but in the loop you want to use item
(from the loop iterator) which is of type InvoiceItem
.
Side note: Debugging something like this might be a lot more straightforward if everything wasn't called "item." Clear naming is important :)
Edit: If you're accessing it outside of the loop, then you need to drill down into the model manually. Something like this:
@Html.DisplayNameFor(modelItem => modelItem.InvoiceItems.First().Item)
It feels like this would be dangerous, since .First()
can throw an exception if there are no items. However, as comments have indicated and some cursory Googling seems to confirm, .First()
isn't actually executed at runtime in this case. This is just a bit of trickery to allow .DisplayNameFor()
to reflect down into the property.
You can create a DisplayTemplate for your InvoiceItems
, say "InvoiceItemHeader":
@model IEnumerable<InvoiceItem>
@Html.DisplayNameFor(m => m.Item)
<br />
@Html.DisplayNameFor(m => m.Description)
The type of the model has to be IEnumerable<T>
, as the DisplayNameFor Method has an overload version for this type.
Then in your view you can display the template:
@Html.DisplayFor(m => m.InvoiceItems, "InvoiceItemHeader")
@foreach (var item in Model.InvoiceItems)
{
@Html.DisplayFor(modelItem => item.Item)
<br />
@Html.DisplayFor(modelItem => item.Description)
<br />
}
I have not tried this, but it should work.
@Html.DisplayNameFor(modelItem => modelItem.First().InvoiceItems().First().Item)
DisplayNameFor extension method is nothing else than a method expecting a Memberexpression with this signature: Expression<Func<TModel, TValue>> expression
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With