C# – How to convert JS Object to JSON using knockoutjs utility to send the json data to server in asp.net mvc

asp.net-mvcasp.net-mvc-3cknockout.js

I am using MVC4 and trying knockoutjs library with it. The form submits the data to the controller if I use the traditional Submit click without using Knockoutjs library.
I am using knockoutjs mapping plugin to convert the viewmodel from the server to create a client-side viewmodel and then trying to extend it client-side.

  • To convert from server-side to client-side vm, I'm using ko.mapping.fromJS(model);
  • To post the data back to the server, I'm converting it back by ko.toJSON(model) while sending via ajax using jQuery.

The data that I receive on server is null. Also, when I log ko.toJSON(model) in console, I get the following:

{
    "FirstName": "foo",
    "LastName": "foo1",
    "Address": "United Kingdom",
    "Age": 22,
    "__ko_mapping__": {
        "ignore": [],
        "include": ["_destroy"],
        "copy": [],
        "observe": [],
        "mappedProperties": {
            "FirstName": true,
            "LastName": true,
            "Address": true,
            "Age": true
        },
        "copiedProperties": {}
    }
}

It seems I am not doing right while converting the js object back to json format to send the data to the server.
Below is all my code:

Controller Code:

public class PersonController : Controller
    {
        PersonViewModel viewModel = new PersonViewModel();
        //
        // GET: /Person/
        [HttpGet]
        public ActionResult Index()
        {

            return View(viewModel);
        }

        [HttpPost]
        public ActionResult Index(PersonViewModel viewModel)
        {
            if (ModelState.IsValid)
            {

            }
            return View(viewModel);
        }
//
    // GET: /Person/LoadData/

    public ActionResult LoadData()
    {
        viewModel = new PersonViewModel() { FirstName = "foo", LastName = "foo1", Age = 22, Address = "United Kingdom" };
        return Json(viewModel, JsonRequestBehavior.AllowGet);
    }
}

ViewModel at Server:

 public class PersonViewModel
    {
        [Required]
        public string FirstName { get; set; }
        [Required]        
        public string LastName { get; set; }
        [Required]
        public string Address { get; set; }
        [Required]
        public int Age { get; set; }
    }

ViewModel at client-side – JavaScript :

var Person = function () {
    var self = this;


    self.SetupKOBindings = function () {
        var source = null;
        this.GetViewModelFromServer = function () {
            $.ajax(
                {
                    url: "/Person/LoadData",
                    type: "GET",
                    async: false
                }).
            success(function (data) {
                source = data;
            });
        }();
        return ko.mapping.fromJS(source);
    };

    self.ViewModel = function () {
        this.model = self.SetupKOBindings();

        this.model.Save = function () {
            console.log(ko.toJSON(model));
            $.ajax(
               {
                   url: "/Person/Index",
                   type: "POST",
                   data: ko.toJSON(model),
                   async: true
               }).
           success(function (data) {

           });
        }

        return this.model;
    };

    self.ApplyKOBindings = function (vm) {
        ko.applyBindings(vm);
    };

    return this;
};

$(function () {
    var PersonPage = Person();
    var viewModel = PersonPage.ViewModel();
    PersonPage.ApplyKOBindings(viewModel);
});

HTML in .cshtml at server:

<form action="" method="post">
        <div>
            First Name:
            @Html.TextBoxFor(model => model.FirstName, new { data_bind = "value: FirstName,valueUpdate:['afterkeydown','propertychange','input']" })<br />
        </div>
        <div>
            Last Name:
                    @Html.TextBoxFor(model => model.LastName, new { data_bind = "value: LastName,valueUpdate:['afterkeydown','propertychange','input']" })<br />
        </div>
        <div>
            Address:
                    @Html.TextBoxFor(model => model.Address, new { data_bind = "value: Address" })<br />
        </div>
        <div>
            Age:
                    @Html.TextBoxFor(model => model.Age, new { data_bind = "value: Age" })<br />
        </div>
        <input type="submit" value="Save" data-bind="click: Save"/>

    </form>

Best Answer

If you are using the mapping plugin you should use ko.mapping.toJSON(model) instead of ko.toJSON(model) because this removes "ko_mapping" properties.

But your main problem is that because you are sending JSON you need to tell in your request that you are sending JSON otherwise ASP.NET MVC cannot parse the request on the server side. For this you need to set the contentType property to "application/json":

So the following $.ajax call should work:

$.ajax({
    url: "/Person/Index",
    type: "POST",
    data: ko.mapping.toJSON(model),
    async: true,
    contentType: "application/json"
}).success(function (data) {

});