The most common Angular and Firebase code smell, and how to fix it

David East
Mar 16, 2016 · 3 min read

You want clean Angular and Firebase code? Here’s my Golden Rule of AngularFire, do not unwrap promises yourself.

The single biggest code smell when building an AngularFire app is using the $loaded() promise.

// WARNING: This is an anti-pattern, don't copy and paste this
// and use it in your own app
function MyController($firebaseArray, itemsRef) {
var syncArray = $firebaseArray(itemsRef);
console.log(syncArray); // empty array at first
// it's like peeling an onion
syncArray.$loaded().then(function(items) {
this.items = items; // populated array
}.bind(this));
}

What does it mean to “not unwrap a promise?” It means to avoid calling .then() or .catch(). But, how do you get the data without invoking .then()?

To find out, take a look at using a $firebaseArray().

Why $loaded() smells bad

var syncArray = $firebaseArray(itemsRef);
console.log(syncArray); // empty array syncArray.$loaded().then(function(items) {
console.log(items); // populated array
console.log(items === syncArray); // true
});

The .then() function is a code smell, and here’s why:

The desire to use $loaded() is natural, because it sounds like what you want. Give me the data once the it has downloaded from the server. While this seems useful, it’s not.

You should never need $loaded() in a controller.

This is because $firebaseArray() and $firebaseObject() are wicked smart. When the data is downloaded, they trigger Angular’s digest loop which then updates the view.

By using $loaded() you’re writing bulky code that works the same as a one-liner.

// Same result as the anti-pattern in the first code snippet
function MyCtrl($firebaseArray, itemsRef) {
this.items = $firebaseArray(itemsRef);
}

So where should $loaded() be used? It works beautifully with the router.

$loaded() and the Router

function MyCtrl($firebaseArray, itemsRef) {
// this works and smells fresh
this.items = $firebaseArray(itemsRef);
// we can’t do this even though it would smell great
// this.first = this.items.$keyAt(0);

// so we have to do this, which smells like Limburger cheese
this.items.$loaded().then(function(data) {
this.first = data.$keyAt(0); // data is available
}.bind(this));
}

We’re stuck with $loaded(), again.

So what’s the solution? Make the data available from the get-go by using the router.

Plunker sample.

$routeProvider.when('/', {
controller: 'MyCtrl as ctrl',
templateUrl: 'view.html',
resolve: {
items: function($firebaseArray, itemsRef) {
// load the view, when the data is available
// this is where $loaded() belongs
return $firebaseArray(itemsRef).$loaded();
}
}
});

The resolve object is your best friend. Every time you resolve a promise in the router, an angel gets its wings.

This right here, is how you abide by the Golden Rule.

Don’t unwrap promises yourself, let Angular do it for you.

Using resolve, Angular will unwrap the promise for you. When Angular invokes the controller, it’ll also inject the resolved data into the constructor.

function MyCtrl(items) { // items is injected by using resolve
// data is 100% available and smells minty fresh
this.items = items;

// the first item is available as well, so fresh and so clean
this.first = this.items.$keyAt(0);
}

The Takeaway

It’s worth pointing out that $loaded() works as a great debugging tool. If you need to inspect the downloaded data set or any errors, this is a great place to do so.

Take some time to read the docs on the Firebase website. You’ll likely save more time in debugging down the road.

How do you use $loaded(), and what are some other code smells you’re aware of?

Google Developers

Engineering and technology articles for developers, written…

Google Developers

Engineering and technology articles for developers, written and curated by Googlers. The views expressed are those of the authors and don't necessarily reflect those of Google.

David East

Written by

Developer Advocate at Google. Working on the Firebases.

Google Developers

Engineering and technology articles for developers, written and curated by Googlers. The views expressed are those of the authors and don't necessarily reflect those of Google.