Saturday 17 April 2010

Using the EditorFor method in MVC2 to display child collections

I was very excited when I discovered the new DisplayFor and EditorFor extensions in the new MVC release. In fact, they look even better than the similar MVCContrib features.

The only problem is that when I tried to render a child collection, it displayed nothing. This is kinda weird, because when your model is a collection, it shows just fine. However, as this post explains, anything complex enough (that is, not convertible to a string) is just filtered out.

It turned out that it could be fixed relatively easily. You have to override the default Object template and fix the logic. Just create a view called Object.ascx in your Views/Shared/EditorTemplates folder, copy the default template from here, and modify the filtering logic. Took me a while, since I had to convert it to VB.Net. Here's the result:

<%@ Control Language="VB" Inherits="System.Web.Mvc.ViewUserControl" %>
<%@ Import Namespace="System.Globalization"%>
<%@ Import Namespace="System.Linq" %>

<% If (ViewData.ModelMetadata.Model Is Nothing) Then%>
<%=ViewData.ModelMetadata.NullDisplayText%>
<% Else%>
<% If (ViewData.TemplateInfo.TemplateDepth > 3) Then%>
<%=Me.ViewData.ModelMetadata.SimpleDisplayText%>
<% Else%>
<% Dim props = From pm In Me.ViewData.ModelMetadata.Properties Where pm.ShowForEdit AndAlso Not ViewData.TemplateInfo.Visited(pm) For Each prop In props If prop.HideSurroundingHtml Then%>
<%= Html.Editor(prop.PropertyName) %>
<% Else Dim str As String = Html.Label(prop.PropertyName).ToHtmlString If Not String.IsNullOrEmpty(str) Then%>
<%= Html.Label(prop.PropertyName) %>

<% End If%>

<%= Html.Editor(prop.PropertyName) %>
<%= Html.ValidationMessage(prop.PropertyName, "*") %>

<% End If%>


<%Next%>
<% End If%>
<% End If%>

Don't forget to check the line related to TemplateDepth. Originally, it makes a shallow copy: everything with the depth>1 is just displayed as a simple string. You might want to make this number greater, or remove the check entirely.