Monthly Archives: May 2012

Automatic mapping with Knockout.js, the mapping plugin and ASP.NET MVC 3

Expected outcome

  • Having a C# Dog class with name and age-properties.
  • When the page loads, it automatically loads the dog using jQuery ajax (serialized as json) and creates a knockout viewmodel.
  • ViewModel should automatically add properties from the C# model.
  • The knockout ViewModel should be able to have extra computed methods for validating form for example.
  • The ViewModel should be able to save the dog using ajax.

Installing components

Install Knockout.Mapping using NuGet, this will automatically install Knockout.

Create Model

First, create the dog, a simple C# plain object with two properties: Name and Age

public class Dog
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Create MVC Controller

Then create a empty DogController and add a empty Index method. The Load method just creates a simple instance of a dog and returns it as Json, and finally the Save method only accepts post data and just returns a formatted string in Json-format containing the updated Dog

public class DogController : Controller
{
    public virtual ActionResult Index()
    {
        return View();
    }

    public ActionResult Load()
    {
        return Json(new Dog { Name = "Rambo", Age = 5 }, JsonRequestBehavior.AllowGet);
    }

    [HttpPost]
    public ActionResult Save(Dog dog)
    {
        return Json(new { Status = string.Format("Success, saved {0} with age: {1}", dog.Name, dog.Age) });
    }
}

Create View

Create empty Index View and add script-references

<script src="@Url.Content("~/Scripts/knockout.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/knockout-mapping-latest.js")" type="text/javascript"></script>

Then you add a form containing the knockout bindings bound to the observables on the ViewModel. Two input boxes data bound to Name and Age, the submit-action to “save”, a function on the JavsScript ViewModel, and finally data-bind the submit button so it’s enabled only if the model is valid.

<form method="POST" data-bind="submit: save">
    <div>Name:</div>
    <div><input type="text" data-bind="value: Name" /></div>

    <div>Age:</div>
    <div><input type="text" data-bind="value: Age" /></div>

    <div><input type="submit" value="Save" data-bind="enable: isValid()" /></div>
</form>

Create ViewModel

Create a class in JavaScript, taking JSON data to be mapped as parameter, and add isValid as a computed property and finally a save function that posts the JSON-data back to the DogController’s Save method.

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

	self.isValid = ko.computed(function () {
		return self.name().length > 0;
	});

	self.save = function () {
		$.ajax({
			url: "Knockout2/Save",
			type: "post",
			contentType: "application/json",
			data: ko.mapping.toJSON(self),
			success: function (response) {
				alert(response.Status);
			}
		});
	};
};

Load initial data

Finally you add a JavaScript document ready handler that loads the initial data. Place it below the ViewModel.

$(function () {
    $.getJSON("Dog/Load", null, function (data) {
        ko.applyBindings(new ViewModel(data));
    });
});

Conclusion

This is probably not the best way, and of course there are lots of stuff you should do. For example, move javascript references to bottom of page, validate the postback and more. But I tried to keep the example as simple as possible.

Download the project here:

SimpleKnockoutExample.zip (456.44 kb)