Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you observe JavaScript hashtables in Knockout?

In my Knockout viewmodel, I've got some properties where I'm trying to make a hash observable. So instead of my pre-Knockout code of

self.MyHash = {};

I am now using:

self.MyHash = ko.observable({});

In other parts of my code, I am manipulating the hash with statements like these:

// add an entry
self.MyHash()["test"] = "My Value";

// remove an entry
delete self.MyHash()["test"];

The code works, in that the entries are added and removed properly. However, the changes to the hashtable don't seem to be detected by areas of the code that are observing it. For example, this computed observable never runs when I am changing the hashtable:

self.Querystring = ko.computed(function ()
{
    var f = [];
    $.each(self.MyHash(), function (k, v)
    {
        f.push(encodeURIComponent(k) + '=' + encodeURIComponent(v));
    });

    return (f.length > 0) ? f.join("&") : "";
});

I am going to guess that this is because Knockout observables are required to be simple variables (or observableArrays), and that it's not detecting the underlying changes to my hashtable.

If so, are there other options? Why isn't there an observableHash type in Knockout?

For what it's worth, my workaround is to have an observableArray of keys, and a regular JavaScript hashtable to lookup the values. Then I changed my computed method to observe the array of keys rather than the other hashtable variable I had before. I just want to make sure I'm not missing "The Right Way" to do it in Knockout.

self.MyHashKeys = ko.observableArray();
self.MyHash = {};

self.Querystring = ko.computed(function ()
{
    var f = [];
    $.each(self.MyHashKeys(), function (index, value)
    {
        f.push(encodeURIComponent(value) + '=' + encodeURIComponent(self.MyHash[value]));
    });

    return (f.length > 0) ? f.join("&") : "";
});
like image 311
Mason G. Zhwiti Avatar asked Apr 10 '12 00:04

Mason G. Zhwiti


1 Answers

See the second example on the observable array page. Just make an array of key/value pairs:

// This observable array initially contains three objects
var anotherObservableArray = ko.observableArray([
    { name: "Bungle", type: "Bear" },
    { name: "George", type: "Hippo" },
    { name: "Zippy", type: "Unknown" }
]);

In your examples you are only iterating (except for deletion), so there is no real need to use an actual dictionary. It would be easy enough to just search for the key. I think the use of map is kind of a premature optimization to some extent. Its also not entirely in line with the ability of query strings to support the same key multiple times.

Edit: if you want to observe the key or value changing in this example, you would also have to make those properties observable:

var anotherObservableArray = ko.observableArray([
    { name: ko.observable("Bungle"), type: ko.observable("Bear") }
]);
like image 186
Paul Tyng Avatar answered Oct 04 '22 22:10

Paul Tyng