Telerik blogs

This may be common knowledge but I failed to quickly find the relevant info when it hit me. In short the default editor template generated by ASP.NET MVC 2 ignores any nested complex properties of your model. Here is a short example which demonstrates the case:

 

Model:

public class Customer
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string Street { get; set; }
public string City { get; set; }
}
Controller:
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View(new Customer
{
FirstName = "John",
LastName = "Doe",
ID = 1,
Address = new Address
{
City = "City",
Street = "Street"
}
});
}

//
// POST: /Home/
[HttpPost]
public ActionResult Index(Customer customer)
{
//Simply render what comes through the wire
return View(customer);
}
}
View:
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<NestedComplexObjectAndEditorTemplate.Models.Customer>" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Index</title>
</head>
<body>
<div>
<% Html.BeginForm(); %>

<%= Html.EditorForModel() %>


<input type="submit" value="Save" />

<% Html.EndForm(); %>
</div>
</body>
</html>
Output:

image

As you see only the properties of primitive type (string and integer in this case) are output. The Address property being a complex object (a class) is ignored and no UI is rendered for it. I was expecting to see the City and Street properties of the Address object too. I went to search in the Internet for a solution or at least for an explanation. The first thing I found was: http://weblogs.asp.net/rashid/archive/2010/02.aspx. I also found a closed issue in codeplex http://aspnet.codeplex.com/workitem/5347. It seems this behavior is by design. I tried the workaround suggested in the blog post which was to create a type converter for the nested complex object (Address in this case):

Model:

[TypeConverter(typeof(AddressConverter))]
public class Address
{
public string Street { get; set; }
public string City { get; set; }
}

public class AddressConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) ? true : base.CanConvertFrom(context, sourceType);
}
}
Output:

image

Still not what I was looking for. Perhaps if I created a custom editor template for the Address type things would start to work. I tried that:

Address.ascx:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NestedComplexObjectAndEditorTemplate.Models.Address>" %>

<%: Html.LabelFor(address => address.City) %> <br />
<%: Html.TextBoxFor(address => address.City) %> <br />
<%: Html.LabelFor(address => address.Street) %> <br />
<%: Html.TextBoxFor(address => address.Street) %>
Output:
image 
Success! The type converter and the custom editor template did the trick!!! Or did they? When I clicked the Save button I saw this:
image
The City and Street properties of the Address were empty! This meant that the model binder didn’t populate the nested Address object for some reason. A quick check with the debugger showed that this was true – Address was null:

image

But why?! The model binder should be capable of doing this. I’d done it in the past, read blog posts doing it etc. Something was wrong. Then I opened up Reflector. I found out the following code in DefaultModelBinder:

if (!bindingContext.ModelMetadata.IsComplexType)
{
return null;
}

It seemed that the type converter workaround enabled the custom editor template but prevented the model binder from populating the Address object. Now that’s a bummer!

 

Fortunately a workaround exists! And the workaround is to create a custom editor template for the model (Customer). And remove that type converter as it causes more harm than good.

Customer.ascx:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NestedComplexObjectAndEditorTemplate.Models.Customer>" %>

<%: Html.LabelFor(customer => customer.ID) %> <br />
<%: Html.TextBoxFor(customer => customer.ID) %> <br />
<%: Html.LabelFor(customer => customer.FirstName) %> <br />
<%: Html.TextBoxFor(customer => customer.FirstName) %> <br />
<%: Html.LabelFor(customer => customer.LastName) %> <br />
<%: Html.TextBoxFor(customer => customer.LastName) %> <br />
Address <br />
<%: Html.LabelFor(customer => customer.Address.City) %> <br />
<%: Html.TextBoxFor(customer => customer.Address.City) %> <br />
<%: Html.LabelFor(customer => customer.Address.Street) %> <br />
<%: Html.TextBoxFor(customer => customer.Address.Street) %> <br />
The output looks suspiciously correct after clicking the save button:
image
Even the debugger shows that the Address property is correctly bound now:

image

Of course writing all that boiler plate code in Customer.ascx (and I’ve intentionally omitted validation to reduce the clutter) is not very pleasant. I wonder what was the reason not to support nested objects in editor templates.

 

Does this limitation affect Telerik Extensions for ASP.NET MVC in any way? Yes it does. If you are using informs or popup editing with the grid it won’t display any complex properties as it happens to use Html.EditorForModel as the example above. The workaround is to create an editor for your model. If you are using inline editing you are lucky – everything should work as expected.

 

The sample project which shows the workaround is attached.

 

I hope this helps!


About the Author

Atanas Korchev

 is Team Leader in Kendo UI Team

Comments

Comments are disabled in preview mode.