Firebase using Angular4

While prototyping for few apps for our clients using MongoDB, Couchbase and MySQL databases, Firebase caught my interest and I started playing around with it. Initially I thought it’s MongoDB on cloud, this is true to some extent but Firebase offers many additional features like real time database, cloud messaging, analytics, out of the box authentication and many more. This additional features makes it perfect technology stack for rapid prototyping of MVP and also for small to medium size apps.

This blog is series of tutorials on what I discovered while learning Firebase and eventually Angular4.

On perusal of Firebase official documentation, I came to know that to understand Firebase I would have to pick up one of UI technologies. As a data engineer with no exposure to UI technologies, I researched which could be an easier UI stack to get onto Firebase. Finally, I decided Angular4 would be appropriate as a programming environment to learn Firebase, mainly because it uses typescript which I can relate to already familiar Scala programming language.

This multi-part blog series is written the way I had learned Angular2 and Firebase. I call it “Learn Firebase- the Data Engineer Way”.
Note: In case you have never written Angular 4 app and does not know HTML5 and CSS (like me), I suggest you to first visit bootstrap website and angular.io for a quick start guide.

USE CASE – INVENTORY APP

Lets develop basic inventory app for Agile Soft Systems, Inc where users can request for available inventory and admin can keep track of it.

Source code is available at git-link

Requirement Specification

Inventory app is a web based portal which

  • Should have web based login/logout for employee/user using company email account
  • Should have two types of user – Admin user and Simple user
  • Should have functionality for simple user to request inventory item
  • Should have functionality for admin user to approve/decline user request
  • Should have functionality for admin user to add/update inventory item
UI Wireframe The app layout should look like below wireframe

DEV SETUP

We need to set up our development environment before we start our coding. I use CentOS7, so all installation commands are given for centos, you can search equivalent installation steps for your os version. Node.js Angular2 needs nodejs 6+. You can find installation steps at node-js Angular CLI You can develop angular2 application without angular cli as well but it makes developers life very easy and I strongly recommend it. You will like it too when you see magic in next section.
sudo npm install -g @angular/cli

Visual Studio Code

You can use your choice of code editor. I like Visual Studio Code, it is real cool code editor.

You can find installation steps at vs-code

That’s it. We are good to code our app.

Init App

Lets create Angular2 App using angular cli.

ng new inventory-app

We can invoke angular cli command using command ng. ng new . This command generates standard skeleton for angular 4 app.
Checkout angular-io link for folder structure and associated files. Also, do read about components.

Editor

Open newly created inventory-app project in Visual Studio Code.

Go to View → Integrated Terminal.

VS Code provides integrated terminal from where you can run cli commands. In integrated terminal, type ng server.

Open http://localhost:4200 in your web browser.

Congratulations…!!! We have our dev environment ready.

USER INTERFACE

Lets extend app to create UI similar to wireframe.

Angular cli (ng) has many commands in its kitty to create components and services.

Looking at wireframe, we should create project structure similar to image shown to right.

Header & Home Components

ng generate component header

Yeah, you guessed it right. g for generate and c for component.

Inventory Components

ng g c inventory/request 
ng g c inventory/manage
ng g c inventory/approve
Auth Components
ng g c auth/signup 
ng g c auth/signin
Similar to components, we can create services using angular cli.
ng generate service auth/auth 
ng g s inventory/inventor

Service is nothing but common/reusable Typescript code which needs to be provided to angular app components and modules.

We have created the folder structure. Now we need to wire all the components using html and typescript code. header.component.html
header.component.html
  1. <nav class="navbar navbar-inverse">
  2.  
  3. <div class="container-fluid">
  4.  
  5. <div class="navbar-header">
  6. <a class="navbar-brand" href="www.agsft.com">Agile Soft Systems</a>
  7. </div>
  8.  
  9.  
  10. <ul class="nav navbar-nav">
  11.  
  12. <li><a routerLink="/">Home</a></li>
  13.  
  14.  
  15. <li><a routerLink="/manage">Manage</a></li>
  16.  
  17.  
  18. <li><a routerLink="/request" >Request</a></li>
  19.  
  20.  
  21. <li><a routerLink="/approve" >Approve</a></li>
  22.  
  23. </ul>
  24.  
  25.  
  26. <ul class="nav navbar-nav navbar-right">
  27.  
  28. <li><a>UserName</a></li>
  29.  
  30.  
  31. <li ><a routerLink="/signup"><span class="glyphicon glyphicon-user"></span> Sign Up</a></li>
  32.  
  33.  
  34. <li ><a routerLink="/signin"><span class="glyphicon glyphicon-log-in"></span> Login</a></li>
  35.  
  36.  
  37. <li><a href=# (click)="onSignOut()"><span class="glyphicon glyphicon-log-out"></span> Logout</a></li>
  38.  
  39. </ul>
  40.  
  41. </div>
  42.  
  43. </nav>

Header component contains all links of our app.

routerLink is an Angular 2 feature which navigates to respective component.

app.component.html
  1. <app-header></app-header>
  2.  
  3. <div class="container">
  4.  
  5. <div class="row">
  6.  
  7. <div class="col-md-12">
  8. <router-outlet></router-outlet>
  9. </div>
  10.  
  11. </div>
  12.  
  13. </div>

app component is a root component which gets gets initialize at the start of angular app.
We have added header component in app.component.html, so that will be shown up as first page.
router-outlet is a magical directive included in Angular 2 which magically redirects to associated component.

“A router outlet will emit an activate event any time a new component is being instantiated, and a deactivate event when it is being destroyed.” — snippet from angular.io

app.module.ts

  1. import { Routes,RouterModule } from ‘@angular/router’;
  2.  
  3. const appRoutes: Routes = [
  4. { path: “”, component: HomeComponent },
  5. { path: “manage”, component: ManageComponent},
  6. { path: “request”, component: RequestComponent },
  7. { path: “approve”, component: ApproveComponent },
  8. { path: “signin”, component: SigninComponent },
  9. { path: “signup”, component: SignupComponent }
  10. ];
  11.  
  12. find imports array and add
  13. RouterModule.forRoot(appRoutes)
 

Similar to app.component.html, the corresponding typescript file gets initialized.
app.module.ts is an important file where we will be writing lot of boot code.
appRoutes is a place where we bind components to html link, which is futher refer by routerLink.
e.g. when user will click on http://localhost:4200/request , angular framework(routerLink) will call RequestComponent
Register appRoutes in angular app using RouterModule.

Reload the app on brower. The angular app should look like

This app works but something missing. We can change look and feel by simply adding bootstrap library to our app.

npm install --save [email protected]

Edit .angular-cli.json
Add “../node_modules/bootstrap/dist/css/bootstrap.min.css” In styles array.

  1. "styles": [
  2. "styles.css",
  3. "../node_modules/bootstrap/dist/css/bootstrap.min.css"
  4. ]

Kill the ng serve process if already running and invoke again.
Open browser and check angular app.

Our base app template is ready, now it’s time to add business logic.

FIREBASE

We will use Authentication feature of Firebase which will allow us user to authenticate in app.

First step is to register yourself in Firebase portal. Firebase offers flexible pricing plans and base plan(spark) is free and is enough for our use case.
Once you register yourself, create a new project in firebase console.
Go to firebase console – https://console.firebase.google.com/
Find and Click on +Add project link
Enter name of your project and country
Click on create.
On project home page, click on Add Firebase to your web app
This will generate Firebase setting. Copy config variable and add it in app.module.ts file.
Note: Generated code is javascript code and we need to add those values in typescript file, so there is slight changes requires as below –

FIREBASE

We will use Authentication feature of Firebase which will allow us user to authenticate in app.

First step is to register yourself in Firebase portal. Firebase offers flexible pricing plans and base plan(spark) is free and is enough for our use case.
Once you register yourself, create a new project in firebase console.
Go to firebase console – https://console.firebase.google.com/
Find and Click on +Add project link
Enter name of your project and country
Click on create.
On project home page, click on Add Firebase to your web app
This will generate Firebase setting. Copy config variable and add it in app.module.ts file.
Note: Generated code is javascript code and we need to add those values in typescript file, so there is slight changes requires as below –

  1. export const firebaseConfig = {
  2. apiKey: "ZyVHE8CXx6zRhtByguogEAIzaSyCVODV6oS4diF",
  3. authDomain: "inventory-app-726af.firebaseapp.com",
  4. databaseURL: "https://inventory-app.firebaseio.com",
  5. projectId: "inventory-app",
  6. storageBucket: "inventory-app.appspot.com",
  7. messagingSenderId: "5963xxx54439"
  8. };

Lets implement authentication feature using this information.

We have to install firebase node module and angular2 node module to use firebase library.

  1. npm install firebase@3.9.0 --save
  2. npm install angularfire2@4.0.0-rc.0 --save

We are ready to use firebase APIs.

app.module.ts

import firebase modules

  1. import { AngularFireModule } from 'angularfire2';
  2. import { AngularFireDatabase } from 'angularfire2/database';
  3. import { AngularFireAuth } from 'angularfire2/auth';

Register firebase configs by appending AngularFireModule.initializeApp(firebaseConfig) in imports array.

Signup Component

signup.component.html

  1. <div class="row">
  2.  
  3. <div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
  4.  
  5. <form (ngSubmit)="onSignup(f)" #f="ngForm">
  6.  
  7. <div class="form-group">
  8. <label for="email">Mail</label>
  9. <input type="email" id="email" name="email" ngModel class="form-control">
  10. </div>
  11.  
  12.  
  13. <div class="form-group">
  14. <label for="password">Password</label>
  15. <input type="password" id="password" name="password" ngModel class="form-control">
  16. </div>
  17.  
  18. <button class="btn btn-primary" type="submit" [disabled]="!f.valid">Sign Up</button>
  19. </form>
  20.  
  21. </div>
  22.  
  23. </div>
signup.component.ts
  1. onSignup(form: NgForm) {
  2. const email = form.value.email;
  3. const password = form.value.password;
  4. this.authService.signupUser(email, password);

This is simple signup form which has two fields email and password.
Things to notice here are ngModel and ngSubmit.
Whichever form field you wish to access in typescript file just insert keyword ngModel in input tag. Also, ngModel required attribute “name” in input tag.
ngSubmit runs prescribed method on click of form submit button.
In typescript code, we are reading form values and passing it to authService which we will be coding soon.

Auth Service

auth.service.ts

  1. constructor(private router: Router, private af: AngularFireAuth) {}
  2. signupUser(email: string, password: string) {
  3. this.af.auth.createUserWithEmailAndPassword(email, password)
  4. .then(
  5. (success) ={
  6. let user:any = this.af.auth.currentUser;
  7. user.sendEmailVerification().then(
  8. (success) ={
  9. console.log("please verify your email");
  10. alert("please verify your email");
  11. }
  12. ).catch(
  13. (err) ={
  14. console.log("Error:" + err);
  15. alert("Error:" + err);
  16. }
  17. )
  18. })
  19. .catch(
  20. error = alert(error.message)
  21. );
  22. }
createUserWithEmailAndPassword & sendEmailVerification is built in method provided by Firebase.

app.module.ts

signin.component.html

  1. <div class="row">
  2.  
  3. <div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
  4.  
  5. <form (ngSubmit)="onSignin(f)" #f="ngForm">
  6.  
  7. <div class="form-group">
  8. <label for="email">Mail</label>
  9. <input type="email" id="email" name="email" ngModel class="form-control">
  10. </div>
  11.  
  12.  
  13. <div class="form-group">
  14. <label for="password">Password</label>
  15. <input type="password" id="password" name="password" ngModel class="form-control">
  16. </div>
  17.  
  18. <button class="btn btn-primary" type="submit" [disabled]="!f.valid">Sign In</button>
  19. Or
  20. <button class="btn btn-primary" type="submit" (click)="onSigninGmail()">Sign In with Google</button>
  21. </form>
  22.  
  23. </div>
  24.  
  25. </div>

signin.component.ts

  1. onSignin(form: NgForm) {
  2. const email = form.value.email;
  3. const password = form.value.password;
  4. this.authService.signinUser(email, password);
  5. }
  6.  
  7. onSigninGmail() {
  8. this.authService.signinGmail();
  9. }
  10. auth.service.ts
  11. signinUser(email: string, password: string) {
  12. this.af.auth.signInWithEmailAndPassword(email,password)
  13. .then(
  14. response = {
  15. this.router.navigate(['/']);
  16. this.af.auth.currentUser.getToken()
  17. .then(
  18. (token: string) = {
  19. localStorage.setItem('token',token);
  20. localStorage.setItem('uid',this.af.auth.currentUser.uid);
  21. localStorage.setItem('email',this.af.auth.currentUser.email);
  22. }
  23. )
  24. }
  25. )
  26. .catch(
  27. error = console.log(error)
  28. );
  29. }
  30.  
  31. signinGmail() {
  32. this.af.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider())
  33. .then(
  34. response = {
  35. this.router.navigate(['/']);
  36. this.af.auth.currentUser.getToken()
  37. .then(
  38. (token: string) = {
  39. localStorage.setItem('token',token)
  40. localStorage.setItem('uid',this.af.auth.currentUser.uid);
  41. localStorage.setItem('email',this.af.auth.currentUser.email);
  42. }
  43. )
  44. }
  45. )
  46. .catch(
  47. error = console.log(error)
  48. );
  49. }
  50.  
  51. logout() {
  52. this.af.auth.signOut();
  53. localStorage.clear();
  54. }
  55.  
  56. getEmail(){
  57. return localStorage.getItem('email');
  58. }
  59.  
  60. getToken() {
  61. return localStorage.getItem('token')
  62. }
  63.  
  64. isAuthenticated() {
  65. return localStorage.getItem('token') != null;
  66. }

Firebase not only provides authentication using email/password but also for social providers such as Google, Facebook, Twitter.
Agile Soft System, Inc uses google for work and it make sense to enable Google provider as well.

signinUser

This method takes email/password and validate the user against Firebase user list.
I am storing few of the parameters such as token, uid and email.
Specify firebase.auth.GoogleAuthProvider() in signInWithPopup method. This will allow user to sign in to our app using google creds.

logout,getEmail,getToken

These are helper methods that I have created, to implement logout and other functionality.

Reload angular app and try to sign in. You will get an verification email on sign up.
Once you verify it, you will be registered in firebase project.
You can check registered users in firebase console as well.
Firebase console → Firebase Project → Authentications → Users

Everything should work now from Sign up to Sign In. We still have to modify few things in header component to make UI look like wireframe.

Lets give final touch.

header.component.html
  1. <nav class="navbar navbar-inverse">
  2.  
  3. <div class="container-fluid">
  4.  
  5. <div class="navbar-header">
  6. <a class="navbar-brand" href="#">Agile Soft Systems</a>
  7. </div>
  8.  
  9.  
  10. <ul class="nav navbar-nav">
  11.  
  12. <li><a routerLink="/">Home</a></li>
  13.  
  14.  
  15. <li><a routerLink="/manage">Manage</a></li>
  16.  
  17.  
  18. <li><a routerLink="/request" >Request</a></li>
  19.  
  20.  
  21. <li><a routerLink="/approve" >Approve</a></li>
  22.  
  23. </ul>
  24.  
  25.  
  26. <ul class="nav navbar-nav navbar-right">
  27.  
  28. <li><a>{{ authService.getEmail() }}</a></li>
  29.  
  30.  
  31. <li *ngIf="!authService.isAuthenticated()"><a routerLink="/signup"><span class="glyphicon glyphicon-user"></span> Sign Up</a></li>
  32.  
  33.  
  34. <li *ngIf="!authService.isAuthenticated()"><a routerLink="/signin"><span class="glyphicon glyphicon-log-in"></span> Login</a></li>
  35.  
  36.  
  37. <li *ngIf="authService.isAuthenticated()"><a href=# (click)="onSignOut()"><span class="glyphicon glyphicon-log-out"></span> Logout</a></li>
  38.  
  39. </ul>
  40.  
  41. </div>
  42.  
  43. </nav>
header.component.ts
  1. constructor(private authService: AuthService) { } ngOnInit() { } onSignOut() { this.authService.logout(); }
Reload the app and figure out the magic.

CAN-ACTIVATE

It seems everything is working but did you figure out – user is still able to access links request, approve and manage even if he/she not logged into the inventory-app.
CanActivate prevents exactly same thing.

auth-guard.service.ts
  1. import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
  2. import { Injectable } from '@angular/core';
  3. import { Router } from '@angular/router';
  4.  
  5. import { AuthService } from './auth.service';
  6.  
  7. @Injectable()
  8. export class AuthGuard implements CanActivate {
  9.  
  10. constructor(private authService: AuthService, private router: Router) {}
  11.  
  12. canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
  13. if(!this.authService.isAuthenticated())
  14. this.router.navigate(['/']);
  15. return this.authService.isAuthenticated();
  16. }
  17. }
app.module.ts
  1. import { AuthGuard } from './auth/auth-guard.service';
  2.  
  3. providers: [AuthService,AngularFireAuth,AuthGuard],
  4.  
  5. const appRoutes: Routes = [
  6. { path: "", component: HomeComponent },
  7. { path: "manage", component: ManageComponent, canActivate: [AuthGuard] },
  8. { path: "request", component: RequestComponent, canActivate: [AuthGuard] },
  9. { path: "approve", component: ApproveComponent, canActivate: [AuthGuard] },
  10. { path: "signin", component: SigninComponent },
  11. { path: "signup", component: SignupComponent }
  12. ]

Check the app now, it should not allow unauthenticated users to access request link.

I know this is a long blog but I hope its informative and gets you off on a good start to learn Firebase. Thanks for being with me. We will implement few more features of our inventory app using Firebase Real Time Database in next blog post. Keep visiting for the update on next blog.