5

I'm trying to do a post of the mapped KnockoutJS model. I can see when debugging it, the JSON is correct. But the server shows that Product is 0 (empty). While it does contain 1 item.

MVC Controller:

[HttpPost]
public ActionResult Test(MyModel model, FormCollection fc)
{
   return RedirectToAction("index");
}

The AJAX submit:

$('#btnSubmit').click(function (event) {
        var theModel = ko.mapping.toJSON(viewModel);
        debugger;
        $.ajax({
            type: 'POST',
            url: "@Url.Action("Test", "Home")",
            data: theModel,
            contentType: 'application/json; charset=utf-8',
            success: function (result) {
                if (!result.success) {
                    //alert(result.error);
                }
                else { }
            }
        });
    });

This is a partial JSON object:

"Products":[{"Id":2,"Name":"bread"}]

What am I doing wrong?

EDIT:

public class MyModel
{
   public int User { get; set; }
   public string Address { get; set; }
   public string ZipCode { get; set; }
   public List<Product> Products { get; set; }

}

public class Product
{
   public int Id { get; set; }
   public string Name { get; set; } 
}
Quoter
  • 4,236
  • 13
  • 47
  • 69
  • Could you post `MyModel` ? – Joffrey Kern Dec 09 '13 at 16:31
  • we use ko.dataFor http://stackoverflow.com/questions/14968565/does-ko-datafor-work-with-select-elements – Matt Bodily Dec 09 '13 at 16:35
  • @JoffreyKern, `MyModel` added – Quoter Dec 09 '13 at 16:56
  • As we can see your `Product` has no notion of `TypeId`. It has a property of `Id`. But, the `JSON` you have posted contains a `TypeId` – Shuhel Ahmed Dec 09 '13 at 17:13
  • Try fixing you `javascirpt` model that represents `Product`. Replace `TypeId` with `Id` or the vice versa. – Shuhel Ahmed Dec 09 '13 at 17:20
  • Hi guys, I'm sorry about that, It does have TypeId on both sided, client and server. Just forgot to adjust the code to Id on the client as well (for the sake of simplicity). But I can assure you that the model on both sides is exactly the same. Updating the question right now. – Quoter Dec 09 '13 at 18:22
  • Anyone else has a suggestion? I'm still stuck with this problem. – Quoter Dec 10 '13 at 15:07

2 Answers2

3

Here is a full working tested example (sending a model back from the controller and posting):

Controller

public ActionResult Test()
{
    var model = new MyModel();
    model.Products = new List<Product> { new Product { Id = 2, Name = "bread" } };
    return View(model);
}

[HttpPost]
public ActionResult Test(MyModel model, FormCollection fc)
{
    // Count equals one
    var count = model.Products.Count();
    return RedirectToAction("index");
}

Model

public class MyModel
{
    public int User { get; set; }
    public string Address { get; set; }
    public string ZipCode { get; set; }
    public List<Product> Products { get; set; }
}
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
}

View

@model MyModel
<form method="post">
    <input id="btnSubmit" type="submit" value="submit" />
</form>
<script src="~/Scripts/jquery-1.8.2.js"></script>
<script src="~/Scripts/knockout-2.2.0.js"></script>
<script src="~/Scripts/knockout.mapping-latest.js"></script>
<script type="text/javascript">

    var Product = function (Id, Name) {
        self = this;
        self.Id = Id;
        self.Name = Name;
    }

    var mapping = {
        'Products': {
            create: function (options) {
                return new Product(options.data.Id, options.data.Name);
            }
        }
    }


    function MyModel(data) {
        var self = this;
        ko.mapping.fromJS(data, mapping, self);
    }

    var viewModel = new MyModel(@Html.Raw(Json.Encode(Model)));

    $('#btnSubmit').click(function (event) {
        event.preventDefault();
        var theModel = ko.mapping.toJSON(viewModel);
        debugger;
        $.ajax({
            type: 'POST',
            url: "@Url.Action("Test", "Home")",
            data: theModel,
            contentType: 'application/json; charset=utf-8',
            dataType: 'json',
            success: function (result) {
                if (!result.success) {
                    //alert(result.error);
                }
                else { }
            }
        });
    });

    </script>
hutchonoid
  • 32,982
  • 15
  • 99
  • 104
  • I had the `dataType: 'json',` defined there as well, but that didn't work. I got it from this post: http://stackoverflow.com/questions/11050523/why-wont-my-submit-from-knockout-bind-to-my-mvc-model but that didn't work. And in this case it should not return anything. Just redirect back to the index page. I rather do a normal post in stead of an AJAX one. But couldn't find a good example on how to do that with `KnockoutJS`. – Quoter Dec 09 '13 at 18:25
  • Please try the rest of my suggestions too. I had a working example by following those steps. I know what u mean, a standard post back can be used but you have to know all the form settings that the model binder expects & make sure knockout keeps them in sync. – hutchonoid Dec 09 '13 at 18:29
  • All the js object values have to match. The final thing for me was the data type for the model binder to map it. – hutchonoid Dec 09 '13 at 18:30
  • They all match, I didn't touch any other object, just filling the Products `List<>`. But it's still not binding the model. Did what you said except for the mapping part. That doesn't work some how. But shouldn't matter for this case. – Quoter Dec 09 '13 at 18:40
  • First I had to change the js types to match. From your earlier question they differed & I used the same project to get it working. It wouldn't work until I set the data type though. That was the key part for me. It might be caching in the browser if you have followed the above & it's not working. Have u tried hard refreshing? – hutchonoid Dec 09 '13 at 18:58
  • Well I changed the model for the simplicity yea. That's why they differed. But Id/TypeId shouldn't matter. They are in sync thought. Is there another way to POST in stead of with AJAX? BTW, I have tried hard refresh. I do that constantly with every script change i make. – Quoter Dec 09 '13 at 19:22
  • Hi, I've updated with the full working example code that I used. I'm sure we'll get there. Good luck. :) – hutchonoid Dec 09 '13 at 20:11
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/42811/discussion-between-hutchonoid-and-quoter) – hutchonoid Dec 09 '13 at 20:23
0

After investigating some more with fiddler, it turned out that I was getting a 500 error with this message:

System.MissingMethodException: No parameterless constructor defined for this object.

After adding the parameterless constructor in the model, I still got the error message. This was caused because I had a few SelectList in my model. So that's why It couldn't be bound to the model.

Found the solution on this SO post (look for the answer of Chris S). Hope it helps others when facing this issue too.

Community
  • 1
  • 1
Quoter
  • 4,236
  • 13
  • 47
  • 69