Easily Add a Back End to Your Angular App With Firebase

As a client-side framework, Angular alone is not enough to build a full back-end webapp. Often times, it’s difficult to know when to sync our data with the back-end, and how to handle the changes and potential conflicts of data between versions of modified content.

Imagine we have two instances of our application running at the same time. What if both instances are trying to edit the same data? Without handling this case, we can get into trouble, especially if we’re building the front-end for a complex web application, like a bank.

Using Firebase we can easily add a backend to our Angular app. Featured on the Angular.js home page, Firebase is quickly becoming the standard for Angular persistence.

Firebase is a real-time back-end for building collaborative, modern applications. Rather than requiring us to focus on building custom request-response models with a server-side component where we manually worry about data-synchronization, Firebase lets us get our app up and running in minutes. We can build a data-backed webapp entirely in Angular that can scale out-of-the-box and update all clients in real-time.

Even more, data stored in Firebase is standard schema-less JSON, which makes it incredibly easy to save data models of any type into Firebase. If a device loses network connection, Firebase continues to allow access to locally cached data and seamlessly synchronizes changes with the cloud when the device comes back online.

The Firebase client libraries and REST API provide easy access to that data from any platform. Although we’re focusing specifically on Angular, this means native apps or other server-side apps can reach the data saved by Angular.

3-Way Data Binding With Firebase and Angular

With Angular, rendering content to the browser is elegant and easy. Storing and retrieving data, the other major component to production-level webapps, is handled elegantly by Firebase. This makes Firebase an excellent partner to Angular.

Angular is great with its two-way data binding between models in JavaScript and the DOM. By syncing our Angular model with Firebase, our app’s data in the model is synchronized in real-time across all clients. That means that when data changes in one client, these updates are immediately persisted to Firebase and rendered across all connected devices.

When data is updated in any of the three places (View, Model, or Firebase), the changes propagate in real-time to the other two across all clients.

Getting Started With AngularFire

It’s easy to create real-time web applications using Firebase and Angular thanks to the official Angular library: AngularFire. The AngularFire bindings were built by the Firebase team specifically for integration with Angular applications, as we’ll see.

There are only four steps to backing our Angular app on Firebase using AngularFire:

1. Sign up and Create a Firebase

Before we can actually save or retrieve any data from Firebase, we’ll need an account. It’s free to create an account, so let’s sign up.

First, head to firebase.com and click on the Sign up button (or login if you already have an account):

Now that we have signed up for an account, we can create our first Firebase. The name we choose will be part of the URL we’ll use to reference our Firebase data.

For example, when we create a Firebase with the name of ng-newsletter, our Firebase URL will be available at https://ng-newsletter.firebaseio.com.

1
https://<my-firebase-name>.firebaseio.com/

The AngularFire bindings let us associate a Firebase URL with a model or collection of models. These models will represent the data that will be transparently kept in sync across all clients currently using our app.

Angular’s two-way data binding keeps the DOM synced to JavaScript variables in memory, and Firebase stores those changes and sends them to all listening clients in real-time.

We get this data synchronization without changing how we build our angular app. Very cool!

2. Include the Firebase and AngularFire libraries

Using AngularFire is as simple as including two JavaScript files in our HTML file, one for Firebase and another for AngularFire.

We’ll need to use Firebase’s CDN, so at the top of our index.html file we’ll add the following two lines:

1
2
<script src="https://cdn.firebase.com/v0/firebase.js"></script> <script src="https://cdn.firebase.com/libs/angularfire/0.5.0-rc1/angularfire.js"></script>

3. Add Firebase as a dependency

As usual with any application libraries, we’ll need to set the firebase library as a dependency for our module. This tells the rest of our application that we can use the firebase bindings in our app:

1
angular.module("myapp", ["firebase"]);

4. Bind a model to a Firebase URL

By declaring Firebase as a dependency, we now have access to the $firebase service, which allows us to inject it as a dependency in our controllers and services.

1
2
3
4
5
6
angular.module('myapp', ['firebase']) .controller("MyCtrl", ["$scope", "$firebase", function($scope, $firebase) { // Our controller definition goes here } ]);

The $firebase service takes a single argument: a Firebase reference.

FirebaseRef (Firebase reference)

The Firebase reference tells $firebase where the data is stored and how to connect. The $firebase service handles the synchronization with Angular, and is where we’ll call methods to save our changes.

This object has several methods we’ll use to interact with our remote data. These methods, detailed below are all prefixed with the $ symbol, such as $add() and $save() and are available on this object.

Note that no changes to the object will result in changes made to the remote data.

To synchronize a local object model to the remote Firebase reference, we’ll use the service method and pass it an instance of the Firebase object. For example, to synchronize the $scope.items model to our ng-newsletter items, we’ll run the following method:

1
2
3
4
5
6
7
angular.module('myApp') .controller("MyCtrl", function($scope, $firebase) { // Firebase URL var URL = "https://ng-newsletter.firebase.com"; // Synchronizing the items on our $scope $scope.items = $firebase(new Firebase(URL + '/items')); });

Now, we can simply interact with the $scope.items object to synchronize our Angular models with Firebase.

Data Synchronization

We can synchronize our data back to Firebase by using the following methods provided by the $firebase object.

$add(value)

The $add method takes a single argument of any type. It will append this value as a member of a chronologically ordered list. We can think of this like we’re calling .push(value) on to the Firebase reference array.

Note that the Firebase reference object is not really an array, but we can act as though it is.

For example, we can add the string “bar” to the Firebase reference located at the /foo endpoint:

1
$scope.items.$add({foo: "bar"});

$remove([key])

The $remove method removes remote child references from Firebase. It takes a single optional argument:

key (optional string)

If a key argument (as a string) is provided, the $remove() method will remove the child referenced by that key. If no key is provided, the entire remote object will be removed.

1
2
3
$scope.items.$remove("foo"); // Remove the child named "foo". $scope.items.$remove(); // Remove the entire object.

$save([key])

The $save method synchronizes all changes to the local elements with the Firebase data store and pushes them instantly to all listening clients. It takes a single argument:

key (optional string)

If the key argument (string) is provided, the $save() method will save changes made to the child element referenced by the key to Firebase. If no key is provided to the $save() method, then all local changes made to the object will be persisted to Firebase.

The $save() method is most commonly used to save any local changes made to the model.

1
2
3
$scope.items.foo = "baz"; $scope.items.$save("foo"); // new Firebase(URL + "/foo") now contains "baz".

$child(key)

The $child() method creates a new $firebase object for a child referenced by the provided key. The method takes a single argument:

key (string)

The key string is used to reference the newly created child.

1
2
3
var child = $scope.items.$child("foo"); child.$remove(); // Equivalent to calling $scope.items.$remove("foo");

$set(value)

The $set() method overwrites the remote value for this object to newValue. The local object will also be subsequently updated to this new value.

It takes a single argument:

value (string)

The value argument is the new value of the local object. The value will overwrite the old value, and will be subsequently updated to this new value.

1
2
$scope.items.$set({bar: "baz"}); // new Firebase(URL + "/foo") is now null.

Ordering in AngularFire

If we want to sort our remote objects, rather than simply sorting locally with Angular’s orderBy filter, we can set the $priority field on a record before calling $save().

1
2
3
$scope.items.foo.$priority = 2; $scope.items.$save("foo"); // new Firebase(URL + "foo")'s priority is now 2.

By default, the $firebase service returns a simple JavaScript object. We can convert this object into an array that respects order simply by using the orderByPriority filter.

This filter turns the object returned by the $firebase service into an array and orders by the priority definition defined by Firebase. Each object in the new array will get an $id property defined on it which references the keyname of the object.

1
2
3
4
5
6
<ul ng-repeat="item in items | orderByPriority"> <l> <input type="text" id="{{item.$id}}" ng-model="item.$priority"/> {{item.name}} </l> </ul>

Firebase events

Firebase fires two types of events that we can use to handle custom logic from within our app. We can use the $on() method to attach event handlers for these two event types:

loaded

The loaded event is fired when initial data is received from Firebase. It is fired once and only once.

1
2
3
$scope.items.$on('loaded', function() { console.log("Items loaded"); });

change

The change event is fired whenever there is a remote change in the data applied to the local object. For instance, this happens if there is a new task added by another user to our task list.

1
2
3
$scope.items.$on('change', function() { console.log("A change is afoot"); });

Implicit Synchronization

To add automatic, implicit synchronization with a $scope variable, we can call the $bind() method on the object returned by the $firebase service.

The $bind() method automatically establishes a 3-way binding so we don’t need to explicitly save data to Firebase using the $add() or $save() methods.

1
2
3
$scope.items.$bind($scope, "remoteItems"); $scope.remoteItems.bar = "foo"; // new Firebase(URL + "/bar") is now "foo".

The $bind() method returns a promise, which will be resolved when the initial data from the server has been received. The promise will be resolved with an unbind function, which will disassociate the 3-way binding when called.

1
2
3
4
5
$scope.items.$bind($scope, "remote") .then(function(unbind) { unbind(); $scope.remote.bar = "foo"; // No changes have been made to the remote data. });

The $bind method returns a promise, which will be resolved when the initial data from the server has been received. The promise will be resolved with an unbind function, which will disassociate the 3-way binding when called. This is useful for optimizing our site and removing unnecessary watches.

Authentication with AngularFire

Firebase provides a simple, client-side authentication strategy out of the box.

Using Firebase’s Simple Login or Custom Login methods, we can easily add user authentication to our application with AngularFire.

Custom Login is most appropriate to use if we have our own server where we want to control our own authentication, or we want to integrate existing authentication with Firebase.

If we want to use Firebase to manage all of our authentication, we can use Simple Login, which supports Facebook, Twitter, GitHub, Persona, and Email/Password authentication.

By defining Firebase as a dependency in our app’s module, we have access to the $firebaseAuth service in our controllers and services.

1
2
3
4
angular.module('myApp') .controller("MyAuthCtrl", function($scope, $firebaseAuth) { // Define our controller here });

The $firebaseAuth service method takes two arguments: a Firebase reference, and an optional object with options. The object can contain the following properties to customize how the authentication works with Firebase:

1
2
3
4
5
6
angular.module('myApp') .controller("MyAuthCtrl", function($scope, $firebaseAuth) { var ref = new Firebase(URL); $scope.auth = $firebaseAuth(ref); // $scope.auth.user is null until the user logs in. });

The object returned by $firebaseAuth() contains a single property named user. user will be set to null if the user is logged out and will change to an object containing the user’s details once they are logged in. We’ll cover detecting logins below.

The contents of the user’s detail object will vary depending upon the authentication mechanism used, but at the very least, it will contain a user id and provider name.

Authentication Events

With the AngularFire authentication, we have access to several methods for changing a user’s authentication state. These methods are $login(), $logout(), and $createUser().

Authentication state in AngularFire is considered global and each of the authentication methods below will be broadcast on the $rootScope. Since nearly all scopes inherit from $rootScope, we can call $scope.on(...) from any controller.

Global authentication means that we cannot have multiple users logged into the same instance of the application at the same time. For example, global authentication prevents two users from being logged into GMail at the same time in the same browser instance.

$firebaseAuth:login

This event is triggered when a user successfully logs in. This event fires with two arguments: an event and user object.

1
2
3
$rootScope.$on("$firebaseAuth:login", function(evt, user) { console.log("User " + user.id + " successfully logged in!"); });
$firebaseAuth:logout

The logout event is triggered when a user logs out. The event is fired with an event argument.

1
2
3
$rootScope.$on("$firebaseAuth:logout", function(evt) { console.log("User logged out!"); });
$firebaseAuth:error

The error event is triggered when there was error during either calling $login() or $logout(). This event will be fired with a single argument, the error.

$login(token, [options])

We’ll use the $login() method to login a user. We’ll usually use this method when a user clicks a login button, like the following:

1
2
3
<a href="#" ng-hide="auth.user" ng-click="auth.$login('persona')">Login</a>

The $login() function takes up to two arguments:

tokenOrProvider (string/JWT token)

If we are using the Firebase Simple Login, we can simply pass in a provider name, such as ‘facebook’, or ‘persona’. If we want to use the Custom Login flow, then we’ll need to pass in a valid JWT token instead.

options (object)

The options argument is only used with Simple Login, where the provided options will be passed as-is to the Simple Login method.

For a “password” provider, we’ll want to provide a username and password as an object.

For more information about the user object, read the Firebase documentation on AngularFire.com.

$logout()

The $logout() method will log out the current user. It takes no arguments.

The $firebaseAuth:logout event will be fired, and the user property will be set to null after the logout is completed. This method is typically attached to a logout button:

1
2
3
<span ng-show="auth.user"> {{auth.user.name}} | <a href="#" ng-click="auth.$logout()">Logout</a> </span>

$createUser()

The $createUser() method is useful when we are using the “password” provider with Firebase Simple Login.

The $createUser() method takes three arguments:

email (string)

The email to create the user with.

password (string)

The password is the password the user will be created with.

callback (function)

The callback method will be called after the $createUser() has run. It will be called with two arguments: error and user. If there was an error in the $createUser() method, the error will contain the error message and user will be null. If the error is null, then the user will be defined.

1
2
3
4
5
auth.createUser(email, password, function(error, user) { if (!error) { console.log('User Id: ' + user.id + ', Email: ' + user.email); } });

Firebase makes it easy to wire up a back-end to our Angular app without having to worry about setting up a server or writing a single line of back-end code. AngularFire enables us to create complex, real-time applications that synchronize immediately between our application’s model and data stored in Firebase.

To learn more about AngularFire, the source code is available on Github: https://github.com/firebase/angularFire.

To get an AngularFire app up and running in minutes, clone the angularFire-seed repo.

Beyond AngularFire

AngularFire is a great wrapper for interacting with Firebase, but you can certainly interact directly with the Firebase SDK from Angular for more complex operations. Check out the Firebase tutorial to learn more about the advanced capabilities of this sophisticated real-time platform.

Get the weekly email all focused on AngularJS. Sign up below to receive the weekly email and exclusive content.
We will never send you spam and it's a cinch to unsubscribe.

Download a free sample of the ng-book: The Complete Book on AngularJS

ng-book: The Complete Book on AngularJS is the canonical AngularJS book available today.

It's free, so just enter your email address and the PDF will be sent directly to your inbox. Mailchimp can take up to an hour to deliver the free sample chapter, but if you don't receive it within the hour, send us an email and we'll manually send them to you!

We'll send you updates about the book, when it updates and other free content.

We will never send you spam and it's a cinch to unsubscribe.

Comments

comments powered by Disqus