I'm looking for the best way to initialize a knockout observable array from some server data (ViewBag), and I want the array contents to be of a javascript type I have defined. Without the requirement of the JS type I could just use:
materialVarieties: ko.observableArray(@Html.Raw(Json.Encode(ViewBag.Materials)))
but I also have a material JS type that I want to use so I can have some extra ViewModel specific properties and functions i.e.:
var material = function(id, name) {
this.id = id;
this.name = name;
this.selected = ko.observable(false);
this.select = function()
{
jQuery.each(processViewModel.materials(), function(index, item)
{
item.selected(false);
});
this.selected(true);
}
}
And then the required initialization bees:
materialVarieties: ko.observableArray([new material(1, "Apricot"), .....
Currently I build up a string from the ViewBag data and then render that as the initializer like this:
@{ var items = string.Join(",",
((IEnumerable<MaterialVariety>) ViewBag.Materials)
.Select(m => string.Format("new material({0}, {1})",
Json.Encode(m.Id), Json.Encode(m.Name)))); }
var processViewModel = {
material: ko.observableArray([@Html.Raw(items)])
But I'm wondering if there is a cleaner way than the string.Join
bit. I could wrap it up in a Helper. What do you do?
I'm looking for the best way to initialize a knockout observable array from some server data (ViewBag), and I want the array contents to be of a javascript type I have defined. Without the requirement of the JS type I could just use:
materialVarieties: ko.observableArray(@Html.Raw(Json.Encode(ViewBag.Materials)))
but I also have a material JS type that I want to use so I can have some extra ViewModel specific properties and functions i.e.:
var material = function(id, name) {
this.id = id;
this.name = name;
this.selected = ko.observable(false);
this.select = function()
{
jQuery.each(processViewModel.materials(), function(index, item)
{
item.selected(false);
});
this.selected(true);
}
}
And then the required initialization bees:
materialVarieties: ko.observableArray([new material(1, "Apricot"), .....
Currently I build up a string from the ViewBag data and then render that as the initializer like this:
@{ var items = string.Join(",",
((IEnumerable<MaterialVariety>) ViewBag.Materials)
.Select(m => string.Format("new material({0}, {1})",
Json.Encode(m.Id), Json.Encode(m.Name)))); }
var processViewModel = {
material: ko.observableArray([@Html.Raw(items)])
But I'm wondering if there is a cleaner way than the string.Join
bit. I could wrap it up in a Helper. What do you do?
- 1 Why not create a HtmlHelper extension method? Take a generic argument and return the encoded output. – Anuj Commented Aug 13, 2011 at 1:59
- @Anuj Yeah thats what I was thinking ('I could wrap it up in a Helper'), it'd also have to have a string representing the JS type. Problem es when working out the properties to use in the JS constructor (and the order required). I can reflect the generic argument to get the properties, but I was hoping to use my server side Models (don't want another set of server side VMs) and they have other properties that I don't need in my JS. And that still leaves me the problem of the constructor arg order.....unless I am missing something? – Simon Fox Commented Aug 13, 2011 at 2:06
1 Answer
Reset to default 15I would typically serialize the array first, then map it when putting it in the view model. Would be like:
var originalVarieties = @Html.Raw(Json.Encode(ViewBag.Materials))
var processViewModel = {
materialVarieties: ko.observableArray(ko.utils.arrayMap(originalVarieties, function(variety) {
return new material(variety.id, variety.name);
}))
}
Requires a minor amount of additional processing on the client-side, but seems cleaner than building strings.