I am developing an application with Firebase (1.0) and Angular (1.4). The problem I\'m having is to ensure the data in view are synchronised with Firebase, while fetching denorm
Brandon's answer is technically-correct answer to the posed question. I'm going to elaborate a bit on what would be a better way to join these records.
I've actually answered this exact question in a fiddle, and also provided a more sophisticated, elegant, and simpler solution of how to cache and merge user profiles into objects. I'll reiterate the details of how this works here.
app.factory('NormalizedPosts', function($firebaseArray, userCache) {
var PostsWithUsers = $firebaseArray.$extend({
// override $$added to include users
$$added: function(snap) {
// call the super method
var record = $firebaseArray.prototype.$$added.call(this, snap);
userCache.$load( record.user ).$loaded(function( userData ) {
record.userData = userData;
});
// return the modified record
return record;
}
});
return PostsWithUsers;
});
Here I've decided to use a cached list of users, since they are likely to be highly redundant, and this provides an elegant way to keep everything in sync. It's not strictly necessary--we could look them up right there in $$added, but that leaves some edge cases to be handled. So a cache of synchronized users feels right here.
So here's the caching utility:
app.factory('userCache', function ($firebase) {
return function (ref) {
var cachedUsers = {};
// loads one user into the local cache, you do not need to
// wait for this to show it in your view, Angular and Firebase
// will work out the details in the background
cachedUsers.$load = function (id) {
if( !cachedUsers.hasOwnProperty(id) ) {
cachedUsers[id] = $firebaseObject(ref.child(id));
}
return cachedUsers[id];
};
// frees memory and stops listening on user objects
// use this when you switch views in your SPA and no longer
// need this list
cachedUsers.$dispose = function () {
angular.forEach(cachedUsers, function (user) {
user.$destroy();
});
};
// removes one user, note that the user does not have
// to be cached locally for this to work
cachedUsers.$remove = function(id) {
delete cachedUsers[id];
ref.child(id).remove();
};
return cachedUsers;
}
});
And here's a gist putting it all together.
Note that, if we know that when our controller is destroyed, that the data will no longer be useful, we can clean up the listeners and memory by calling $destroy
. This isn't strictly necessary and could be a premature optimization, but is probably worth mentioning for users with complex production apps that have tens of thousands of records to track:
app.controller('...', function(NormalizedPosts, userCache, $scope) {
$scope.posts = new NormalizedPosts();
$scope.$on('$destroy', function() {
$scope.posts.$destroy();
userCache.$dispose();
});
});