I'm using knockout mapping to help map a serverside object into JSON. I have an object with numerous collections in it so I don't want to have to recreate and map each piece manually in javascript. So, I'm using knockout-mapping to do this for me.
I was having issues, so I decided to try it with a simple example, so here is what I have for an ASP.NET MVC application:
C# Model:
public class Vaccinations
{
public string Vaccination { get; set; }
public System.DateTime VaccinationDate { get; set; }
}
public class Dog
{
public string Name { get; set; }
public int Age { get; set; }
public Dog()
{
this.Vaccinations = new System.Collections.Generic.List<Vaccinations>();
}
public System.Collections.Generic.List<Vacinations> Vacinations { get; set; }
}
As you can see, each Dog has a list of vaccinations they may or may not have.
In my controller, I create and return a pre-populated Dog object:
public ActionResult Load()
{
Dog rambo = new Dog
{
Name = "Rambo",
Age = 5
};
rambo.Vacinations = new System.Collections.Generic.List<Vacinations> {
new Vacinations { Vacination = "Rabies", VacinationDate = DateTime.Now.AddYears(-1) },
new Vacinations { Vacination = "Mumps", VacinationDate = DateTime.Now.AddYears(-2) }
};
return Json(rambo, JsonRequestBehavior.AllowGet);
}
In my view (Index.cshtml), I have it set up to show the dog and a list of it's vaccinations. I want to allow the user to click on an Add Vaccination button to add a new line to the collection and allow them to enter the data.
Again, I'm using knockout.mapping to do the mapping for me. In the javascript section, this is what I have:
var ViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, {}, self);
self.isValid = ko.computed(function () {
return self.Name().length > 3;
});
// Operations
self.save = function () {
$.ajax({
url: "Dog/Save",
type: "post",
contentType: "application/json",
data: ko.mapping.toJSON(self),
success: function (response) {
alert(response.Status);
}
});
};
self.addVaccination = function () {
self.Vaccinations.push(new self.Vaccination()); // <--- This doesn't work and I know why, so how do I do this?
}
};
$(function () {
$.getJSON("Dog/Load", null, function (data) {
ko.applyBindings(new ViewModel(data));
});
});
My question revolves around the "addVaccination" function that I've added to the ViewModel object. How do I specify a new "Vaccination" object without having to "code" one in Javascript? That was the entire reason for me using knockout mapping so I don't have to do that. But, I don't see any other way around it.
Is there a way to access the base Vaccination object from the Vaccinations observable array so I can create a new one?
And then the final question is, how to I pass this back to my controller? I'm not sure if this will work or not.
You can't directly. But what you can do is define a Vaccination instance at the server side and return it as a the default instance.
So, you need to return the old data and the default instance.
public ActionResult Load()
{
...
var data = new {
defaultVacination = new Vacination(),
rambo = rambo,
};
return Json(data , JsonRequestBehavior.AllowGet);
}
And on the client side you receive the same data and the default instance.
var ViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data.rambo, {}, self);
var defaultInstance = data.defaultVacination;
...
self.addVaccination = function () {
// clone the default instance.
self.Vaccinations.push(ko.utils.extend({}, defaultInstance));
}
I hope it helps.