MVC Complex Model Postback: Bind Field to JSON string
MVC Complex model postback: bind field to JSON string
Introduction
In my scenario, model had both simple fields directly bindable to input controls, and an array of complex objects that was constructed using fancy JavaScript. I had to submit the whole model using one form postback (no AJAX).
This is the model (the challenge here was to bind ComplexData
):
public class ComplexModel
{
public string FirstName {get;set;}
public string LastName {get;set;}
[JsonBindable]
public List<ComplexObject> ComplexData {get;set;}
}
Controller is trivial:
public class ComplexController : Controller
{
public ViewResult Save(ComplexModel model)
{
// save it
}
}
View binds FirstName
and LastName
directly to inputs and ComplexData
property is prepared just before form submit by stringifying the JSON object. Formatted JSON string
is placed in a hidden field.
<script>
$(document).ready(function () {
$('form').submit(function () {
$('input[name=ComplexData]').val(JSON.stringify(getMyComplexDataObject()));
});
}
</script>
<form>
<input name='FirstName'/>
<input name='LastName'/>
<input type='hidden' name='ComplexData'/>
<input type='submit' value='Save!' />
</form>
So the problem was to bind JSON string to the ComplexObject
. The solution to the problem was to create custom attribute:
public class JsonBindableAttribute : Attribute
{
}
… and a custom model binder:
public class JsonPropBinder : DefaultModelBinder
{
protected override object GetPropertyValue(ControllerContext controllerContext,
ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor,
IModelBinder propertyBinder)
{
if (propertyDescriptor.Attributes.OfType<Attribute>().
Any(x => (x is JsonBindableAttribute)))
{
var value = bindingContext.ValueProvider.
GetValue(bindingContext.ModelName).AttemptedValue;
return JsonConvert.DeserializeObject
(value, propertyDescriptor.PropertyType);
}
return base.GetPropertyValue(controllerContext, bindingContext,
propertyDescriptor, propertyBinder);
}
}
… and finally register the binder:
protected void Application_Start()
{
ModelBinders.Binders.Add(typeof(ComplexModel), new JsonPropBinder());
}