Reputation: 2115
I am working through a KnockOutJs Sample and am having some posting problems with MVC3. My sample correctly posts when using a full page postback. When I try to save using an jQuery Ajax post, I can see the Post in the using Firebug NET viewer to be:
{ gifts:[{"GiftId":0,"Title":"sad","Price":3}] }
When I view the ControllerContext in the ModelBinder in MVC3, the form parameters are empty and the json does not bind. Any ideas as to what is happening?
I have tried a number of configurations but here is the jQuery posting code (currently hard-coded to a static value):
...
$.ajax({
url: "/Home/PartialUpdate",
type: 'POST',
cache: false,
data: '{ gifts:[{"GiftId":0,"Title":"sad","Price":3}] }', //ko.toJSON({ gifts: this.gifts }),
dataType: 'json' ,
contentType: "application/json;",
success: function(result){
alert(result);
var data = ko.utils.parseJson(result);
this.gifts = ko.observableArray(data) ;
},
error:function(xhr,err){
alert("readyState: " + xhr.readyState+"\nstatus: "+xhr.status);
alert("responseText: " + xhr.responseText);
}
});
Edit: Here is the MVC3 action code for the Ajax update code
[HttpPost]
public JsonResult PartialUpdate ([FromJson] IEnumerable<Gift> gifts)
{
gifts = gifts ?? new List<Gift>();
using (var context = new KnockOutContext())
{
// Add record if not in DB
foreach (var gift in gifts )
{
context.Entry(gift).State = (gift.GiftId == 0) ? EntityState.Added : EntityState.Modified;
}
// Delete records if not in ViewModel
foreach (var dbGift in context.Gifts)
{
if (gifts.SingleOrDefault(c => c.GiftId == dbGift.GiftId) == null)
context.Gifts.Remove(dbGift);
}
context.SaveChanges();
}
return GetGifts_Json();
}
And the full postback code that works (from Steve Sanderson's example at http://blog.stevensanderson.com/2010/07/12/editing-a-variable-length-list-knockout-style/)
[HttpPost]
public ActionResult Index([FromJson] IEnumerable<Gift> gifts)
{
SaveGifts(gifts);
return RedirectToAction("Index");
}
using this custom model binder:
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);
}
}
}
Upvotes: 1
Views: 1643
Reputation: 114792
My guess is that you started with Steve's download, which is using MVC2. MVC2 did not have the JsonValueProvider registered by default. The [FromJson]
attribute was intended to work with URL-encoded JSON that was submitted via ko.utils.postJson (full postback). This is not necessary when posting JSON via AJAX with the correct content-type (in MVC3).
So, the easiest thing to do is upgrade your project to MVC 3 (easy way here) and remove the [FromJson]
attribute from your partial update.
Working copy here.
One other really minor thing: your static data is currently invalid JSON ('{ gifts:[{"GiftId":0,"Title":"sad","Price":3}] }'
). gifts
would need to be "gifts"
Upvotes: 4