In this codelab, you’ll learn how to use the <firebase-element> to store and process your data using Firebase.

What you’ll learn

What you’ll need

How would rate your experience with Polymer?

NoviceIntermediateProficient

Create a new project

The first time you run Chrome Dev Editor it will ask you to setup your workspace environment.

Fire up Chrome Dev Editor and start a new project:

  1. Click to start a new project.
  2. Enter "PolymerFirebaseCodelab" as the Project name.
  3. In the Project type dropdown, select "JavaScript web app (using Polymer paper elements)".
  4. Click the Create button.

Chrome Dev Editor creates a basic scaffold for your Polymer app. In the background, it also uses Bower to download and install a list of dependencies (including the Polymer core library) into the bower_components/ folder. Fetching the components may take some time if your internet connection is slow. You'll learn more about using Bower in the next step.

If you ever get stuck, you can look up the complete code for each individual step in the GitHub repository for this codelab.

Preview the app

At any point, select the index.html file and hit the  button in the top toolbar to run the app. Chrome Dev Editor fires up a web server and navigates to the index.html page. This is a great way to preview changes as you make them.

You will be writing a TODO app with Polymer without persistency first and add Firebase for persistency afterwards.

Preparing the UI components

First, import the paper-elements so that you can accept the user’s input.

In the <head> of index.html there is a list of imports. Add the import below to the bottom of the list.

index.html

<head>
  ...
  <link rel="import" href="bower_components/paper-input/paper-input.html">
  ...
</head>

Building the UI

You are going to add a <form> containing a <paper-input> and a <paper-button> at the top. The form fires a submit event on Enter without the need for you to write any additional code. Below that, you are going to use a <template is=”dom-repeat”> to traverse over your TODO item list and create a checkbox and a delete button for each item.

Change the body of your index.html to match the following:

index.html

<body unresolved>
  <template is="dom-bind" id="app">
    <form on-submit="addItem">
      <paper-input value="{{newItemValue}}" 
        placeholder="Enter your item here..."></paper-input>
      <paper-button on-click="addItem">Add</paper-button>
    </form>
    <template is="dom-repeat" items="{{items}}">
      <div>
        <paper-icon-button icon="delete" 
          on-click="deleteItem"></paper-icon-button>
        <paper-checkbox on-change="toggleItem"
          checked="{{item.done}}">[[item.text]]</paper-checkbox>
      </div>
    </template>
  </template>
  <script src="main.js"></script>
</body>

This codelab is not about styling, so just replace the contents of style.css with the following:

style.css

body {
  font-family: Roboto, sans-serif;
  color: #333;
  max-width: 700px;
  width: 100%;
  margin: 0 auto;
}

form {
  display: flex;
  flex-direction: row;
  align-items: center;
  margin-bottom: 20px;
}

paper-button {
  flex-shrink: 1;
}
paper-input {
  flex-grow: 1;
}

paper-checkbox {
  display: inline-block;
  margin: 5px 0;
  transition: opacity 0.3s;
}

paper-checkbox[checked] {
  opacity: 0.5;
}

Adding the logic

As you can already tell by the markup, you are going to maintain a list called items, and each item is an object providing a self-explanatory done and a text field.

Additionally, you will have an addItem function that will be called once the user presses Enter inside the <paper-input> or clicks the “Add” button. You will also create  a toggleItem function that toggles the done state of a TODO list item and a deleteItem function that deletes an item from the list.

Open main.js and replace its contents with the code below.

main.js

(function(document) {
  'use strict';

  var app = document.querySelector('#app');
  
})(document);

With the app variable you now have a reference to the <template is=”dom-bind”> element in your markup. Now you need to attach values and functions to it, starting  with your list of items. Below the app variable declaration, add the following code.

main.js

app.items = [
  {
    done: true,
    text: 'Write a TODO app'
  },
  {
    done: false,
    text: 'Use Firebase'
  }
];

These two items are just placeholders so that you can see that both the checked and unchecked styles work at a glance.

Below that, add three event handlers using Polymer’s utility functions. They are equivalent to their native counterparts, but also take care of emitting change events if necessary to update any bindings to that variable. For details, check out the documentation for <template is=”dom-repeat”>

main.js

app.addItem = function(event) {
  event.preventDefault(); // Don't send the form!
  this.push('items', {
    done: false,
    text: app.newItemValue
  });
  this.newItemValue = '';
};

app.toggleItem = function(event) {
  event.model.set('done', !event.model.item.done);
};
app.deleteItem = function(event) {
  this.splice('items', event.model.index, 1);
};

event.model in toggleItem is a property set by <template is=”dom-repeat”> to give event handlers a way of accessing the data used in that iteration of the repeat loop.

Just as a reminder: If you want, you can take a look at the result by pressing .

Next up

You now have the logic in place, but no persistency. Enter Firebase.

Now it is time to pull in the <firebase-element>!

Creating a Firebase instance

  1. Go to https://www.firebase.com
  2. Create an account. You can either click on “Sign up” and create a new account or click on “Login” and use your GitHub account to log in.
  3. On the dashboard, create a new Firebase app. You will have to pick a different URL than what’s shown in the picture.

  1. Click on the URL of the newly created app to open the app’s dashboard and see the empty database.

Allowing anonymous login

For now, you will allow anonymous logins to your application. Open the dashboard of the Firebase instance you just created in the previous step, go to “Login & Auth” and activate anonymous login.

Add the firebase elements

You now have a Firebase app at your disposal. To actually make use of it, you need to load and add the corresponding Firebase element. Open bower.json and add the firebase elements to the dependencies. The dependencies should look like this afterwards:

bower.json

"dependencies": {
  "iron-elements": "PolymerElements/iron-elements#^1.0.0",
  "paper-elements": "PolymerElements/paper-elements#^1.0.1",
  "firebase-element": "PolymerElements/firebase-element#^1.0.0"
}

Afterwards, right-click on the bower_components folder and click “Bower Install”. Chrome Dev Editor downloads and installs all of the dependencies.

Add the following imports to your markup:

index.html

<link rel="import" href="bower_components/firebase-element/firebase-auth.html">
<link rel="import" href="bower_components/firebase-element/firebase.html">

Save your authentication method and your Firebase database URL to a variable. Add the following code directly underneath your app variable declaration:

main.js

app.firebaseURL = 'https://<YOUR FIREBASE NAME>.firebaseio.com';
app.firebaseProvider = 'anonymous';

Remember to adjust the URL accordingly.

To authenticate the user, you will use the <firebase-auth> element. For simplicity’s sake, you will not allow your app to be used without being logged in. Therefore, you can immediately trigger the login process after loading. The element provides an auto-login attribute for that.

At the very top of our <template> add:

index.html

<firebase-auth
  auto-login
  redirect
  location="[[firebaseURL]]"
  provider="[[firebaseProvider]]"
  on-error="onFirebaseError"
  on-login="onFirebaseLogin"></firebase-auth>
<paper-toast id="errorToast"></paper-toast>

You will be using the <paper-toast> to display any errors that happen during the authentication process. You will be notified of such errors by the onFirebaseError handler you added earlier. If the login succeeds, onFirebaseLogin will be called. You will use that opportunity to listen to data changes on the Firebase element.

Add these two functions to the bottom your script file:

main.js

app.onFirebaseError = function(event) {
  this.$.errorToast.text = event.detail.message;
  this.$.errorToast.show();
};
app.onFirebaseLogin = function(event) {
  this.ref = new Firebase(this.firebaseURL + '/user/' + 
                                                event.detail.user.uid);
};

Next up

You have everything in place to access our Firebase data store. Now you actually use the database.

Now that authentication is set up, you have access to your user’s personal data store. Add an event handler to your Firebase reference to get notified whenever data changes.

Modify your onFirebaseLogin handler to look like this:

main.js

app.onFirebaseLogin = function(event) {
  this.ref = new Firebase(this.firebaseURL + '/user/' + 
                                                  event.detail.user.uid);
  this.ref.on('value', function(snapshot) {
    app.updateItems(snapshot);
  });
};

Since you are now going to sync with Firebase immediately, remove the dummy data you added at the start of the codelab.

  1. Replace the dummy data in app.items with an empty array
  2. Add the implementation for updateItems.

The Firebase’s value event passes a snapshot of the new data as a parameter. Since we know that the user’s data is an array of TODO items, we will loop over these and add each one to our items list. In the end, the code should look like this:

app.js

app.items = [];

app.updateItems = function(snapshot) {
  this.items = [];
  snapshot.forEach(function(childSnapshot) {
    var item = childSnapshot.val();
    item.uid = childSnapshot.key();
    this.push('items', item);
  }.bind(this));
};

Now you need to update your three event handlers to use Firebase and the item’s uid (for “unique identifier”). Notice that you are using Firebase’s push() and not Polymer’s. The new data will be pushed to your Firebase instance. The updateItems() handler will take care of updating the local copy.

main.js

app.addItem = function(event) {
  event.preventDefault(); // Don't send the form!
  this.ref.push({
    done: false,
    text: app.newItemValue
  });
  app.newItemValue = '';
};

app.toggleItem = function(event) {
  this.ref.
    child(event.model.item.uid).
    update({done: event.model.item.done});
};

app.deleteItem = function(event) {
  this.ref.child(event.model.item.uid).remove();
};

Press  and see Firebase in action. Add items, check items, remove items. Afterwards, reload the page and witness your items not getting lost in data limbo.

Next up

You now have a proof-of-concept, but to deliver a minimum viable product you need to add a few things, starting with security.

Currently your TODO list is not secure. If someone finds out your uid, they can just open DevTools and access your data. Firebase’s “Security & Rules” feature solves this problem. To restrict access to a user’s document to that user only, do the following:

  1. Go to your Firebase dashboard
  2. Go to “Security & Rules”
  3. Change the rules to match the following:

Security & Rules

{
    "rules": {
        "user": {
          "$uid": {
            ".read": "auth != null && auth.uid == $uid",
            ".write": "auth != null && auth.uid == $uid"
          }
        }
    }
}

This rule ensures that users can only read and write their own data. Changing the uid in the DevTools will result in an error.

Next up

In this last section, you enable Google authentication so that users can access their TODO lists from all of their machines.

Since your app is currently linked to the browser instance (the anonymous uid is saved to local storage), your users can only access their TODO lists from the machine where they created the lists. To change that, you need to use a consistent uid across multiple devices. Using Google login is one way to achieve that.

Getting a Google API Key

To make use of Google Login, you need to create an API key. Go to the Google Developer Console and create a new project. Once the project has been created, go to “APIs & auth” → “Credentials” and create a new “OAuth 2.0 client ID”.

Select “Web Application”. If the wizard doesn’t allow you to, follow the instructions on screen. Then add https://auth.firebase.com as an authorized JavaScript origin and set https://auth.firebase.com/v2/<your Firebase ID>/auth/google/callback as an authorized redirect URI:

Click “Create”. Your Client ID and Client Secret will be shown on screen. Save these for the next section.

Activating Google login

Navigate back to your Firebase dashboard, go to “Login & Auth”, go to the “Google” tab and enter the credentials you just saved in the previous section.

Your app is now able to log in using Google credentials. The only thing missing is to tell your app that it supports Google authentication. Change the firebaseProvider variable to google:

main.js

app.firebaseProvider = 'google';

This is where  gets really fun! Open up multiple browsers, log in with your Google account if you haven’t done so already and see your TODO list being synchronized to all browsers in real-time! You can also open up an incognito window if you would like to see the authentication again as well.

You just built a real-time TODO app that is usable in parallel across multiple devices. Firebase magic!

What you’ve learned

Learn More

Polymer

Other