Building a Reactive App with AngularJS and Firebase

Cleaver

I recently got a chance to work on a short project with a couple of pretty superb engineers at Hack Reactor. We came up with Cleaver, a cool way to choose a place to eat by eliminating unwanted restaurants or cuisines.

One thing that may not be immediately obvious from using Cleaver is that you can send the generated short URL to multiple people. Any changes that are made on an individual client view are immediately propagated to everyone else. To simulate this on a single machine, open a new tab, incognito mode, or another browser.

This functionality was key in our product vision, and using Firebase and AngularJS made building a reactive application really straightforward.

You may be familiar with AngularJS' two-way data binding, wherein any elements updated in Angular are immediately reflected in the DOM and vice-versa. Introducing Firebase changes the paradigm to three-way data binding, where any updates immediately propagate to each client's scope and, therefore, their DOM.

Firebase's excellent tutorial and documentation make getting started super simple. After creating an account with Firebase, we add the script dependencies:

<script src="https://cdn.firebase.com/js/client/2.0.4/firebase.js"></script>  
<script src="https://cdn.firebase.com/libs/angularfire/0.9.0/angularfire.min.js"></script>  

Then, we inject the services:

angular.module('cleaver.services', ['firebase'])  

Now we can add the data bindings:

    var setupFirebase = function(uniqueID) {
// Construct new Firebase reference
var fb = new Firebase('https://cleaverapp.firebaseio.com/sessions');  
// Get a reference to a specific subpath
var ref = fb.child(uniqueID + '/categoryVetoes');  
// Create a synchronized object with three-way data binding
data.categoryVetoes = $firebase(ref).$asObject();  

We placed the above code in a Factory, which is then used in our recommendation controller. Here's the glue:

    .factory('Rec', function($http, $location, $firebase) {
// Firebase and other code here
return {  
  data: data
}
})

.controller('MainController', function($scope, $state, Rec) {
  angular.extend($scope, Rec);
// Other stuff here
});

Now, our data.categoryVetoes object is available in Firebase, on the scope, and can be rendered and immediately updated in the DOM.

Firebase

Normally, this would be all that's required. However, in our case, categoryVetoes stores cuisines which have been "vetoed", so we actually ended up using it as input to a filter:

    <div class='ui segment restaurants' ng-repeat="restaurant in data.restaurants | orderBy:calculateScore | removeVetoes:data.restaurantVetoes:data.categoryVetoes">

Now, anytime a single client vetoes a cuisine, the data.categoryVetoes object is updated, propagated to Firebase, and to all other clients.

Valentyn Boginskey
Valentyn Boginskey

Valentyn is a system administrator turning web developer. He is passionate about privacy and virtual currencies. In his spare time, he enjoys mountain biking, skiing, backpacking, and racing go-karts.