8

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 becomes:

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?

2
  • 1
    Why not create a HtmlHelper extension method? Take a generic argument and return the encoded output. 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 comes 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? Commented Aug 13, 2011 at 2:06

1 Answer 1

15

I 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.

Sign up to request clarification or add additional context in comments.

2 Comments

ah, that looks good. I am new to Knockout and was unaware of utils.arrayMap. Thank you.
yep, there is nothing special about ko.utils.arrayMap. Pretty simple code. jQuery map would do the same.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.