Book Image

Ionic 2 Blueprints

By : Indermohan Singh
Book Image

Ionic 2 Blueprints

By: Indermohan Singh

Overview of this book

<p>Ionic 2, the latest version of Ionic Mobile SDK, is built on the top of latest technologies such as Angular 2, TypeScript, SASS, and lot more. The idea behind Ionic 2 is to make the entire app development process even more fun.</p> <p>This book makes it possible to build fun and engaging apps using Ionic 2. You will learn how to use various Ionic components, integrate external services, derive capabilities, and most importantly how to make professional apps with Ionic 2. It will help you in understanding the fundamentals of Ionic 2 app development and will show how to build applications, gradually increasing your capabilities and the complexity of applications so that you can learn Ionic app development in a consistent and convenient way.</p> <p>You will also explore services such as Firebase, using LocalStorage, the WordPress JSON API, and REST API, which will help you turn your next billion dollar idea into reality.</p> <p>By the end of this book, you will be able to proudly call yourself a pro Ionic developer who can create a host of different apps with Ionic, and you’ll have a deeper practical understanding of Ionic.</p>
Table of Contents (14 chapters)

Coding our app


Now we have everything set up to start working on our application, there are three important steps for coding our app. They are as follows:

  • Defining our main app.ts file

  • Creating providers/services for various functionalities

  • Creating Ionic pages for various views

Defining app.ts

This is the root of our application and it defines the root component using the @App decorator. This is where we inject all of our dependencies. The following should be present in app.ts:

/* /app/app.ts */ 
import {NavController, Platform, ionicBootstrap} from 'ionic-angular'; 
import {StatusBar} from 'ionic-native'; 
import {Component, Inject} from '@angular/core'; 
import {LoginPage} from './pages/login/login'; 
import {TabsPage} from './pages/tabs/tabs'; 
import {AuthProvider} from './providers/auth-provider/auth-provider'; 
import {ChatsProvider} from './providers/chats-provider/chats-provider'; 
import {UserProvider} from './providers/user-provider/user-provider'; 
import {UtilProvider} from './providers/utils'; 
import { 
    FIREBASE_PROVIDERS,  
    defaultFirebase,  
    firebaseAuthConfig,  
    FirebaseRef, 
    AngularFire, 
    AuthProviders,  
    AuthMethods 
} from 'angularfire2'; 
 
@Component({ 
  template: '<ion-nav id="nav" [root]="rootPage" #content></ion-nav>' 
}) 
class MyApp { 
  message: string; 
  rootPage: any; 
      
  constructor(public authProvider:AuthProvider, public platform:Platform) { 
        let auth = authProvider.getAuth(); 
        auth.onAuthStateChanged(user => { 
          if(user) { 
            this.rootPage = TabsPage; 
          } else { 
            this.rootPage = LoginPage; 
          } 
        }); 
  } 
} 
 
ionicBootstrap(MyApp, [FIREBASE_PROVIDERS,defaultFirebase({ 
    apiKey: "AIzaSyC2gX3jlrBugfnBPugX2p0U1XiSqXhrRgQ", 
    authDomain: "chat-app-1e137.firebaseapp.com", 
    databaseURL: "https://chat-app-1e137.firebaseio.com", 
    storageBucket: "chat-app-1e137.appspot.com", 
  }), 
  firebaseAuthConfig({ 
    provider: AuthProviders.Password, 
    method: AuthMethods.Password, 
    remember: 'default', 
    scope: ['email'] 
  }), 
  AuthProvider,  
  ChatsProvider, 
  UserProvider, 
  UtilProvider] ) 

This is the entry point of our application. In it, we are initializing Firebase and configuring it to use password authentication. We are also checking that, if a user is authenticated, it goes to the TabsPage; otherwise, it will go to the LoginPage. We are also providing all of our dependencies. We haven't yet created our dependencies, so TypeScript will show errors. Just ignore it.

ionicBootstrap is an alternative to Angular's bootstrap method for Ionic 2 applications.

Note that we have written a template inside this file. We have used ion-nav to create a navigation stack, where we will push our views.

Note

To read more about what is going on under the hood inside IonicBootstrap, check this blog post at http://inders.in/blog/2015/10/28/introduction-to-ionic-2/.

Providers for our application

We will have the following providers for our application:

  • AuthProvider

  • UserProvider

  • ChatsProvider

  • UtilProvider

Defining AuthProvider

AuthProvider is the provider that we will use for authentication purposes in our app. With the Ionic CLI, we can now generate pages and providers with the command line.

Generating the provider

The following command will create auth-provider.js files in the app/provider/auth-provider directory, with some default content:

ionic g provider AuthProvider
Changing the extension

Now, we have to change the extension of the file to .ts for using TypeScript in our code.

AuthProvider code

This is the module where we handle all of our authentication work. The following code should be present in auth-provider.ts:

/* /app/provider/auth-provider/auth-provider.ts */ 
import {Injectable, Inject} from '@angular/core'; 
import {FirebaseAuth, FirebaseRef, AngularFire} from 'angularfire2'; 
import {LocalStorage, Storage} from 'ionic-angular'; 
 
@Injectable() 
export class AuthProvider { 
  local = new Storage(LocalStorage); 
  constructor(public af:AngularFire) {} 
  getAuth() { 
    return firebase.auth(); 
  }; 
   
  signin(credentails) {    
    return this.af.auth.login(credentails); 
  } 
   
  createAccount(credentails) { 
    return this.af.auth.createUser(credentails); 
  }; 
   
  logout() { 
     var auth = firebase.auth(); 
     auth.signOut(); 
  } 
} 

Let's understand the preceding code:

  • getAuth() returns the Firebase SDK's firebase.auth() method.

  • signin() does the login process for us. Since we will be using Firebase password authentication. We are using AngularFire2's auth.login method by passing it login credentials.

  • createAccount() takes an object with e-mail and password values and creates a Firebase account for the user. Again, we are using AngularFire2's auth.createUser with credentials to create a Firebase user account.

  • logout() logs the user out. We are using Firebase SDK's auth.signOut() function here.

Defining UserProvider

First we will generate our provider using the following command:

ionic g generate UserProvider

Then, change the extension of user-provider.js to user-provider.ts.

UserProvider code

UserProvider is for doing user-related work in our application, such as creating a user, getting a list of users, updating the profile of a user, and other things. The following code should be present in user-provider.ts:

/* /app/providers/user-provider/user-provider.ts */ 
import {Injectable, Inject} from '@angular/core'; 
import {FirebaseRef, AngularFire} from 'angularfire2'; 
import {LocalStorage, Storage} from 'ionic-angular'; 
import {Camera} from 'ionic-native'; 
 
@Injectable() 
export class UserProvider { 
  local = new Storage(LocalStorage); 
  constructor(public af:AngularFire) { } 
   
  // Get Current User's UID 
  getUid() { 
    return this.local.get('userInfo') 
    .then(value => { 
      let newValue = JSON.parse(value); 
      return newValue.uid; 
    }); 
  } 
   
  // Create User in Firebase 
  createUser(userCredentails) { 
    this.getUid().then(uid => { 
      let currentUserRef = this.af.database.object(`/users/${uid}`); 
      currentUserRef.set({email: userCredentails.email}); 
    }); 
  } 
   
  // Get Info of Single User 
  getUser() { 
    // Getting UID of Logged In User 
    return this.getUid().then(uid => { 
      return this.af.database.object(`/users/${uid}`); 
    }); 
  } 
 
   
  // Get All Users of App 
  getAllUsers() { 
      return this.af.database.list('/users'); 
  } 
    
  // Get base64 Picture of User 
  getPicture() { 
      let base64Picture; 
      let options = { 
          destinationType: 0, 
          sourceType: 0, 
          encodingType:0   
      }; 
       
      let promise = new Promise((resolve, reject) => { 
           Camera.getPicture(options).then((imageData) => { 
                base64Picture = "data:image/jpeg;base64," + imageData; 
                resolve(base64Picture); 
            }, (error) => { 
                reject(error); 
          }); 
       
      }); 
      return promise; 
  } 
   
  // Update Provide Picture of User 
  updatePicture() { 
    this.getUid().then(uid => { 
      let pictureRef = 
       this.af.database.object(`/users/${uid}/picture`); 
      this.getPicture() 
      .then((image) => { 
          pictureRef.set(image); 
      }); 
    }); 
  } 
} 

Let's understand the preceding code:

  • getUid() returns a promise, which resolves intothe  uid of the logged-in user. It gets the uid from the userInfo key of LocalStorage.

  • createUser() creates a new user in the users endpoint in the Firebase database.

  • getUser() returns the Observable, which has the information of the logged-in user from the Firebase database.

  • getAllUsers() returns an AngularFire2 list Observable, which lists all the users of our application.

  • getPicture() gets a picture from the user's mobile device and returns a promise. This promise resolves into a base64 Encoded JPEG Image.

  • updatePicture() updates the user's profile picture. It takes the picture using the getPicture method and sets the picture key of the logged-in user.

Note

In this app, we are storing images as a base64 string. It is not a very efficient method. Instead, Firebase provides us with a storage bucket to store binary data such as images, videos, and other binary data. We have used the Firebase storage-bucket approach in Chapter 7, Social App with Firebase of this book.

Defining ChatsProvider

First, we need to generate our provider using the following command:

ionic g provider ChatsProvider

Then, change the extension of the file chats-provider.js to chats-provider.ts.

ChatsProvider code

ChatsProvider is used to get a list of previous chats and check if a chat already exists between two users. The following code should be present in chats-provider.ts:

/* /app/providers/chats-provider/chats-provider.ts */ 
import {Injectable, Inject} from '@angular/core'; 
import {AngularFire, FirebaseRef} from 'angularfire2'; 
import {Observable} from 'rxjs/Observable'; 
import {UserProvider} from '../user-provider/user-provider'; 
 
@Injectable() 
export class ChatsProvider { 
  constructor(public af: AngularFire, public up: UserProvider) {} 
  // get list of Chats of a Logged In User 
  getChats() { 
     return this.up.getUid().then(uid => { 
        let chats = this.af.database.list(`/users/${uid}/chats`); 
        return chats; 
     }); 
  } 
   
  // Add Chat References to Both users 
  addChats(uid,interlocutor) { 
      // First User 
      let otherUid = interlocutor; 
      let endpoint =
       this.af.database.object(`/users/${uid}/chats/${interlocutor}`); 
      endpoint.set(true); 
       
      // Second User 
      let endpoint2 = 
       this.af.database.object(`/users/${interlocutor}/chats/${uid}`); 
      endpoint2.set(true); 
  } 
 
  getChatRef(uid, interlocutor) { 
      let firstRef = 
       this.af.database.object(`/chats/${uid},${interlocutor}`,
       {preserveSnapshot:true}); 
      let promise = new Promise((resolve, reject) => { 
          firstRef.subscribe(snapshot => { 
                let a = snapshot.exists(); 
                if(a) { 
                    resolve(`/chats/${uid},${interlocutor}`); 
                } else { 
                    let secondRef = 
                     this.af.database.object(`/chats/${interlocutor},
                      ${uid}`, {preserveSnapshot:true}); 
                    secondRef.subscribe(snapshot => { 
                        let b = snapshot.exists(); 
                        if(!b) { 
                            this.addChats(uid,interlocutor); 
                        } 
                    }); 
                    resolve(`/chats/${interlocutor},${uid}`); 
                } 
            }); 
      }); 
       
      return promise; 
  } 
} 

Let's understand the preceding code:

  • getChats() gets a list of chats of the logged-in user-chats that a user has already initiated.

  • addChats() takes two input values. The first is the uid of the logged-in user, and the second is the uid of the other user (interlocutor). This function adds chat references (uid of the other user) to both users' information in the Firebase database.

  • getChatRef() takes two arguments. One is the uid of the logged-in user and the other is the uid of the other user. It returns a promise, which resolves to the Firebase database URL of the chats between these two users. If this is the first time these two users are chatting, it creates a URL in the form of /chats/${interlocutor},${uid}, where ${interlocutor} is the uid of the other user and ${uid} is the uid of the logged-in user.

Defining UtilProvider

UtilProvider is a module for abstracting some functionalities that we will use repeatedly, such as the Ionic alert. The following code should be present in utils.ts:

/* /app/providers/utils.ts */ 
import {Injectable, Inject} from '@angular/core'; 
import {Alert} from 'ionic-angular'; 
@Injectable() 
export class UtilProvider { 
    doAlert(title, message, buttonText) { 
      console.log(message); 
      let alert = Alert.create({ 
          title: title, 
          subTitle: message, 
          buttons: [buttonText] 
      }); 
      return alert;  
    } 
} 

In UtilProvider, we have created a doAlert function that takes a title, message, and buttonText as an input, and creates an Ionic alert box for us. This function becomes very useful for displaying alert messages without writing alert code again and again.

Application pages

Now we have to define all the providers and services for our application. First let's define the pages of our application.

We will have the following pages in our application:

  • LoginPage

  • TabsPage

  • UsersPage

  • ChatsPage

  • AccountPage

  • ChatViewPage

Each page has two or three files. One is the .ts file, which controls the page. The other is the .html file, which is the template of the page and, if present, the last is the .scss file, which is the styling file for the page.

Defining the LoginPage

The LoginPage includes the login template and a controller to handle that template. This is the page where the user creates an account or logs in. The following code should be present in login.ts:

/* /app/pages/login/login.ts*/ 
import {Component} from '@angular/core'; 
import {NavController, Storage, LocalStorage} from 'ionic-angular'; 
import {TabsPage} from '../tabs/tabs'; 
import {FormBuilder, Validators} from '@angular/common'; 
import {validateEmail} from '../../validators/email'; 
import {AuthProvider} from '../../providers/auth-provider/auth-provider'; 
import {UserProvider} from '../../providers/user-provider/user-provider'; 
import {UtilProvider} from '../../providers/utils'; 
import {FirebaseAuth} from 'angularfire2'; 
 
@Component({ 
  templateUrl: 'build/pages/login/login.html' 
}) 
export class LoginPage { 
  loginForm:any; 
    storage = new Storage(LocalStorage); 
    constructor(public nav:NavController,  
      form:FormBuilder,  
      public auth: AuthProvider,  
      public userProvider: UserProvider, 
      public util: UtilProvider) { 
         
        this.loginForm = form.group({ 
            email: ["",Validators.compose([Validators.required, 
             validateEmail])], 
            password:["",Validators.required] 
        }); 
    } 
     
  signin() { 
      this.auth.signin(this.loginForm.value) 
      .then((data) => { 
          this.storage.set('userInfo', JSON.stringify(data)); 
          this.nav.push(TabsPage); 
      }, (error) => { 
          let errorMessage = "Enter Correct Email and Password"; 
          let alert = this.util.doAlert("Error",errorMessage,"Ok"); 
          this.nav.present(alert); 
      }); 
    }; 
     
    createAccount() { 
        let credentails = this.loginForm.value; 
        this.auth.createAccount(credentails) 
        .then((data) => { 
           this.storage.set('userInfo', JSON.stringify(data)); 
           this.userProvider.createUser(credentails); 
        }, (error) => { 
            let errorMessage = "Account Already Exists"; 
            let alert = this.util.doAlert("Error",errorMessage,"Ok"); 
            this.nav.present(alert); 
        }); 
    }; 
} 

In the constructor, we have created a login form using Angular2's form builder, and we are using a custom validator that we have defined in the following code to validate the e-mail ID, since Angular2 doesn't have a validator for e-mail ID.

The signin function takes the user's e-mail and password, and authenticates the user, using the signin member function of AuthProvider. If the user is authenticated successfully they navigate to the TabsPage. Otherwise, it shows an error message using the doAlert member function of UtilProvider.

Similarly, the createAccount function takes the user's e-mail and password and creates a new user account. It also adds the user's information to the users key in the Firebase database. If the user already exists it shows an error message.

Template

The following code should be present in login.html:

<!--  /app/pages/login/login.html --> 
<ion-header> 
    <ion-navbar primary> 
        <ion-title>Login</ion-title>     
    </ion-navbar> 
</ion-header> 
 
<ion-content class="padding"> 
   <form [ngFormModel]="loginForm"> 
    <ion-list> 
        
        <ion-item> 
            <ion-label floating>Email</ion-label> 
            <ion-input type="text" ngControl="email"></ion-input> 
        </ion-item> 
 
        <ion-item> 
            <ion-label floating>Password</ion-label> 
            <ion-input type="password" ngControl="password"></ion-
             input> 
        </ion-item> 
    </ion-list> 
     
    <div padding> 
      <button primary block (click)="signin()" 
       [disabled]="!loginForm.valid">Sign In</button> 
    </div> 
     
    <div padding> 
      <button full clear favorite (click)="createAccount()" 
       [disabled]="!loginForm.valid"> 
          <ion-icon name="person"></ion-icon> 
          Create an Account</button> 
    </div> 
   </form> 
</ion-content> 

Both the Sign In and Create Account buttons will be enabled only when the form is valid.

Defining a custom e-mail validator

We need to define a custom validator, which validates the e-mail input from users because Angular 2 doesn't have a default e-mail validator. The following code should be present in email.ts:

/* /app/validators/email.ts  */ 
import {Control} from '@angular/common'; 
 
export function validateEmail(c: Control) { 
 let EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-
  9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i; 
  return EMAIL_REGEXP.test(c.value) ? null : { 
    validateEmail: { 
      valid: false 
    } 
  }; 
} 

This just takes the control's value and validates it against the regular expression provided using EMAIL_REGEXP.

Defining the TabsPage

The TabsPage handles the tabs of our app. This is the place where we define the root page of each tab. The following code should be present in tabs.ts:

/*  /app/pages/tabs/tabs.ts  */ 
import {Component} from '@angular/core'; 
import {NavController} from 'ionic-angular'; 
import {ChatsPage} from '../chats/chats';  
import {AccountPage} from '../account/account'; 
import {UsersPage} from '../users/users'; 
 
@Component({ 
  templateUrl: 'build/pages/tabs/tabs.html' 
}) 
export class TabsPage { 
  chats = ChatsPage; 
  users = UsersPage; 
    profile = AccountPage; 
} 

Template

The following code should be present in tabs.html:

<!-- /app/pages/tabs/tabs.html --> 
<ion-tabs light> 
    <ion-tab [root]="users" tabTitle="Users" tabIcon="people"></ion-
     tab> 
    <ion-tab [root]="chats" tabTitle="Chats" tabIcon="chatboxes"></ion-
     tab> 
    <ion-tab [root]="profile" tabTitle="Account" tabIcon="person"></ion-tab> 
</ion-tabs> 

In the TabsPage, we have just defined the root pages of all our tabs. The [root] property is used to set the root page of a tab. By default, the first tab is opened when the TabsPage is pushed into the navigation stack.

Defining the UsersPage

The UserPage is the page where we will list all of our users. The following code should be present in users.ts:

/*  /app/pages/users/users.ts  */ 
import {Component} from '@angular/core'; 
import {NavController} from 'ionic-angular'; 
import {Observable} from 'rxjs/Observable'; 
import {UserProvider} from '../../providers/user-provider/user-provider'; 
import {ChatViewPage} from '../chat-view/chat-view'; 
 
@Component({ 
    templateUrl: 'build/pages/users/users.html' 
}) 
export class UsersPage { 
    users:Observable<any[]>; 
    uid:string; 
    constructor(public nav: NavController, public userProvider: 
     UserProvider) { 
        userProvider.getUid() 
        .then(uid => { 
            this.uid = uid; 
            this.users = this.userProvider.getAllUsers(); 
        }); 
    } 
    openChat(key) { 
        let param = {uid: this.uid, interlocutor: key}; 
        this.nav.push(ChatViewPage,param); 
    } 
} 

In the constructor, we got the uid of the logged-in user and a list of all users of the app as an Observable.

The openChat function opens the ChatViewPage by passing the uid of both users as navigation parameters.

Template

The following code should be present in user.html:

<!-- /app/pages/users/users.html --> 
<ion-header> 
    <ion-navbar primary> 
        <ion-title>Users</ion-title> 
        <ion-buttons end> 
            <ion-spinner *ngIf="!(users | async)"></ion-spinner> 
        </ion-buttons> 
    </ion-navbar> 
</ion-header> 
 
<ion-content> 
    <ion-list> 
      <span *ngFor="let user of users | async"> 
        <a ion-item (click)="openChat(user.$key)" *ngIf="user.$key !== 
         uid"> 
             <ion-avatar item-left> 
                <img *ngIf="!user.picture" src="img/default.jpg"> 
                <img *ngIf="user.picture" src="{{user.picture}}"> 
             </ion-avatar> 
             <h2>{{user.email}}</h2> 
        </a> 
      </span> 
    </ion-list> 
</ion-content> 

We have used ngFor to iterate over the users list and we have excluded the logged-in user from the list using ngIf. Basically, we check if the uid of the logged-in user and the user in the iteration is the same, then exclude it from the list. We are also showing the avatar of each user. If the user has uploaded his picture, we show it from Firebase; otherwise, we show a default image.

We are also showing a loading spinner in the navbar until we get the list of users.

Defining the ChatsPage

The ChatsPage lists all previous chats. The following code should be present in chats.ts:

/* /app/pages/chats/chats.ts */ 
import {Component} from '@angular/core'; 
import {NavController, NavParams} from 'ionic-angular'; 
import {Observable} from 'rxjs/Rx'; 
import {UserProvider} from '../../providers/user-provider/user-provider'; 
import {ChatsProvider} from '../../providers/chats-provider/chats-provider'; 
import {AngularFire} from 'angularfire2'; 
import 'rxjs/add/operator/map'; 
import {ChatViewPage}  from '../chat-view/chat-view'; 
 
@Component({ 
    templateUrl: 'build/pages/chats/chats.html' 
}) 
export class ChatsPage { 
    chats:Observable<any[]>; 
    constructor(public chatsProvider: ChatsProvider,  
        public userProvider: UserProvider,  
        public af:AngularFire,  
        public nav: NavController) { 
             
            this.chatsProvider.getChats() 
            .then(chats => { 
                this.chats = chats.map(users => { 
                    return users.map(user => { 
                        user.info = 
                        this.af.database.object(`/users/${user.$key}`); 
                        return user; 
                    }); 
                }); 
            }); 
        } 
     
    openChat(key) { 
        this.userProvider.getUid() 
        .then(uid => { 
            let param = {uid: uid, interlocutor: key}; 
            this.nav.push(ChatViewPage,param); 
        });    
    } 
} 

The ChatsPage is similar to the UsersPage. The openChat function does exactly the same thing as it does in the UsersPage. The only difference is that instead of showing all the users of the application, we show only those users who have already had a conversation with the logged-in user. First we get all the references of his previous chats from his Firebase endpoint, which contains all the uid of other people. Then we map all those uid to keys inside the users endpoint to get the e-mails of those users. It is like a join. This is an asynchronous process, and this is what Observables are capable of. You can filter, map, search, and do lots of other stuff on Observables.

Template

The following code should be present in chats.html:

<!-- /app/pages/chats/chats.html --> 
<ion-header> 
    <ion-navbar primary> 
        <ion-title>Chats</ion-title> 
        <ion-buttons end> 
            <ion-spinner primary *ngIf="!(chats | async)"></ion-
             spinner> 
        </ion-buttons> 
    </ion-navbar> 
</ion-header> 
 
<ion-content> 
     <a ion-item *ngFor="let chat of chats | async" 
      (click)="openChat(chat.$key)"> 
        <ion-avatar item-left> 
                <img *ngIf="!(chat.info | async).picture" 
                 src="img/default.jpg"> 
                <img *ngIf="(chat.info | async).picture" src="
                 {{(chat.info | async).picture}}"> 
        </ion-avatar> 
        <span>{{(chat.info | async).email}}</span> 
     </a> 
</ion-content> 

Defining the ChatViewPage

The ChatViewPage is the place where the actual chatting takes place. The following code should be present in chat-view.ts:

/* /app/pages/chat-view/chat-view.ts */ 
import {Component, ViewChild} from '@angular/core'; 
import {NavController, NavParams, Content} from 'ionic-angular'; 
import {Observable} from 'rxjs/Observable'; 
import {AngularFire, FirebaseListObservable} from 'angularfire2'; 
import {ChatsProvider} from '../../providers/chats-provider/chats-provider'; 
import {UserProvider} from '../../providers/user-provider/user-provider'; 
 
@Component({ 
  templateUrl: 'build/pages/chat-view/chat-view.html', 
}) 
export class ChatViewPage { 
  message: string; 
  uid:string; 
  interlocutor:string; 
  chats:FirebaseListObservable<any>;   
  @ViewChild(Content) content: Content; 
  constructor(public nav:NavController,  
  params:NavParams,  
  public chatsProvider:ChatsProvider,  
  public af:AngularFire,  
  public userProvider:UserProvider) { 
     
    this.uid = params.data.uid; 
    this.interlocutor = params.data.interlocutor; 
     
    // Get Chat Reference 
    chatsProvider.getChatRef(this.uid, this.interlocutor) 
    .then((chatRef:any) => {   
        this.chats = this.af.database.list(chatRef); 
    }); 
  } 
 
  ionViewDidEnter() { 
    this.content.scrollToBottom(); 
  } 
 
  sendMessage() { 
      if(this.message) { 
          let chat = { 
              from: this.uid, 
              message: this.message, 
              type: 'message' 
          }; 
          this.chats.push(chat); 
          this.message = ""; 
      } 
  }; 
   
  sendPicture() { 
      let chat = {from: this.uid, type: 'picture', picture:null}; 
      this.userProvider.getPicture() 
      .then((image) => { 
          chat.picture =  image; 
          this.chats.push(chat); 
      }); 
  } 
} 

In the constructor, we get the uid of both users and then we get the Firebase URL of their chat's endpoint, which is something like this: /chats/uid1,uid2. With this URL, we get a list of all the messages between these two users' AngularFire2 lists.

In the sendMessage function, we send chat messages using the push function of the AngularFire2 list. Similarly, in the sendPicture function, we get a picture from the user's gallery and send it as a base64 encoded string.

The ionViewDidEnter() function is an Ionic page hook. It fires each time a page is pushed in the navigation stack. We scroll to the bottom in this function using the scrollToBottom() method of ion-content.

It is important to note that we are using a @ViewChild decorator to get hold of ion-content.

Note

For further information about Ionic page life cycle hooks, take a look at http://ionicframework.com/docs/v2/api/components/nav/NavController/.

Template

The following code should be present in chat-view.html:

<!-- /app/pages/chat-view/chat-view.html --> 
<ion-header> 
  <ion-navbar primary> 
    <ion-title>Chat</ion-title> 
    <ion-buttons end> 
      <button (click)="sendPicture()"><ion-icon name="image" ></ion-
       icon>Send Image</button> 
    </ion-buttons> 
  </ion-navbar> 
</ion-header> 
 
<ion-content padding class="chat-view" id="chat-view">   
  <div class="messages"> 
      <div class="message" *ngFor="let chat of chats | async" 
       [ngClass]="{'me': uid === chat.from}"> 
            <span *ngIf="chat.message">{{chat.message}}</span> 
            <img *ngIf="chat.picture" src="{{chat.picture}}" 
             class="picture"> 
      </div> 
  </div> 
</ion-content> 
 
<ion-footer> 
  <ion-toolbar> 
    <ion-row> 
      <ion-col width-10> 
          <ion-spinner *ngIf="!(chats)"></ion-spinner> 
      </ion-col> 
       
      <ion-col width-70 [hidden]="!chats"> 
          <ion-input type="text"  placeholder="Enter Message" 
              [(ngModel)]="message"> 
          </ion-input> 
      </ion-col> 
      <ion-col width-20 [hidden]="!chats"> 
          <button full (click)="sendMessage()"><ion-icon name="send">
           </ion-icon></button> 
      </ion-col> 
    </ion-row>     
  </ion-toolbar> 
</ion-footer> 

Note

Take a look at how we have used a different class for the logged-in user's chat message, and how we have used an Ionic grid in ion-toolbar.

Style sheet

The following code should be present in chat-view.scss:

/* /app/pages/chat-view/chat-view.scss */ 
.chat-view { 
  .messages { 
    width: 100%; 
    position: absolute; 
    .message { 
      width: 70%; 
      padding: 5px 10px; 
      background: #3F51B5; 
      color: #fff; 
      border-radius: 5px; 
      margin: 5px; 
      float: left; 
    } 
    .message.me { 
      float: right; 
      background: #fff; 
      border: 1px solid #3F51B5; 
      color: #222; 
      text-align: right; 
    } 
  } 
} 

Defining the AccountPage

The AccountPage is the place where the user updates their profile picture and logs out. The following code should be present in accounts.ts:

/* /app/pages/account/account.ts */ 
import {Component} from '@angular/core'; 
import {NavController, LocalStorage, Storage} from 'ionic-angular'; 
import {AuthProvider} from '../../providers/auth-provider/auth-provider'; 
import {UserProvider} from '../../providers/user-provider/user-provider'; 
 
@Component({ 
    templateUrl: 'build/pages/account/account.html' 
}) 
export class AccountPage { 
    rootNav; 
    user = {}; 
    local = new Storage(LocalStorage); 
    constructor(public nav: NavController, public auth: AuthProvider, 
     public userProvider: UserProvider) { 
        this.userProvider.getUser() 
        .then(userObservable => { 
            userObservable.subscribe(user => { 
                this.user = user; 
            }); 
        }); 
    } 
     
    //save user info 
    updatePicture() { 
        this.userProvider.updatePicture(); 
    }; 
 
    logout() { 
        this.local.remove('userInfo'); 
        this.auth.logout(); 
    } 
} 

In the AccountPage, we are just updating the user's profile picture using the updatePicture function, and we are logging out the user using the logout function. When the user logs out, we also remove the value of the userInfo key from LocalStorage.

Template

The following code should present in accounts.html:

<!-- /app/pages/account/account.html --> 
<ion-header> 
    <ion-navbar primary> 
        <ion-title>Account</ion-title>    
    </ion-navbar> 
</ion-header> 
 
<ion-content> 
    <ion-list> 
        <ion-card> 
            <img *ngIf="!user.picture" src="img/default.jpg"/> 
            <img *ngIf="user.picture" src="{{user.picture}}"/> 
        </ion-card> 
        <ion-item> 
            <button full (click)="updatePicture()">Change 
             Picture</button> 
        </ion-item> 
        <a ion-item primary (click)="logout()"> 
            Logout 
        </a> 
    </ion-list> 
</ion-content>