knockoutFire icon indicating copy to clipboard operation
knockoutFire copied to clipboard

Best way to subscribe to individual changes in collection?

Open thinkloop opened this issue 12 years ago • 2 comments
trafficstars

In Firebase I am storing a bunch of keys/ids, but no real data. For example I store user IDs, but not user names. So when it comes time to display the user, I make another request to another service to populate the rest of the item. To get this to work, I need to know which individual item has changed in the collection. My first attempt was something like this:

// init knockoutfire
self.suggestions2({ collection: KnockoutFire.observable(recommenderFirebase.child('ratings/suggestions').child(user.id), {
        '.limit': 20,
       "$suggestion" : {
            accountID: true,
            itemID: true,
            relevance: true
        }
    })
});

// ******* TRYING TO SUBSCRIBE TO INDIVIDUAL ELEMENT CHANGES ********
self.suggestions2().collection.subscribe(function(newValue) {
    log(newValue.length);
    log(newValue[0]().itemID());
});

But this listens to the whole collection, returning all 20 items, instead of the individual item that was changed.

A horrible but functional way to solve this would be if I were willing to modify KnockoutFire itself, and populate the rest of the object on the native Firebase changes. So for example, KnockoutFire listens to "child_added" - if in that function I populated the object it would work (see bottom) :

firebaseRef.on("child_added", function(childSnap, prevChildName) {
    //var child = KnockoutFire.observable(childSnap.ref(), map[childVariable]);
    var childMap = map[childVariable];
    var child = KnockoutFire.mapObservable(childMap);
    if (childSnap.getPriority()) {
        child()[".priority"] = childSnap.getPriority();
    }
    child.extend({firebaseObject: {firebaseRef: childSnap.ref(), map: childMap}});
    //console.log(childSnap.ref().path.toString() + ":" + child().firebase);
    self.insert(child, prevChildName, map[".reverse"]);
    self().last(child());

    // ******* POPULATE OBJECT ********
    outsideService.populate(child());
});

Of course I don't want to do that. So whats's the best way to listen to changes to individual elements in the collection, and not the whole collection itself?

thinkloop avatar Apr 27 '13 22:04 thinkloop

I'm not sure why I didn't think of this originally, but I can simply subscribe directly to the underlying Firebase independent of KnockoutFire:

recommenderFirebase.child('ratings/suggestions').child(user.id).limit(20).on('child_added', function(data) {
    log(data.val());
});

thinkloop avatar Apr 27 '13 23:04 thinkloop

I think ".extend" map option may work.

ko.extenders.suggestion = function(target) {
  outsideService.populate(target());
};

then specify the extender.

"$suggestion" : {
  ".extend": {"suggestion": null},
  ...
}

hiroshi avatar Apr 28 '13 13:04 hiroshi