-
-
Save evanrmurphy/ced9358cff14ea3ae2a8 to your computer and use it in GitHub Desktop.
Computed observable arrays for Knockout
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| define([ | |
| 'underscore', | |
| 'knockout' | |
| ], function(_, ko) { | |
| // Example usage: | |
| // | |
| // function alphabeticalComputeFn = function(metadataOA) { | |
| // function getName(metadatum) { return metadatum.name(); } | |
| // return _.sortBy(metadataOA(), getName); | |
| // }); | |
| // | |
| // var metadataOA = metadataCollection.accessor()._instances; | |
| // var alphabeticalMetadataCOA = | |
| // computedObservableArray(metadataOA, alphabeticalComputeFn); | |
| function computedObservableArray(OA, computeFn) { | |
| var persistingCOA = ko.observableArray(); | |
| return ko.computed(function() { | |
| var newCOA = computeFn(OA); | |
| mergeObservableArrays(persistingCOA, newCOA); | |
| return persistingCOA; | |
| }); | |
| } | |
| function mergeObservableArrays(destinationOA, sourceOA) { | |
| var itemsToRemove = _.difference(destinationOA(), sourceOA()); | |
| var itemsToAdd = _.difference(sourceOA(), destinationOA()); | |
| function sourceOAIndex(item) { return _.indexOf(sourceOA(), item); } | |
| var indexedItemsToAdd = _.indexBy(itemsToAdd, sourceOAIndex); | |
| function removeItem(item) { destinationOA.remove(item); } | |
| _.each(itemsToRemove, removeItem); | |
| function addItem(item, index) { destinationOA.splice(index, 1, item); } | |
| _.each(indexedItemsToAdd, addItem); | |
| } | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
so there's this pretty big motivating use case that this doesn't support. consider a set of subscribers:
and we've got a
SubscriberCollectionwith instances ofSubscriberModels that have an observable for theidandmsisdn.so then we'll want a table of these somewhere, and that view is going to want to display pretty phone numbers. we want a computed for a nicely formatted number, but that's a view concern, so we don't want to stick that on the model (in reality this is a useful
SubscriberModelproperty, but there will be more specific, view-only cases of stuff like this, so just humor me here).what we want is to bind the table rows to something like this:
this is pretty much what you've got (except it's only for mapping, not sorting/filtering). it's nice because:
subscribersCollection()or one of the models is updated, the UI is updatedthe problem is it re-renders the entire table every time
subscribersCollection()changes. this is a perf problem (we encountered it on the stream), but more importantly if those rows contain any view state (say an input on each row that allows you to make updates inline), then those are blown away by just adding a row. this is the difference between using ko'sforeachbinding w/ an OA and a POJO array.does this make sense? if we standardize a COA implementation, it needs to cover this case (we've already got 2 examples of it in the code).
also note that if we do cover this case, we need some way of determining if an item in the mapped array corresponds to one in the original. in your example we can just check if objects equal eachother, in mine we'd need to compare
.id()s, and in the existing examples in the code we assume the.data()field of the mapped array contains the original object, so we just test the equality of that.effin complicated, i kno...