Creating a Firebase Powered End to End Ionic Application

Created: 2015-08-28 13:44 Updated: 2015-08-28 13:44 Source: http://www.sitepoint.com/creating-firebase-powered-end-end-ionic-application/ Notebook: All Tech/Frontend Development

Creating a Firebase Powered End to End Ionic Application


Success! Subscribed.

Special offer – free ebook!

MEAN Stack, with a free 14–day SitePoint Premium trial

Technology has come a long way since mankind used rocks to start a fire. There was a time when the Internet was meant to serve Hypertext documents across a few machines. But today, we have reached a complex state where your heart rate is monitored by a device and then transmitted over to your computer. And if the heart rate is not normal, you might even see an Ambulance waiting at the end of your sprint.

This is pretty much how we live these days. And to power such amazing features, we need to have amazing technologies. In this post, we are going to discuss two such bleeding edge technologies, the Ionic Framework and Firebase.

What is the Ionic Framework?

Ionic is a powerful AngularJS driven mobile web framework that makes building hybrid mobile application easy. Not only does it have the power of two way data-binding, it has an awesome interface for working with RESTful APIs. This makes Ionic a perfect match for developing apps and keeping them in sync across devices.

What is Firebase?

There were times when provisioning a machine to deploy a simple website would take weeks. Then, along came Amazon. You simply tell Amazon what kind of system you want, and it provides a server for you. Next, we have seen the rise of Heroku, which provides a PaaS (Platform As A Service) to host your applications. This allowed developers to focus more on the application, instead of worrying about provisioning and deploying the application. And finally, we have Firebase, which is a self sufficient “Server as a Service” driven by a NoSQL data store. In Firebase, all you need to do is define a collection of data, and Firebase will take care of exposing it as RESTful API for you.

Bucketlist Application

I have written an article named Ionic Restify MongoDB – An End to End Hybrid App, which explains how to build an end to end hybrid app using Restify and MongoDB as the API server and Ionic as the hybrid client. In this post, we will see how we can completely eliminate the API server layer with Firebase.

The Bucketlist application we are going to build will have an authentication layer, allowing the users to register and login. Once authenticated, the user is given the option to create a new bucketlist item.

The primary view of the application shows a list of incomplete items and a secondary view to show the list of completed items. The user will have an option to mark an item as complete or delete it.

Before we start building the application, you should:

Application Architecture

Our application will primarily consist of two layers. The first is the client (in our case, the Ionic App, but this could be any other client that can consume a RESTful API), and the second is the server (Firebase).

Application Architecture

As you can see from the above diagram, on the client side we have an Angularfire layer which interacts with Firebase and acts as a service layer for the Ionic application. It is this layer that gives the power to keep the data in sync between Firebase and our Ionic client.

On the Firebase end, we will configure a simple login to take care of the authentication.

Our Ionic application will have five key controllers:

  1. Sign up controller
  2. Sign in controller
  3. Create new item controller
  4. Show incomplete items controller
  5. Show completed items controller

Apart from that, we will have a couple of methods that will take care of marking an item as complete and delete the item.

Designing the Data Structure

Firebase is ideally used for real time data synchronization, where multiple clients across the globe are expected to see the same data at almost the same moment. This is not the case with our app. We are really not looking for a multi-device sync. All we need is for Firebase to take care of managing our bucketlist data for us.

The awesome part of Firebase is that it provides an authentication API out of box. All we need to do is enable it and include the client, and Firebase will take care of the rest for us.

For the bucketlist collection, we need a relation between the user and a bucketlist Item, kind of like a foreign key. This will enable us to show bucketlist items created only by the user.

A sample buckletlist collection is shown below:

"BucketListCollection"

  "item" "test"
  "isCompleted" false
  "user" "test@bla.com"
  "created" 1400801853144
  "updated" 1400801853144
 
  "item" "tes message"
  "isCompleted" false
  "user" "test@bla.com"
  "created" 1401008504927
  "updated" 1401008504927
 
  "item" "Just to check"
  "isCompleted" 
  "user" "test@bla.com"
  "created" 1401008534451
  "updated" 1401008534451
 

In the above sample JSON, the user key holds the link between the logged in user and their items. So, when we fetch the data, we fetch the records that match the logged in user. And this is how we represent the query using a RESTful end point:

https://bucketlist-app.firebaseio.com/bucketList/test@bla.com

Unfortunately, there is no easy way to implement this in Firebase.

As per this Stack Overflow post, there are three ways:

  • Use location names and priorities intelligently.
  • Do client-side querying.
  • Run a separate server.

These approaches were kind of an overkill for a simple API. Then, I stumbled across this Stack Overflow post, that mentions how you can flip the data structure to be more user centric than feature centric. So I changed the app data structure as shown below.

"test@bla,com"  
  "item" "test"
  "isCompleted" false
  "created" 1400801853144
  "updated" 1400801853144
 
  "item" "tes message"
  "isCompleted" false
  "created" 1401008504927
  "updated" 1401008504927


"test2@bla,com"  
  "item" "test2"
  "isCompleted" false
  "created" 14008012853144
  "updated" 14008012853144
 
  "item" "tes message2"
  "isCompleted" false
  "created" 14010028504927
  "updated" 14010028504927

Now, every user has their own collection, rather than a common bucketlist collection, which makes more sense in our application. So, we will be using this structure for managing our data. And our URLs will look like this:

https://bucketlist-app.firebaseio.com/test@bla,com

Note: I am not 100% sure if a large user base would affect the overall response time for a single query (more users = more collections ).

Setup Firebase

We have a good idea as to where we are headed to. Our first step would be to setup a Firebase account, create a new Firebase application instance, and configure the authentication for it.

Navigate to Firebase.com and create a new account if you do not have one. Next, navigate to the Accounts page and create a new app. Provide the desired name and URL. Once the app is created, click on the app name to navigate to the data and configuration page. This is a bird’s eye view of the backend. Feel free to browse around before you continue.

Next, we will setup authentication for our application. Click on the Simple Login tab on the left hand side of the page, and in the main content area you will see the available options. Under the Authentication Providers section click on Email and Password and then Check the Enabled checkbox. This will set up the Simple Login for us.

Setup an Ionic Project

Next, we will scaffold a new Ionic application from a blank template using the Ionic command line interface (CLI). Create a new folder named myIonicFireApp and open terminal/prompt here. First we will install Cordova and Ionic. Execute the following command:

$ npm i -g cordova ionic

Next, we will scaffold a new Ionic app. Generally, I like to keep my code organized. Since this is a test app and we are not going to use any version control to manage development and production, we will create two folders, myIonicFireApp/dev and myIonicFireApp/prod. This step is optional and totally a preference. Next, cd into the dev folder (if you have created one) and run the following command:

$ ionic start bucketListApp blank

bucketListApp is the name of the application. This will scaffold the Ionic + PhoneGap template for us. Once the setup is done, the first order of business is to move the config.xml from the bucketListApp folder to www folder (A PhoneGap build requirement).

Next open up config.xml in your favorite editor and update the widget ID, name, description, and author fields. These will be the meta data for your app, when it run through Phonegap Build. The updated file would look like:

<?xml version='1.0' encoding='utf-8'?>
widget com.ionicfire.bucketlist version0.0.1 xmlnshttp://www.w3.org/ns/widgets xmlns:cdvhttp://cordova.apache.org/ns/1.0
  BucketList App</name
  descriptionAn Awesome App</description
  author emailhi@bucketlist.com http://bucketlist.com/Arvind Ravulavaru</author
  content index.html />
  access origin />
  preference fullscreen value />
  preference webviewbounce valuefalse />
  preference UIWebViewBounce valuefalse />
  preference DisallowOverscroll value />
  <!-- Don't store local date in an iCloud backup. Turn this to "cloud" to enable storage
         to be sent to iCloud. Note: enabling this could result in Apple rejecting your app.
  -->
  preference BackupWebStorage value />
  feature StatusBar
    param ios-package valueCDVStatusBar onload />
  </feature
</widget

Refer to PhoneGap 3 CLI Setup on Mac and Windows to completely understand and setup PhoneGap on Windows and Mac.

To add iOS platform support (Mac only), run the following command:

$ ionic platform add ios

To add Android platform support, run the following command:

$ ionic platform add android

Next, we will build the app, by running:

$ ionic platform build ios
$ ionic platform build ios

Next, to emulate the app, execute:

$ ionic emulate ios
$ ionic emulate android

You can use the above approach to test your code. But, you need to build the code for the respective platforms every time you make changes to the code in www folder.

Given my laziness, I will never do that. The Ionic project come packed with Gulp support. Let us take advantage of that. Back in the terminal, execute the following command:

$ npm install

This will install all the dependencies listed in package.json. Next, install gulp-connect using the command:

$ npm install gulp-connect --save

Then, open up gulfile.js, present at the root of bucketListApp folder and replace it with the following code:

 gulp  require'gulp'
 gutil  require'gulp-util'
 bower  require'bower'
 concat  require'gulp-concat'
 sass  require'gulp-sass'
 minifyCss  require'gulp-minify-css'
 rename  require'gulp-rename'
 sh  require'shelljs'
 connect  require'gulp-connect'

 paths  
  sass 'scssscss'
  www  'www'


gulp'default' 'sass'
gulp'serve' 'connect' 'watch'

gulp'sass' functiondone 
  gulp'./scss/ionic.app.scss'
    
    gulp'./www/css/'
    minifyCss
      keepSpecialComments 
    
    rename extname '.min.css' 
    gulp'./www/css/'
    'end' done


gulp'reload' function  
  return gulp'www/index.html'
    connectreload


gulp'watch' function 
  // Uncomment below line if you wish to work wit SASS
  //gulp.watch(paths.sass, ['sass']);

  gulpwatchpathswww 'reload'


gulp'install' 'git-check' function 
  return bowercommandsinstall
    'log' functiondata 
      gutil'bower' gutilcolorsdataid datamessage
    


gulp'git-check' functiondone 
   shwhich'git' 
    console
        gutilcolors'Git is not installed.'
      '\n  Git, the version control system, is required to download Ionic.'
      '\n  Download git here:' gutilcolors'http://git-scm.com/downloads'  
      '\n  Once git is installed, run \''  gutilcolors'gulp install'  '\' again.'
    
    process
  
  


gulp'connect' function 
  connectserver
    root 'www'
    port '1881'
    livereload 
  

Back in the terminal, run:

$ gulp serve

This will spin up a server. Now, all you need to do is open http://localhost:1881 and observe!.

Note that cordova.js will be a 404 during development. And, since we added live reload support, all you need to do is make changes and switch to your browser to see the changes.

Note: If you are building an app with native plugins like contacts or camera, this approach will not work! You need to deploy the app to the device to test it.

Our Ionic app setup is done. let us get building the actual app.

Ionic and Firebase

The first thing we are going to do is open www/index.html and add the required Firebase, AngularFire and Firebase-simple-login JavaScript references.

script https://cdn.firebase.com/v0/firebase.js</script
script https://cdn.firebase.com/libs/angularfire/0.5.0/angularfire.min.js</script
script https://cdn.firebase.com/v0/firebase-simple-login.js</script

They are pointed to the CDN, but you can download the files and server them locally too. Next update the ng-app directive value on the body tag from starter to bucketList. This will be our module name. Finally, we will add Back button support. Add the following code to the page body:

ion-nav-bar classbar-stable nav-title-slide-ios7
  ion-nav-back-button classbutton-icon icon ion-chevron-left
    Back
  </ion-nav-back-button
</ion-nav-bar

The completed www/index.html will look like:

<!DOCTYPE html>

  
     charsetutf-8
     viewport contentinitial-scale1, maximum-scale1, user-scalableno, widthdevice-width
    title</title
     lib/ionic/css/ionic.css stylesheet
     css/style.css stylesheet
    <!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above
      <link href="css/ionic.app.css" rel="stylesheet">
    -->
    <!-- ionic/angularjs js -->
    script lib/ionic/js/ionic.bundle.js</script
    script https://cdn.firebase.com/v0/firebase.js</script <!-- firebase -->
    script https://cdn.firebase.com/libs/angularfire/0.5.0/angularfire.min.js</script <!-- angularfire -->
    script https://cdn.firebase.com/v0/firebase-simple-login.js</script <!-- firebase-simple-login -->
    <!-- cordova script (this will be a 404 during development) -->
    script cordova.js</script
    <!-- your app's js -->
    script js/app.js</script
    script js/controllers.js</script
  </head
   ng-appbucketList animationslide-left-right-ios7
    ion-nav-bar classbar-stable nav-title-slide-ios7
      ion-nav-back-button classbutton-icon icon ion-chevron-left
        Back
      </ion-nav-back-button
    </ion-nav-bar
    ion-nav-view</ion-nav-view
  </body
</html

Notice that we have added a reference to controllers.js. We will resolve that in a moment. If you go back to the browser and check the developer console you will see a couple of 404s and an Uncaught object error. The Uncaught object error is because, we have updated the ng-app directive in index.html but not in www/js/app.js. You can kill the gulp task, as we are going to make quite a few changes. Once everything is done, we can relaunch the server.

Open www/js/app.js in your favorite editor. First, let us update the module name. Then we will add a couple of dependencies. Update the existing module declaration with:

angularmodule'bucketList' 'ionic' 'firebase' 'bucketList.controllers'

The primary dependency is ionic, next firebase, and finally the controllers.

To develop our application, we are going use two pairs of ion-tabs component. The first set of tabs will be used to show Login & Register screens and the second set of tabs will be used to show incomplete bucketlist items and completed bucketlist items screens.

We are going to wrap our tabs in another abstract tab to gain more control. This will bring our total routes count to six. Inside the run method, we will inject a couple of variables and methods into the $rootScope variable. That would include the Firebase instance URL, a checkSession, logout and loaders for better UX. The final app.js would be

angularmodule'bucketList' 'ionic' 'firebase' 'bucketList.controllers'

function$ionicPlatform $rootScope $firebaseAuth $firebase $window $ionicLoading 
  $ionicPlatformreadyfunction 
    // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
    // for form inputs)
     windowcordova  windowcordovapluginsKeyboard 
      cordovapluginsKeyboardhideKeyboardAccessoryBar
    
     windowStatusBar 
      StatusBarstyleDefault
    

    $rootScopeuserEmail  
    $rootScopebaseUrl  'https://bucketlist-app.firebaseio.com/'
     authRef   Firebase$rootScopebaseUrl
    $rootScopeauth  $firebaseAuthauthRef

    $rootScopeshow  functiontext 
      $rootScopeloading  $ionicLoading
        content text  text  'Loading..'
        animation 'fade-in'
        showBackdrop 
        maxWidth 
        showDelay 
      
    

    $rootScopehide  function 
      $ionicLoading
    

    $rootScopenotify  functiontext 
      $rootScopetext
      $windowsetTimeoutfunction 
        $rootScope
       
    

    $rootScopelogout  function 
      $rootScopeauth$logout
      $rootScopecheckSession
    

    $rootScopecheckSession  function 
       auth   FirebaseSimpleLoginauthRef functionerror user 
         error 
          // no action yet.. redirect to default route
          $rootScopeuserEmail  
          $windowlocationhref  '#/auth/signin'
           user 
          // user authenticated with Firebase
          $rootScopeuserEmail  useremail
          $windowlocationhref  '#/bucket/list'
          
          // user is logged out
          $rootScopeuserEmail  
          $windowlocationhref  '#/auth/signin'
        
      
    
  


configfunction$stateProvider $urlRouterProvider 
  $stateProvider
    state'auth' 
      url "/auth"
      abstract 
      templateUrl "templates/auth.html"
    
    state'auth.signin' 
      url '/signin'
      views 
        'auth-signin' 
          templateUrl 'templates/auth-signin.html'
          controller 'SignInCtrl'
        
      
    
    state'auth.signup' 
      url '/signup'
      views 
        'auth-signup' 
          templateUrl 'templates/auth-signup.html'
          controller 'SignUpCtrl'
        
      
    
    state'bucket' 
      url "/bucket"
      abstract 
      templateUrl "templates/bucket.html"
    
    state'bucket.list' 
      url '/list'
      views 
        'bucket-list' 
          templateUrl 'templates/bucket-list.html'
          controller 'myListCtrl'
        
      
    
    state'bucket.completed' 
      url '/completed'
      views 
        'bucket-completed' 
          templateUrl 'templates/bucket-completed.html'
          controller 'completedCtrl'
        
      
    
    $urlRouterProviderotherwise'/auth/signin'

Notice that we initialize the Firebase Auth service using this code:

$rootScopebaseUrl  'https://bucketlist-app.firebaseio.com/'
 authRef   Firebase$rootScopebaseUrl
$rootScopeauth  $firebaseAuthauthRef

Do not forget to replace baseURL with your Firebase Instance

Now, let’s build controllers.js. Create a new file at www/js and name it controllers.js. As the name suggests, this file will hold all the controllers. Next, creat a new folder named templates. We will populate each template as we go along.

First, we have the Signup controller. Let’s create the required templates first. Create a new file named auth.html in the templates folder. This will be the abstract tab for the Signin and Signup tabs. Fill it with the following code:

ion-tabs classtabs-icon-top
  ion-tab titleSign In icon-onion-ios7-locked
    icon-offion-ios7-locked-outline #/auth/signin
    ion-nav-view auth-signin</ion-nav-view
  </ion-tab
  ion-tab titleSign Up icon-onion-ios7-personadd
    icon-offion-ios7-personadd-outline #/auth/signup
    ion-nav-view auth-signup</ion-nav-view
  </ion-tab
</ion-tabs

Next, let’s add the Signup template. Create a new file named auth-signup.html inside the templates folder and add the following code:

ion-header-bar classbar-positive
   classtitleSign Up</h1
</ion-header-bar
ion-content classhas-header padding
   class
    label classitem item-input
       classinput-labelEmail</span
      input  ng-modeluser.email
    </label
    label classitem item-input
       classinput-labelPassword</span
      input password ng-modeluser.password
    </label
    label classitem item-input
      button classbutton button-block button-positive ng-clickcreateUser()
        Sign Up
      </button
    </label
  </div
</ion-content

When the user clicks submit, we call createuser(). The controller looks like this:

angularmodule'bucketList.controllers' 
  controller'SignUpCtrl' 
    '$scope' '$rootScope' '$firebaseAuth' '$window'
    function $scope $rootScope $firebaseAuth $window 
      $scopeuser  
        email 
        password 
      
      $scopecreateUser  function  
         email  useremail
         password  userpassword

         email  password 
          $rootScopenotify"Please enter valid credentials"
          return false
        

        $rootScope'Please wait.. Registering'
        $rootScopeauth$createUseremail password function error user 
           error 
            $rootScope
            $rootScopeuserEmail  useremail
            $windowlocationhref  '#/bucket/list'
          
           
            $rootScope
             errorcode  'INVALID_EMAIL' 
              $rootScopenotify'Invalid Email Address'
            
              errorcode  'EMAIL_TAKEN' 
              $rootScopenotify'Email Address already taken'
            
             
              $rootScopenotify'Oops something went wrong. Please try again later'
            
          
        
      
    
  

Things to notice:

  1. $rootScope.show(), $rootScope.hide(), and $rootScope.notify() are defined in app.js to show the loading overlay.
  2. $rootScope.auth.$createUser() is responsible for interacting with Firebase and creating a new user.
  3. Notice the various errors message that is returned by Firebase. You can find the entire list here.
  4. On successful registration, we will redirect the user to our primary view.

Next up is the Signin controller. Create a new file named auth-signin.html inside the templates folder and add the following markup:

ion-header-bar classbar-positive
   classtitleSign In</h1
</ion-header-bar
ion-content classhas-header padding
   class
    label classitem item-input
       classinput-labelEmail</span
      input  ng-modeluser.email
    </label
    label classitem item-input
       classinput-labelPassword</span
      input password ng-modeluser.password
    </label
    label classitem item-input
      button classbutton button-block button-positive ng-clickvalidateUser()Sign In</button
    </label
  </div
</ion-content

When the user clicks submit, we call the validateUser(). The controller would be (continuing from above):

controller'SignInCtrl' 
  '$scope' '$rootScope' '$firebaseAuth' '$window'
  function $scope $rootScope $firebaseAuth $window 
     // check session
     $rootScopecheckSession
     $scopeuser  
        email 
        password 
     
     $scopevalidateUser  function  
        $rootScope'Please wait.. Authenticating'
         email  useremail
         password  userpassword
         email  password 
           $rootScopenotify"Please enter valid credentials"
           return false
        
        $rootScopeauth$login'password' 
           email email
           password password
        
        function user 
          $rootScope
          $rootScopeuserEmail  useremail
          $windowlocationhref  '#/bucket/list'
         function error 
          $rootScope
           errorcode  'INVALID_EMAIL' 
            $rootScopenotify'Invalid Email Address'
          
            errorcode  'INVALID_PASSWORD' 
            $rootScopenotify'Invalid Password'
          
            errorcode  'INVALID_USER' 
            $rootScopenotify'Invalid User'
          
           
            $rootScopenotify'Oops something went wrong. Please try again later'
          
        
     
  

Things to notice:

  1. $rootScope.auth.$login() is responsible for the Firebase authentication.
  2. $rootScope.auth.$login() returns a promise, which will be resolved once the request is completed.
  3. On successful authentication, we will redirect to our primary view.

Next, let’s build the primary view of the app. Create a new file named bucket.html inside the templates folder and add the following code:

ion-tabs classtabs-icon-top
  ion-tab titleMy List icon-onion-ios7-browsers
    icon-offion-ios7-browsers-outline #/bucket/list
    ion-nav-view bucket-list</ion-nav-view
  </ion-tab
  ion-tab titleCompleted icon-onion-ios7-checkmark
    icon-offion-ios7-checkmark-outline #/bucket/completed
    ion-nav-view bucket-completed</ion-nav-view
  </ion-tab
</ion-tabs

This is the abstract view that holds our bucketlist complete & incomplete views. Next, create a new file named bucket-list.html inside the templates folder and add the following code:

ion-header-bar classbar-positive
  button classbutton button-clear ng-clicknewTask()New</button
   classtitleMy Bucket List</h1
  button classbutton button-clear ng-clicklogout()Logout</button
</ion-header-bar
ion-content classhas-header padding has-tabs on-refreshonRefresh()
   class ng-repeatitem in list {{item.key}} 
     classitem item-text-wrap
      {{ item.item }}</span
      />  />
       classactions padding
         classion-checkmark-circled icon-actions margin ng-clickmarkCompleted({{item.key}})</i
         classion-trash-b icon-actions margin ng-clickdeleteItem({{item.key}})</i
      </p
    </div
  </div
   class 
     classitem item-text-wrap ng-shownoData
      
      No Items in your bucket List. Click   javascript: ng-clicknewTask()Here</a and create one
      </span
    </div
  </div
</ion-content

Things to notice:

  1. We have added a New button to the header. This will open a popup, where user can enter the item description and create it.
  2. The body of the view renders a card that will show the item description and a Delete and Mark as Completed icons.

The controller looks like this:

controller'myListCtrl' function$rootScope $scope $window $ionicModal $firebase 
  $rootScope"Please wait... Processing"
  $scopelist  
   bucketListRef   Firebase$rootScopebaseUrl  escapeEmailAddress$rootScopeuserEmail
  bucketListRef'value' functionsnapshot 
     data  snapshot

    $scopelist  

      key  data 
       datahasOwnPropertykey 
         datakeyisCompleted  false 
          datakeykey  key
          $scopelistdatakey
        
      
    

     $scopelistlength   
      $scopenoData  
      
      $scopenoData  false
    
    $rootScope
  

  $ionicModalfromTemplateUrl'templates/newItem.html' functionmodal 
    $scopenewTemplate  modal
  

  $scopenewTask  function 
    $scopenewTemplate
  

  $scopemarkCompleted  functionkey 
    $rootScope"Please wait... Updating List"
     itemRef   Firebase$rootScopebaseUrl  escapeEmailAddress$rootScopeuserEmail    key
    itemRefupdate
      isCompleted 
     functionerror 
       error 
        $rootScope
        $rootScopenotify'Oops! something went wrong. Try again later'
        
        $rootScope
        $rootScopenotify'Successfully updated'
      
    
  

  $scopedeleteItem  functionkey 
    $rootScope"Please wait... Deleting from List"
     itemRef   Firebase$rootScopebaseUrl  escapeEmailAddress$rootScopeuserEmail
    bucketListRefchildkeyremovefunctionerror 
       error 
        $rootScope
        $rootScopenotify'Oops! something went wrong. Try again later'
        
        $rootScope
        $rootScopenotify'Successfully deleted'
      
    
  

Things to notice:

  • We will be buiding the Firebase Reference based on the logged in user, as discussed in the Designing the data structure section.
 bucketListRef   Firebase$rootScopebaseUrl  escapeEmailAddress$rootScopeuserEmail

We are creating a collection named after escaping the email address of the user. You can add escapeEmailAddress() definition at the bottom of the controllers.js.

function escapeEmailAddressemail 
   email return false
  // Replace '.' (not allowed in a Firebase key) with ','
  email  emailtoLowerCase
  email  emailreplace/\./g 
  return email
  • Next, we will use this dynamic reference to pull all the buckelist items using the on listener for value event. This will get triggered when ever there is a change in collection (One of the best parts of Firebase).
  • We check if the item is not completed data[key].isCompleted == false, and then add it to the list of items to be shown.
  • We also register the newTask(), that will open the Create New item popup.
  • $scope.markCompleted() and $scope.deleteItem(), interact with the Firebase API to update the isCompleted value to true and delete a piece of data from the collection respectively.

Next, we will add the newCtrl, responsible for creating a new controller. Create a new file named newItem.html inside the templates folder and add the following code:

 classmodal slide-in-up ng-controllernewCtrl
  header classbar bar-header bar-secondary
    button classbutton button-clear button-primary ng-clickclose()Cancel</button
     classtitleNew Item</h1
    button classbutton button-positive ng-clickcreateNew()Done</button
  </header
  ion-content classpadding has-header
    input  placeholderI need to do... ng-modeldata.item
  </ion-content
</div

On clicking Done, we call createUser(). In controller.js append the following code:

controller'newCtrl' function$rootScope $scope $window $firebase 
  $scopedata  
    item 
  

  $scopeclose  function 
    $scopemodal
  

  $scopecreateNew  function 
     item  dataitem

     item return

    $scopemodal
    $rootScope
    $rootScope"Please wait... Creating new"

     form  
      item item
      isCompleted false
      created Date
      updated Date
    

     bucketListRef   Firebase$rootScopebaseUrl  escapeEmailAddress$rootScopeuserEmail
    $firebasebucketListRefform
    $rootScope
  

Things to notice:

  • We build a form object, that will consists of all the essentials data to create a new bucketlist item.
  • We will spawn a new connection to the user’s collection and then using $firebase(bucketListRef).$add(form); we insert the data into the collection.
  • Once the data is inserted, Firebase triggers the value event, which will refresh our bucketlist items view.

Finally, let us add the controller to show all the completed bucketlist items. Create a new file named bucket-completed.html inside the templates folder and add the following code:

ion-header-bar classbar-positive
   classtitleCompleted Items</h1
  button classbutton button-clear ng-clicklogout()Logout</button
</ion-header-bar
ion-content classhas-header padding has-tabs on-refreshonRefresh()
   class ng-repeatitem in list 
     classitem item-text-wrap
      {{ item.item }}</span
      />  />
       classactions padding
         classion-trash-b icon-actions margin ng-clickdeleteItem({{item.key}})</i
      </p
    </div
  </div
   class 
     classitem item-text-wrap ng-shownoData || incomplete
       ng-showincomplete
      You can have not completed any of your Bucket List items yet. Try harder!!
      </span
       ng-shownoData
      No Items in your bucket List.
      </span
    </div
  </div
</ion-content

This controller is similar to the incomplete bucketlist controller, except for Create New item and Mark Item Incomplete. You can add them here too if you want. The controller looks like this:

controller'completedCtrl' function$rootScope $scope $window $firebase 
  $rootScope"Please wait... Processing"
  $scopelist  

   bucketListRef   Firebase$rootScopebaseUrl  escapeEmailAddress$rootScopeuserEmail
  bucketListRef'value' functionsnapshot 
    $scopelist  
     data  snapshot

      key  data 
       datahasOwnPropertykey 
         datakeyisCompleted   
          datakeykey  key
          $scopelistdatakey
        
      
    
     $scopelistlength   
      $scopenoData  
      
      $scopenoData  false
    

    $rootScope
  

  $scopedeleteItem  functionkey 
    $rootScope"Please wait... Deleting from List"
     itemRef   Firebase$rootScopebaseUrl  escapeEmailAddress$rootScopeuserEmail
    bucketListRefchildkeyremovefunctionerror 
       error 
        $rootScope
        $rootScopenotify'Oops! something went wrong. Try again later'
        
        $rootScope
        $rootScopenotify'Successfully deleted'
      
    
  

Finally, let’s add a bit of CSS. Open style.css in the www/css folder and add the following code:

.margin 
  margin-left px
  margin-right px

.icon-actions 
  font-size px

.checkbox 
  vertical-align middle

.actions 
  float right

.item-text-wrap 
  overflow auto

.ion-checkmark-circled.icon-actions.margin
  margin-right px

We’re done! Let’s run the app and see how it looks. In the terminal, run:

gulp serve

This will start the server. Next, navigate to http://localhost:1881 and you should be greeted with a Signin view. Click on Signup and register for an account. Once the registration is successful, you will be redirected to the bucketlist view. Play around with your new Firebase powered Ionic Application.

Note: You can go to your Firebase app account and check out the data structure there too.

Issue a PhoneGap Build

We have successfully built an app that works fine in the browser. Let’s build a native installer and see how the app works on an actual device.

Note: If you are new to PhoneGap, I would recommend reading the PhoneGap Quick Start before continuing.

Step 1: First, copy the myIonicFireApp/dev/bucketListApp/www folder and its contents to myIonicFireApp/prod. This is all we need to issue a PhoneGap build.

Step 2: Create a new GitHub repo named IonicFirePGInstaller.

Step 3: cd into the myIonicFireApp/prod folder (not inside the www folder) and run the following commands:

$ git init
$ git add -A
$ git commit -am "Initial Commit"
$ git remote add origin git@github.com:sitepoint/IonicFirePGInstaller.git

Make sure you update the repo path to point to the one you have created. Finally, check in the code:

$ git push origin master

This will push the code to GitHub.

Step 4: Navigate to PhoneGap Build and login.

Step 5: Click on + New App and submit the GitHub repo URL (the https one and not the ssh one) under open-source. Now, the PhoneGap service will go to GitHub and fetch the repo. Once the repo is loaded, you will see a Ready to Build button. Click on it to issue a PhoneGap build.

Once the build is completed, you can download the installers for your device and test the app out.

Conclusion

This concludes the article on building a Hybrid app using Firebase and the Ionic framework. Hope you gained a fair idea on how to go about building one of your own.

  • You can find the code base we developed on GitHub.
  • You can find the www folder that you can submit to PhoneGap build on GitHub too.
  • You can download the app installer here.

Thanks for reading!


View static HTML