Build a Real-Time Status Update App with AngularJS & Firebase

If you’ve spent any time with AngularJS then you’ll likely be familiar with Firebase—a realtime data store that makes it very easy to save and sync data across any platform. Firebase provides an AngularJS binding for its platform called AngularFire which makes using the Firebase API even easier.

In this tutorial we will be creating a simple status update app that will let us see how AngularJS and Firebase interact. For the UI, we’ll use Angular Material which is the AngularJS implementation of Google’s Material Design and comes with a large set of great UI components. Angular Material is based on flexbox which might take a bit of getting used to if you haven’t become familiar with it yet. We’re also going to focus a lot on the authentication portion of the app which, as we’ll see, is made simple by Firebase’s built-in authentication system.

--ADVERTISEMENT--

This tutorial will assume that you’re familiar with AngularJS and that you have a cursory understanding of what Firebase is and how it works.

Installing the Dependencies

Installing Angular Material will give us other packages as well, including the most recent version of AngularJS. We’ve included UI Router as we’ll need to handle two different states—one for logging in/registering and another for viewing statuses. Angular MD5 will give us a quick way to hash email addresses which will be needed for getting Gravatar images for our users.

You’ll also need some kind of server to view and interact with the app. For this purpose http-server is ideal.

Setting up the App

We’ll want a folder structure that gives us a clean way of breaking out the different areas of responsibility. For this, let’s use a folder called components. The entire folder structure should look like this:

We’ve bootstrapped the app on the body tag and called it statusApp. We’re also immediately making use of Angular Material in the body by specifying that the opening div tag should have a layout of row. By setting the layout to row, everything inside the container will be placed horizontally. If we were to set the layout to column, everything would be stacked vertically.

In the next div, we’re setting the width to be 33% by putting a value of 33 on the flex attribute. The offset attribute lets us center the element by saying it should be moved over to the right by a third.

The last element is our ui-view tag which is the point at which our (yet to be created) UI Router states will be loaded.

As you’ll see, we’re calling the AngularJS module statusApp which matches up with our ng-app declaration on the body tag. We’ve injected the modules we need by specifying them in the array next to the module name and then setup some configuration for the app. The configuration block is where we’ll setup the rules for UI Router to handle our different states. To make this happen we need to pass the configuration function $stateProvider and $urlRouterProvider.

We’ve yet to setup the actual controllers and templates for these states, but what we’re saying here is that when we are at a URI of /auth, we want to load the auth view and auth controller. This state is responsible for providing a login and registration box for users.

Once logged in, we want to go to the /status state which loads the status controller and view. Finally, we want to negate any other routes so we tell $urlRouterProvider if in doubt, send the user to the /auth state.

A Little CSS

We’ll need a little bit of CSS to style the status listings in our app.

Handling Authentication

Our app is going to need to be able to register and authenticate users and fortunately for us, Firebase provides an easy to use, hosted solution for authentication. While it offers ways to authenticate with Google, Facebook and others, we’re going to keep things simple by relying on their email and password method.

NOTE: The code samples reference my own Firebase app that I’ve created which you are free to use. Alternatively, you may create your own Firebase account and change up the references to in the code to point to it. To do so, see the section Adding Your Own Firebase Account at the end of the article.

We give this factory a name of Auth and setup a connection to the already-created Firebase app called statusapp. We pass our application reference to $firebaseAuth which is the service responsible for handling the Firebase authentication methods. Returning this from our Auth service will allow us to hook into it from our controller.

Next, let’s setup the authentication controller with some methods that will login and register users.

The first method we have on this controller is createUser which is responsible for accepting user input and using the Auth service we created earlier to create the new user in Firebase. You’ll notice that the Auth methods we are calling weren’t created by us anywhere. Rather, these methods are actually accessed from the $fireabaseAuth service which is what we are returning from our Auth service.

When the new user is successfully created, we call the login method in which we are again using one of the built-in Firebase authentication methods, $authWithPassword. This method accepts an object with and email and password key which we set to be whatever the user has input for those fields. The success handler within then lets us clear the user input and redirect them to the main status page if their login was successful. If the login was unsuccessful, we catch it and for now just log the error to the console.

You’ll see that in the saveUser method we currently just have a code comment that we need to add support to save the user data at the /users endpoint in Firebase. We’ll need to create another service to handle this which we will do in the next section.

Before moving on, let’s put the HTML for our authentication page in place so that we can see what we’ve got.

That’s some pretty dense HTML! Angular Material is great and makes for very nice UI design, but the HTML can tend to add up. However, the nice thing is that we have very descriptive custom HTML tags that help us get a sense of what each element is for.

We’re making use of Angular Material’s tabs for the login/registration page. We’ve got two buttons at work here—one for logging in and the other for registering. You’ll see that we have an ng-click declared for each of them and that they call the appropriate methods from the authController.

If everything is working properly, you should see this:

Saving New User Data

Firebase authentication makes it very easy to manage simple user authentication with an email and password; however, one limitation to it is that we can’t store any other arbitrary data about the user when we create accounts for them. It would be great to have the ability to store a username and any biographical info that might be of interest.

Fortunately we can get around this quite easily by creating an endpoint dedicated solely to users and their data which can then be accessed across our app.

To get us started, let’s create a new service that will be responsible for handling data storage related to users.

Here we have a new factory service called User which returns three methods. You’ll notice that we’re making use of Firebase’s $firebaseObject service here to setup a synchronized object.

The newUserRef method is responsible for creating a key at the /users endpoint for the newly registered user. We rely on the uid for the new user, which is a unique identifier that Firebase creates for us that is guaranteed to be distinct across all the authentication providers that Firebase supports. The uid is formatted using the provider method and then a number representing the user. In the case of our simple login method, the 30th user would get a uid of simplelogin:30. When a new user registers, the object that is returned on success contains the uid for that user, and this is what allows us to tap into it in the User service.

The second method, getUserData, is responsible for accessing the user data for a specific user at the /users endpoint which gives us an easy way to access user information across the app.

Finally, the getLoggedInUser method lets us access data that Firebase stores in local storage for the currently logged-in user. For our app, Firebase keeps this data on a key called firebase:session::statusapp. Since the data is stored as a string, we have to apply JSON.parse to turn it into a useful object.

Now that our User service is in place, let’s add some logic to our authController to make use of it.

We start by injecting the User service into the AuthController function so we can make use of its methods in saveUser. The saveUser method takes some user data as an argument which, in our case, will be the object that Firebase returns when a new user is created. This user data is passed to the newUserRef method on the User service which, as we saw earlier, establishes a new user key at the /users endpoint.

You’ll see that we’re establishing some properties—username and email—on the newly established user. If we then just call AngularFire’s $save method, the data will be pushed up to Firebase.

If the save was successful, we clear the user input and redirect the user to the status state.

That was a lot of setup! Let’s now get to the fun part and actually save and retrieve user’s statuses.

Saving Statuses

We’re going to save all the user statuses at the /status endpoint in our Firebase data store. To setup that connection, let’s create a new service called Status.

We can get all of our status data as an array by simply making a call to the Status service and assigning that call to a key, which is what we’ve done here with vm.statusData = Status. We’ll now be able to do an ng-repeat on this data in our view, which we’ll see next.

We’ll want to get Gravatar images for our users to display beside their status updates and this will require that we hash their email addresses. We’ll take care of all of that right in the view to keep things simple, but to do so we’ll need access to Angular MD5 in the template. This is accomplished by setting vm.md5 = md5.

In our addStatus method, we’re first checking to see if a status entry exists from the view, and if it does, we use AngularFire’s $add method to add the entry to the data store. We pass in an object with some extra data, including the date which is equal to the actual timestamp on the server. It’s important that we use the Firebase server’s timestamp as it is the “official” time that an entry gets recorded. If we relied on the user’s own timestamp from their computer, small delays when sending the data over the wire would mean inaccuracies in the actual time the data is recorded, which can then lead to mis-ordered data later on.

You’ll see that in the user object passed to the $add method, we’re setting a username and email key that get their data from $rootScope. We haven’t yet set $rootScope up for this, but we will do so in the next section.

Finally, after the status has been added we clear the vm.statusText field.

At the top of the view we have a text area and submit button for our users to log their statuses. We set ng-model to equal our statusText key and ng-click on the submit button to equal addStatus.

To display saved statuses we use Angular Material’s md-list-item element and set an ng-repeat on it to loop over the array of statuses. You’ll see that we’re ordering by date here, but in reverse so that we get the latest statuses at the top. To accomplish this we can simply put a negative sign in front of date, so we get orderBy:'-date'.

To get Gravatar images for our users we simply need to set the ng-src of an img tag to Gravatar’s domain and add the user’s hashed email address at the end. Since we have a reference to Angular MD5 in the controller, we can now use it in the view. We call the createHash method and pass in the user’s email address to generate the hash.

From there we are simply displaying the username, date, and text of the status update. If everything worked out, you should see this:

Adding the Current User to $rootScope

As we noted earlier, for the addStatus method to work properly, we need to add some properties to $rootScope which reference the data for the currently logged-in user. We can do this in the run method in our app.js file by using Angular’s $on event listener with the $stateChangeStart event that comes with UI Router. Essentially what we want is to get the data for our logged-in user from the /users endpoint we setup earlier that gets populated when a new user registers. To complete this step we’ll use a couple of the methods we saw earlier in the User service.

//app.js
(function() {
'use strict';
angular
.module('statusApp', ['firebase', 'ngMaterial', 'angular-md5', 'ui.router'])
.config(function($stateProvider, $urlRouterProvider) {
// If a route other than status is requested,
// go to the auth route
$urlRouterProvider.otherwise('/auth');
$stateProvider
.state('auth', {
url: '/auth',
templateUrl: 'components/auth/authView.html',
controller: 'AuthController as auth'
})
.state('status', {
url: '/status',
templateUrl: 'components/status/statusView.html',
controller: 'StatusController as status'
});
})
.run(function($rootScope, $state, User) {
// Listen for changes to the state and run the code
// in the callback when the change happens
$rootScope.$on('$stateChangeStart', function() {
// Use the User service to get the currently
// logged-in user from local storage
var loggedInUser = User.getLoggedInUser();
// Check that we actually have a logged-in user
// saved in local storage
if(loggedInUser) {
// Use the getUserData method on the User service
// to grab the data from the /users endpoint in
// Firebase for the logged-in user
$rootScope.loggedInUserData = User.getUserData(loggedInUser.uid);
}
});
});
})();

In the run method we are listening for changes to the app’s state—for example, after the user logs in or registers and is redirected to the status state. When this happens we want to use the User service to get the currently logged-in user which relies on using the data that Firebase keeps in local storage for the user with the key firebase:session::statusapp. If there is data stored there, we want to call the getUserData method on the User service and pass in the uid for our logged-in user. The data returned from that call is then placed on the loggedInUserData property of $rootScope which lets us access it across the app.

Now that we have our current user’s data on $rootScope, we can can access it in the StatusController so that when the user makes a status update, their username and email are saved with their status.

Deleting Statuses

The final thing we’ll want to do here is give the logged-in user the ability to delete their own statuses. To do so, we can use the $remove method that AngularFire provides. Let’s create a new method in our StatusController that will handle the delete.

We’ve added a method called vm.deleteStatus which references the newly defined deleteStatus function. This will accept a status and use AngularFire’s $remove to delete it from Firebase and automatically update the view to reflect the removal. With this method in place, we need to put it to work in the view.

You’ll see here that we’ve added a new md-icon element to the view. The ng-if directive says that we only want this element to be included if the username of the status is equal to the username of the currently logged-in user. This way, the option to delete a status is only shown to the owner of that status. We set the ng-click to the deleteStatus method we created in the StatusController and pass in the current statusItem. Now when the user clicks the X, that status will be deleted.

Adding Your Own Firebase Account

Getting your own Firebase account working with the status application is very easy. You’ll first need to sign up for Firebase which will give you access to the free starter tier. Once you’ve signed up, add an application by selecting “Create New App” in your dashboard and give it whichever name you like. Next, you’ll need to enable simple authentication by selecting “Manage App”, then “Login and Auth” from the sidebar and checking the box next to “Enable Email and Password Authentication”. Finally, you’ll need to change the name of the Firebase account that the app communicates with by swapping out statusapp for the name of your application in three different files:

components/auth/authService.js

components/status/statusService.js

components/user/userService.js

A Note About Security

Before going to production, we really do need to add an additional layer of security to the app. Without it, users with knowledge of how Firebase works could tamper with the application and create and delete statuses belonging to other users. Specifically, we need to add rules to our app that define who gets to do what. For example, we need to say that a user must be authenticated to be able to add new statuses and that users can only delete statuses if they are the owners of them.

Firebase rules are written in JSON and can be defined in the Security and Rules tab from the application dashboard. To find out more about Firebase rules and how to implement them in your application, read about User Based Security.

Wrapping Up

In this tutorial we’ve seen how to put together an AngularJS and Firebase application using AngularFire and Angular Material for styling. We focused pretty heavily on the authentication portion of the app but also looked at how to create and delete user statuses.

Hopefully this will be useful for you when it comes time to implement Firebase for your next project. I’d love to hear about what you’re working on in the comments!

Ryan is a full-stack developer from Canada who works for Auth0 as a Tech Writer where he provides in-depth tutorials and seed projects for the newest frameworks such as Angular 2, React, and Aurelia as well as other modern web technologies.
Ryan also writes for his own site and you can get notified about his latest articles by signing up for his mailing list.