Is there a better (built in?) way to mix observableArray and associative arrays?
viewModel = {
a: ko.observableArray(),
a_assoc: {},
add_item: functi
Typically, you would do something like this in Knockout:
var viewModel = {
a: ko.observableArray(["a","b","c","d"]),
add_item: function() {
this.a.push("new" + this.a().length);
}
};
viewModel.a_assoc = ko.dependentObservable(function() {
var result = {};
ko.utils.arrayForEach(this.a(), function(item) {
result[item] = 1;
});
return result;
}, viewModel);
So, you have a dependentObservable that maps your array to an object. Note that each time that the original array is updated, the object is rebuilt. So, it is less efficient than the method in your post, but unless your object is substantially big, it is doubtful that it would cause a performance issue.
Sample here: http://jsfiddle.net/rniemeyer/PgceN/
I think it is best to use an associative array under the hood for performance. You also (as you alluded to) need to use an observableArray to be able to track changes (because that's how knockout works).
If you're seeking a 'knockout friendly' associative array you probably want a way to track changes to the item (and thus affect dependency chains).
http://jsfiddle.net/rz41afLq/2/
Here's what I came up with that suited my needs. Basically you store a key value pair in the observableArray
and use a standard associative array to store a copy for lookup purposes.
// VIEW MODEL
var viewModel = {
// we have to use observable array to track changes
items: ko.observableArray([]),
// also use associative array for performance
_assoc: {},
update_item: function(key, value) {
// use a true assoc array for performance
// _assoc[key] = ko.observable(value)
if (viewModel._assoc[key])
{
// update observable (overwriting existing item)
viewModel._assoc[key].value(value);
}
else {
// Important: See how we wrap value with an observable to detect changes
this.items.push(viewModel._assoc[key] = { key: key, value: ko.observable(value) });
}
}
};
So you save your dogs like this:
update_item('dogs', ['kim', 'abbey', 'goldie', 'rover']);
update_item('dogs', ko.observableArray(['kim', 'abbey', 'goldie', 'rover']));
(The second is only required if you are planning on calling pop()
or push()
on the list of dogs and want to update the UI. You can of course call update_item
to completely update the item at any time in which case it doesn't need to be observable)
To lookup a value, you dynamically create a computed to go fetch it :
var dogs = getValue('dogs');
Then as soon as the value indexed as 'dogs' in the associative array changes then the observable dogs()
will update (and can be chained). This dogs
observable can be bound to the UI of course
var getValue = function(key) { return ko.pureComputed(function()
{
// reevaluate when item is added or removed to list
// not necessary if the value itself changes
viewModel.items();
var item = viewModel._assoc[key];
return (item == null) ? null : item.value();
}, viewModel);