Lesson Monday

So far, our application can route to different pages, display a list of hard-coded Albums managed by a service, and dynamically route to single Album detail pages whose contents are also retrieved from an injected service. Not bad! However, we'd eventually like to create new Album objects for our marketplace directly through the site.

But unless we actually save these new Albums, they'll disappear everytime we restart our application. So, before we begin adding to our inventory, let's make sure we have somewhere to persist this new data.

In this lesson we'll learn how to create a Firebase database, and install and configure the AngularFire client into our applications. In subsequent lessons we'll address how to read, write and delete Firebase entries.

Firebase

We'll use a database tool called Firebase to store and manage our information. Firebase is a cloud services provider that offers real time databases.

Cloud services are simply any resource provided over the internet. Real Time databases are databases that are constantly updating and syncing whenever our data changes. This means we can create a remote database online that will continually sync our data. Pretty cool!

Creating an Account

First off, you'll need to make a free account at Firebase's website.

Creating a Project

Next, we'll need to create a remote database for our application through Firebase's website.

Once you've created an account, you should be taken to a user dashboard area, with an option to Create a New Project. Select this option, provide a name for your new project, and select your Country/region from the drop-down menu.

You'll then be taken to an "Overview" area. Where you'll be offered three options:

  • Add Firebase to your iOS app
  • Add Firebase to your Android app
  • Add Firebase to your web app

Select Add Firebase to your web app. Firebase should respond with a pop-up modal window. Keep the information in this modal handy, we'll use it in just a moment.

AngularFire

Now that our database exists, we'll need to configure our project to begin communicating with Firebase. First, we'll download a package called AngularFire. As described in its documentation, AngularFire is the official library for connecting Firebase with Angular. It'll assist our application and Firebase database in communicating.

Installation

AngularFire also requires Firebase's own npm package, too. We can install both AngularFire and Firebase with a single npm command, like this:

$ npm install [email protected] [email protected]^3.6.6 --save
  • Notice we are using the --save flag. This flag tells npm to both install the package into our project, and update the dependencies in the project's package.json to include the new package.

Package Versions and Known Issues

The portions reading @4.0.0-rc.0 and @^3.6.6 specify exact version of AngularFire2 and Firebase used in our curriculum that are confirmed to work together. Make sure to use these exact versions. Not every version of AngularFire2 and Firebase work exactly as depicted in our lessons. In fact, there are a few common known issues that can occur if you use different versions:

  • If you receive an error reading "Module not found: Error: Can't resolve 'promise-polyfill' in `.../firebase/app'", update your package.json to use the correct versions (see below). If that doesn't work, install the promise-polyfill package with the command $ npm install promise-polyfill --save-exact.

  • Errors reading "Promise is not defined" or "Firebase has no exported member Promise" are also caused by using incompatible versions of the Firebase and AngularFire2 packages. Check out this GitHub Issue discussion both for more details on why this happens, and some exposure to some real-world open-source community troubleshooting and discussion. (You'll likely visit similar GitHub issue discussions on bug hunting expeditions at your future internship and job!)

If you receive either of these errors, try updating the versions specified in your package.json, like this:

...
"angularfire2": "4.0.0-rc.0",
"firebase": "^3.6.6",
...

And run $ npm install again.

TypeScript Configurations

One more step - we need to add a line to the tsconfig.json file at the very end to mention Firebase:

tsconfig.json
{
  "compileOnSave": false,
  "compilerOptions": {
    "outDir": "./dist/out-tsc",
    "baseUrl": "src",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es5",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2016",
      "dom"
    ],
    "types": [ "firebase" ]
  }
}

This instructs the TypeScript compiler it will need to learn new types to compile correctly. Essentially, the Firebase package came with a new language that TypeScript doesn't automatically know, so we're instructing it to 'learn' this new language by looking in the newly-installed Firebase package. This package includes type definitions the TypeScript compiler may refer to in order to 'understand' this new language. (If you're curious, you can check out what these definitions look like in node_modules/firebase/firebase.d.ts).

Adding Credentials

We previously discussed how to include sensitive API keys in projects without publishing them to GitHub for the world to see in the Managing API Keys lesson.

We'll follow a similar process in Angular. We can create a new file called api-keys.ts in the src/app directory. Then, we can place our Firebase credentials (the information Firebase provided in that modal window), like this:

src/app/api-keys.ts
export const masterFirebaseConfig = {
    apiKey: "xxxx",
    authDomain: "xxxx.firebaseapp.com",
    databaseURL: "https://xxxx.firebaseio.com",
    storageBucket: "xxxx.appspot.com",
    messagingSenderId: "xxxx"
  };

Your file should have your specific Firebase credentials and other information here, instead of xxxx.

Then, we can ignore this file in .gitignore:

.gitignore

...
#Firebase credentials
/src/app/api-keys.ts
...

Important Note on Hiding Firebase Credentials

Don't forget that anyone should be able to clone down and demo any of your projects in your GitHub portfolio. And, by ignoring your credentials you're ensuring they won't be present in your GitHub repo.

So, if a potential employer clones your project it won't run as-is! So, always include instructions for users to acquire their own Firebase credentials and place them in the appropriate file, in the appropriate place, with the appropriate variable names. Your project READMEs should always setup instructions detailing all necessary steps to get a project up and running. Including adding Firebase credentials.

Moving on, we'll need to import our Firebase credentials into our root module. First, we'll import our masterFirebaseConfig hash of information at the top of the file:

src/app/app.module.ts
...
import { masterFirebaseConfig } from './api-keys';
...

We'll also import the AngularFireModule.

src/app/app.module.ts
...
import { AngularFireModule } from 'angularfire2';
...

And finally, the AngularFireDatabaseModule:

src/app/app.module.ts
...
import { AngularFireDatabaseModule } from 'angularfire2/database';
...

Then, we'll export firebaseConfig. We'll fetch each value from the masterFirebaseConfig object we imported (and ignored). This will make them available throughout our root module:

src/app/app.module.ts
...

export const firebaseConfig = {
  apiKey: masterFirebaseConfig.apiKey,
  authDomain: masterFirebaseConfig.authDomain,
  databaseURL: masterFirebaseConfig.databaseURL,
  storageBucket: masterFirebaseConfig.storageBucket
};

@NgModule({
  ...
...

It may seem silly to import credentials into a module just to export them again. However, if we placed the credentials directly into the root module, we'd have to include it in the .gitignore. And, as an integral part of our application, that wouldn't make much sense. It would also require much more effort to re-create the root module than our api-keys.ts file.

Initializing Firebase

Continuing on, we'll add both the AngularFireModule and AngularFireDatabaseModule to the root module's imports array. We'll also call initializeApp() upon AngularFireModule, passing in our Firebase credentials. This establishes the connection between our application, and our Firebase database. The entire file should now look like this:

src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { routing } from './app.routing';
import { AppComponent } from './app.component';
import { WelcomeComponent } from './welcome/welcome.component';
import { AboutComponent } from './about/about.component';
import { MarketplaceComponent } from './marketplace/marketplace.component';
import { AlbumDetailComponent } from './album-detail/album-detail.component';
import { masterFirebaseConfig } from './api-keys';
import { AngularFireModule } from 'angularfire2';
import { AngularFireDatabaseModule } from 'angularfire2/database';

export const firebaseConfig = {
  apiKey: masterFirebaseConfig.apiKey,
  authDomain: masterFirebaseConfig.authDomain,
  databaseURL: masterFirebaseConfig.databaseURL,
  storageBucket: masterFirebaseConfig.storageBucket
};

@NgModule({
  declarations: [
    AppComponent,
    WelcomeComponent,
    AboutComponent,
    MarketplaceComponent,
    AlbumDetailComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    routing,
    AngularFireModule.initializeApp(firebaseConfig),
    AngularFireDatabaseModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

As detailed in the Firebase documentation, the initializeApp() method is responsible for creating a new instance of our Firebase application within our Angular application.

Firebase Rules

Finally, we just need to tell our Firebase database that we'd like to allow outside applications to read and write to it.

  • We'll visit our Firebase Console once more, and select our project's name from the list.
  • Then, we'll visit the Database area by selecting the Database option from the navbar on the left-hand side.
  • Near the top of the page, there should be a blue navbar reading Realtime Database. Below this, select the option that reads RULES.
  • Change both the ".read", and ".write" properties here to "true", as seen below, then click Publish:
{
  "rules": {
    ".read": "true",
    ".write": "true"
  }
}

This tells our Firebase database to allow applications to read and write to it.

Excellent! Our database should now be configured, initialized, and connected to our application. In the next lessons we'll explore Firebase and AngularFire further as we learn how to read, write, and delete data.