I have a ko.computed() function that proxies a collection like this:
function ListView(query) {
var self = this;
this.query = query;
this.items = ko.observableArray([]);
ko.computed( function() {
self.items( self.query() == null ? [] : self.query().postings() );
}
// more code
}
Unfortunately, this causes the self.items() variable to change with every change to every object in the postings() array. What I would rather do is have a computed value items represent the query’s postings directly. But if I do this:
self.items = ko.computed(function() { return self.query().postings(); });
the value becomes a scalar, not an array, as far as Knockout is concerned. The reasons this matters is that as some of the items in the list change state, I would like to update just those parts of the display, rather than re-rendering the entire list. This is not just a performance optimization (although with hundreds of items performance is also an issue), but also I am trying to log which items are currently being displayed to keep track of what the user might have seen.
I suppose logically, I would like to have something like this:
self.items = self.query().postings;
but this breaks if the query changes because (I think) the dependencies exist on a different object.
Is there a clean way to build a dependency mechanism and still preserve collection semantics?
EDITED: 13 May 2012
I tried to implement the extension approach suggested by @MichaelBest, but that produced the following error:
Uncaught TypeError: Object function dependentObservable() {
if (arguments.length > 0) {
set.apply(dependentObservable, arguments);
} else {
return get();
}
} has no method 'valueWillMutate'
ko.utils.arrayForEach.ko.observableArray.fn.(anonymous function) knockout-2.1.0.debug.js:1087
SubviewModel.self.refresh.self.doSort SummaryViewModel.js:73
ko.ignoreDependencies knockout-deferred-updates.js:172
subFnObj.(anonymous function).newCallback knockout-deferred-updates.js:188
ko.subscribable.fn.notifySubscribers knockout-2.1.0.debug.js:870
ko.utils.arrayForEach knockout-2.1.0.debug.js:85
ko.subscribable.fn.notifySubscribers knockout-2.1.0.debug.js:866
evaluateImmediate knockout-deferred-updates.js:300
evaluatePossiblyAsync knockout-deferred-updates.js:246
ko.subscribable.fn.notifySubscribers knockout-2.1.0.debug.js:870
ko.utils.arrayForEach knockout-2.1.0.debug.js:85
ko.subscribable.fn.notifySubscribers knockout-2.1.0.debug.js:866
ko.observable.observable.valueHasMutated knockout-2.1.0.debug.js:946
observable knockout-2.1.0.debug.js:934
updateViewModel knockout.mapping-latest.debug.js:514
changes knockout.mapping-latest.debug.js:389
visitPropertiesOrArrayEntries knockout.mapping-latest.debug.js:569
updateViewModel knockout.mapping-latest.debug.js:374
ko.mapping.fromJS knockout.mapping-latest.debug.js:91
Topic.load querium.js:1212
...
You can create a computed observable that returns an array and acts like an
observableArray:Now you can use all the functions that an
observableArrayhas withitems.Edit: This approach will only work if you also add
valueWillMutateandvalueHasMutatedfunctions to the object:And this will also only work if you use the debug version of Knockout, because in the minified version, the function names will be compressed.