I've got a Knockout Model that gets posted via a save method:
self.save = function(form) {
ko.utils.postJson($("form")[0], self);
};
I check the request to make sure all the data is properly being posted (it is):
However, when I get to my action:
[HttpPost]
public ActionResult Create(EquipmentCreateModel equipmentCreateModel)
{
/stuff here
}
BuildingCode and Room contain escaped quotes, and identifiers is totally not null but has a count of 0:
And my ModelState is not valid, there is one error, for the Identifiers
property which has an attempted value of :
and the Exception message is:
"The parameter conversion from type 'System.String' to type 'System.Collections.Generic.KeyValuePair`2[[System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' failed because no type converter can convert between these types."
My Model:
public class EquipmentCreateModel
{
//used to populate form drop downs
public ICollection<Building> Buildings { get; set; }
public ICollection<IdentifierType> IdentifierTypes { get; set; }
[Required]
[Display(Name = "Building")]
public string BuildingCode { get; set; }
[Required]
public string Room { get; set; }
[Required]
[Range(1, 100, ErrorMessage = "You must add at least one identifier.")]
public int IdentifiersCount { get; set; } //used as a hidden field to validate the list
public string IdentifierValue { get; set; } //used only for knockout viewmodel binding
public IDictionary<Guid, string> Identifiers { get; set; }
}
Now first I thought it was a problem with knockout, but then I found out the data wasn't being posted in the request correctly. I fixed that and still had the same problem. I thought MVC3 automatically converts Json now? Why are my simple properties appearing in escaped quotes and why can't my identities collection properly populate from the posted data?
Try this:
[HttpPost]
public ActionResult Create([FromJson] EquipmentCreateModel equipmentCreateModel)
{
//stuff here
}
where FromJson is:
public class FromJsonAttribute : CustomModelBinderAttribute
{
private readonly static JavaScriptSerializer serializer = new JavaScriptSerializer();
public override IModelBinder GetBinder()
{
return new JsonModelBinder();
}
private class JsonModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var stringified = controllerContext.HttpContext.Request[bindingContext.ModelName];
if (string.IsNullOrEmpty(stringified))
return null;
return serializer.Deserialize(stringified, bindingContext.ModelType);
}
}
}
This is taken from:
http://blog.stevensanderson.com/2010/07/12/editing-a-variable-length-list-knockout-style/
you should check the comments to as there are some modification for the FromJsonAttribute.
If you are using MVC3 you don't need to add JsonValueProviderFactory. For those of us who are still on MVC2 you can add JsonValueProviderFactory manually
http://haacked.com/archive/2010/04/15/sending-json-to-an-asp-net-mvc-action-method-argument.aspx
However JsonValueProvider only works with AJAX post. For the full postback it needs the extra processing. There's a thread describing how to handle full postback: groups.google.com/d/topic/knockoutjs/3FEpocpApA4/discussion
The easiest solution would be to use AJAX post. Change
ko.utils.postJson($("form")[0], self);
to
$.ajax({
url: $("form").action,
type: 'post',
data: ko.toJSON(self),
contentType: 'application/json',
success: function (result) {
alert(result);
}
});
You could try:
[HttpPost]
public ActionResult Create(string equipmentCreateModelString)
{
var equipmentCreateModel = JsonConvert.DeserializeObject<EquipmentCreateModel> equipmentCreateModelString, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
}
Otherwise you need to use a JsonValueProviderFactory
. Here's an example
@DOTang, i have another approach. First, you need a clean js object from your view model. You can get it calling: ko.mapping.toJS(self)
, then pass your view model to postJson function as a property. Finally add [FromJson]
attribute to your controller. Your controller argument name, must be equal to your js property name, in this case: model.
I hope it works for you as works for me.
server
[HttpPost]
public ActionResult RegisterUser([FromJson] EquipmentCreateModel model)
{
//...
}
client
self.save = function() {
var jsModel = ko.mapping.toJS(self);
ko.utils.postJson('@Url.Action("Create", "Equipment")', { model : jsModel });
}