ASP.net MVC - Collection Display Template

I have the following model in MVC:

public class ParentModel { public string Property1 { get; set; } public string Property2 { get; set; } public IEnumerable<ChildModel> Children { get; set; } } 

When I want to display all the children of the parent model, I can do:

 @Html.DisplayFor(m => m.Children) 

Then I can create a ChildModel.cshtml display template, and DisplayFor will iterate over the list automatically.

What if I want to create my own template for IEnumerable?

 @model IEnumerable<ChildModel> <table> <tr> <th>Property 1</th> <th>Property 2</th> </tr> ... </table> 

How to create a display template with IEnumerable<ChildModel> model type and then call @Html.DisplayFor(m => m.Children) without complaining about the wrong model type?

+37
asp.net-mvc asp.net-mvc-3
Nov 03 2018-11-11T00:
source share
4 answers

Like this:

 @Html.DisplayFor(m => m.Children, "YourTemplateName") 

or like this:

 [UIHint("YourTemplateName")] public IEnumerable<ChildModel> Children { get; set; } 

where obviously you will have ~/Views/Shared/DisplayTemplates/YourTemplateName.cshtml :

 @model IEnumerable<ChildModel> <table> <tr> <th>Property 1</th> <th>Property 2</th> </tr> ... </table> 
+57
Nov 03 2018-11-11T00:
source share

This is a response to Maslow's comment. This is my first contribution to SO, so I do not have enough reputation to comment - hence the answer as an answer.

You can set the "TemplateHint" property in ModelMetadataProvider. This will automatically connect any IEnumerable to the specified template. I just tried this in my project. The code below is

 protected override CachedDataAnnotationsModelMetadata CreateMetadataFromPrototype(CachedDataAnnotationsModelMetadata prototype, Func<object> modelAccessor) { var metaData = base.CreateMetadataFromPrototype(prototype, modelAccessor); var type = metaData.ModelType; if (type.IsEnum) { metaData.TemplateHint = "Enum"; } else if (type.IsAssignableFrom(typeof(IEnumerable<object>))) { metaData.TemplateHint = "Collection"; } return metaData; } 

You basically override the CreateMetadataFromPrototype method for 'CachedDataAnnotationsModelMetadataProvider' and register your derived type as the preferred ModelMetadataProvider.

In a template, you cannot directly access ModelMetadata from the elements of your collection. I used the following code to access ModelMetadata for items in my collection -

 @model IEnumerable<object> @{ var modelType = Model.GetType().GenericTypeArguments[0]; var modelMetaData = ModelMetadataProviders.Current.GetMetadataForType(null, modelType.UnderlyingSystemType); var propertiesToShow = modelMetaData.Properties.Where(p => p.ShowForDisplay); var propertiesOfModel = modelType.GetProperties(); var tableData = propertiesOfModel.Zip(propertiesToShow, (columnName, columnValue) => new { columnName.Name, columnValue.PropertyName }); } 

In my opinion, I just call @ Html.DisplayForModel () and the template loads. There is no need to indicate "UIHint" on models.

I hope this made some difference.

+7
Aug 03 '13 at 18:05
source share

In my question about not getting output from views, I actually have an example of how to template a model with a collection of child models and have them all rendered.

ASP.NET Display Templates - No Output

Essentially, you need to create a model that subclasses List<T> or Collection<T> and use this:

 @model ChildModelCollection @foreach (var child in Model) { Html.DisplayFor(m => child); } 

In your template for a collection model for iterating and rendering children. Each child needs to be strongly typed, so you can also create your own types of models for these elements and have templates for them.

So for the OP question:

 public class ChildModelCollection : Collection<ChildModel> { } 

Creates a strongly typed model that can be solved for a template, like any other.

+6
Apr 24 '14 at 20:06
source share

The actual "valid answer" is -IMHO- incorrectly answers the question. I think the OP is looking for a way to have a list template that starts without specifying UIHint.

Magical stuff almost does the job

Some magic download the correct view for the specified type .
A few more magic loads of the same representation for the collection of the specified type.
There must be some magic that iterates the same view for a collection of the specified type.

Change the actual behavior?

Open your favorite disassembler. The magic happens in System.Web.Mvc.Html.TemplateHelpers.ExecuteTemplate . As you can see, there are no extensibility points for changing behavior. Perhaps a transfer request in MVC might help ...

Go with the actual magic

I came up with something that works. Create a display template ~/Views/Shared/DisplayTemplates/MyModel.cshtml .

Declare the model as type object .

If the object is a collection, repeat and draw the template again. If this is not a collection, then show the object.

 @model object @if (Model is IList<MyModel>) { var models = (IList<MyModel>)Model; <ul> @foreach (var item in models) { @Html.Partial("DisplayTemplates/MyModel", item) } </ul> } else { var item = (MyModel)Model; <li>@item.Name</li> } } 

DisplayFor now works without UIHint .

+1
Dec 13 '14 at 23:29
source share



All Articles