One of the really cool features of Knockout js is the ability to take a raw json object, say from a web endpoint, and map it to an observable. This is done via the mapping plug-in.
Take a look at the code below for an example
$.get(url)
.done((data) => {
var temp = self.ToDos();
_.each(data, (item) => {
var toDoVM = ko.mapping.fromJS(item);
temp.push(toDoVM);
});
self.ToDos.valueHasMutated();
});
The magic above is the line ko.mapping.fromJS(item), this is what will take my json object and convert it to an observable. Proof is below.
Below is the output via the Chrome Dev tools of our ‘item’ prior to mapping. Notice we have raw properties.
Here is the output after the mapping, notice how now we have functions rather than properties. This tells me I have a knockout observable.
There is one little issue here in the mapping above. The issue is that the resulting type from the mapping is ‘object’. Now this could be 100% ok but what if you want to custom object type for your binding. Maybe you have computed properties you want to use as well. Fortunately you can accomplish this via Knockout as well.
Assume I have this custom type as seen below (this is using TypeScript
export class ToDoListItemViewModel {
public Id: KnockoutObservableNumber = ko.observable(0);
public Task: KnockoutObservableString = ko.observable("");
public DueDate: KnockoutObservableString = ko.observable("");
public ReminderDate: KnockoutObservableString = ko.observable("");
public Priority: KnockoutObservableString = ko.observable("");
public Category: KnockoutObservableString = ko.observable("");
public DisplayDueDate: KnockoutComputed = ko.computed(() => { return "aaa" });
constructor() {
this.DisplayDueDate = ko.computed(() => {
var displayDate = "";
if (this.DueDate && this.DueDate() != "") {
displayDate = moment(this.DueDate()).format('L');
}
return displayDate;
});
}
}
How could I use Knockout mapping to returned a typed observable?
Turns out it is really simple. The code below shows how
$.get(url)
.done((data) => {
var temp = self.ToDos();
_.each(data, (item) => {
var toDoVM = ko.mapping.fromJS(item, {}, new ToDoListItemViewModel());
temp.push(toDoVM);
});
self.ToDos.valueHasMutated();
});
The ONLY difference in this code is in the ko.mapping.fromJs line. Notice how I am passing in 2 extra arguments. The 2nd argument is the one I want and this is the ‘target’ output type.
Now when I look at the output in the debugging tools I get what I want, a typed object being returned
So the trick to returned a typed observable is to use the overload of the .fromJS method.
Till next time,
Posted
03-06-2013 5:28 AM
by
Derik Whittaker