Installation and Setup: 0. Prerequisites
Installation and Setup: 0. Prerequisites
0. Prerequisites
Before you start installing AngularFire2, make sure you have latest version of angular-cli installed. To
verify run the command ng -v and ensure you see angular-cli: 1.x.x-beta.xx. The lowest compatible
version is 1.x.x-beta.14.
If not, you may need to do the following:
The Angular CLI's new command will set up the latest Angular build in a new project structure.
Now that you have a new project setup, install AngularFire2 and Firebase from npm.
4. Setup @NgModule
Open /src/app/app.module.ts, inject the Firebase providers, and specify your Firebase configuration. This
can be found in your project at the Firebase Console:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { AngularFireModule } from 'angularfire2';
@NgModule({
imports: [
BrowserModule,
AngularFireModule.initializeApp(firebaseConfig)
],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule {}
5. Inject AngularFire
Open /src/app/app.component.ts, and make sure to modify/delete any tests to get the sample working
(tests are still important, you know):
import { Component } from '@angular/core';
import { AngularFire, FirebaseListObservable } from 'angularfire2';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.css']
})
export class AppComponent {
constructor(af: AngularFire) {
}
}
6. Bind to a list
In /src/app/app.component.ts:
import { Component } from '@angular/core';
import { AngularFire, FirebaseListObservable } from 'angularfire2';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.css']
})
export class AppComponent {
items: FirebaseListObservable<any[]>;
constructor(af: AngularFire) {
this.items = af.database.list('/items');
}
}
Open /src/app/app.component.html:
<ul>
<li class="text" *ngFor="let item of items | async">
{{item.$value}}
</li>
</ul>
Troubleshooting
1. Cannot find namespace 'firebase'.
If you run into this error while trying to invoke ng serve, open src/tsconfig.json and add the "types" array
as follows:
{
"compilerOptions": {
...
"typeRoots": [
"../node_modules/@types"
],
// ADD THIS
"types": [
"firebase"
]
}
}
2. Cannot find name 'require' (This is just a temporary workaround for the Angular CLI).
If you run into this error while trying to invoke ng serve, open src/typings.d.ts and add the following two
entries as follows:
declare var require: any;
declare var module: any;
Make sure you have bootstrapped your application for AngularFire2. See the Installation guide for
bootstrap setup.
AngularFire is an injectable service, which is injected through the constructor of your Angular component
or @Injectable()service.
If you've followed the earlier step "Installation and Setup" your /src/app/app.component.ts should look like
below.
import { Component } from '@angular/core';
import { AngularFire, FirebaseListObservable } from 'angularfire2';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.css']
})
export class AppComponent {
items: FirebaseListObservable<any[]>;
constructor(af: AngularFire) {
this.items = af.database.list('items');
}
}
In this section, we're going to modify the /src/app/app.component.ts to retreive data as object.
1. Relative URL
2. Absolute URL
Retrieve data
To get the object in realtime, create an object binding as a property of your component or service. Then in
your template, you can use the async pipe to unwrap the binding.
Replace the FirebaseListObservable to FirebaseObjectObservable in your /src/app/app.component.ts as
below. Also notice the templateUrl changed to inline template below:
import {Component} from '@angular/core';
import {AngularFire, FirebaseObjectObservable} from 'angularfire2';
@Component({
selector: 'app-root',
template: `
<h1>{{ (item | async)?.name }}</h1>
`,
})
export class AppComponent {
item: FirebaseObjectObservable<any>;
constructor(af: AngularFire) {
this.item = af.database.object('/item');
}
}
Saving data
API Summary
The table below highlights some of the common methods on the FirebaseObjectObservable.
method
Replaces the current value in the database with the new value specified as the parameter. This is
set(value: any)
called a destructive update, because it deletes everything currently in place and saves the new value.
update(value: Updates the current value with in the database with the new value specified as the parameter. This is
Object) called a non-destructive update, because it only updates the values specified.
remove() Deletes all data present at that location. Same as calling set(null).
Returning promises
Each data operation method in the table above returns a promise. However, you should rarely need to use
the completion promise to indicate success, because the realtime database keeps the object in sync.
The promise can be useful to chain multiple operations, catching possible errors from security rules
denials, or for debugging.
Updating data
Use the update() method for non-destructive updates.
const itemObservable = af.database.object('/item');
itemObservable.update({ age: newAge });
Only objects are allowed for updates, not primitives. This is because using an update with a primitive
is the exact same as doing a .set() with a primitive.
Deleting data
Use the remove() method to remove data at the object's location.
const itemObservable = af.database.object('/item');
itemObservable.remove();
Example app:
@Component({
selector: 'app-root',
template: `
<h1>{{ item | async | json }}</h1>
<input type="text" #newname placeholder="Name" />
<input type="text" #newsize placeholder="Size" />
<br />
<button (click)="save(newname.value)">Set Name</button>
<button (click)="update(newsize.value)">Update Size</button>
<button (click)="delete()">Delete</button>
`,
})
export class AppComponent {
item: FirebaseObjectObservable<any>;
constructor(af: AngularFire) {
this.item = af.database.object('/item');
}
save(newName: string) {
this.item.set({ name: newName });
}
update(newSize: string) {
this.item.update({ size: newSize });
}
delete() {
this.item.remove();
}
}
Data retrieved from the object binding contains special properties retrieved from the unwrapped Firebase
DataSnapshot.
property
The key for each record. This is equivalent to each record's path in our database as it would be returned
$key
by ref.key().
If the data for this child node is a primitive (number, string, or boolean), then the record itself will still be an
$value
object. The primitive value will be stored under $value and can be changed and saved like any other field.
AngularFire2 unwraps the Firebase DataSnapshot by default, but you can get the data as the original
snapshot by specifying the preserveSnapshot option.
this.item = af.database.object('/item', { preserveSnapshot: true });
this.item.subscribe(snapshot => {
console.log(snapshot.key)
console.log(snapshot.val())
});
Querying?
Because FirebaseObjectObservable synchronizes objects from the realtime database, sorting will have no
effect for queries that are not also limited by a range. For example, when paginating you would provide a
query with a sort and filter. Both the sort operation and the filter operation affect which subset of the data
is returned by the query; however, because the resulting object is simply json, the sort order will not be
preseved locally. Hence, for operations that require sorting, you are probably looking for a list
Make sure you have bootstrapped your application for AngularFire2. See the Installation guide for
bootstrap setup.
AngularFire is an injectable service, which is injected through the constructor of your Angular component
or @Injectable()service. In the previous step, we modified the /src/app/app.component.ts to retrieve data
as object. In this step, let's start with a clean slate.
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.css']
})
export class AppComponent {
constructor(af: AngularFire) {
}
}
In this section, we're going to modify the /src/app/app.component.ts to retreive data as list, but before
that let's look at ways around how to bind to a list.
1. Relative URL
2. Absolute URL
3. Query
Retrieve data
To get the list in realtime, create a list binding as a property of your component or service. Then in your
template, you can use the async pipe to unwrap the binding.
Update /src/app/app.component.ts to import FirebaseListObservable from angularfire2 and iterate thru
the list once data is retrieved. Also note the change in attribute templateUrl to inline template below.
import {Component} from '@angular/core';
import {AngularFire, FirebaseListObservable} from 'angularfire2';
@Component({
selector: 'app-root',
template: `
<ul>
<li *ngFor="let item of items | async">
{{ item | json }}
</li>
</ul>
`,
})
export class AppComponent {
items: FirebaseListObservable<any>;
constructor(af: AngularFire) {
this.items = af.database.list('/items');
}
}
Saving data
API Summary
The table below highlights some of the common methods on the FirebaseListObservable.
method
push(value: any) Creates a new record on the list, using the Realtime Database's push-ids.
remove(key: string?) Deletes the item by key. If no parameter is provided, the entire list will be deleted.
Returning promises
Each data operation method in the table above returns a promise. However, you should rarely need to use
the completion promise to indicate success, because the realtime database keeps the list in sync.
The promise can be useful to chain multiple operations, catching possible errors from security rules
denials, or for debugging.
If you omit the key parameter from .remove() it deletes the entire list.
const items = af.database.list('/items');
items.remove();
Example app
@Component({
selector: 'app-root',
template: `
<ul>
<li *ngFor="let item of items | async">
<input type="text" #updatetext [value]="item.text" />
<button (click)="updateItem(item.$key, updatetext.value)">Update</button>
<button (click)="deleteItem(item.$key)">Delete</button>
</li>
</ul>
<input type="text" #newitem />
<button (click)="addItem(newitem.value)">Add</button>
<button (click)="deleteEverything()">Delete All</button>
`,
})
export class AppComponent {
items: FirebaseListObservable<any>;
constructor(af: AngularFire) {
this.items = af.database.list('/messages');
}
addItem(newName: string) {
this.items.push({ text: newName });
}
updateItem(key: string, newText: string) {
this.items.update(key, { text: newText });
}
deleteItem(key: string) {
this.items.remove(key);
}
deleteEverything() {
this.items.remove();
}
}
Data retrieved from the object binding contains special properties retrieved from the unwrapped Firebase
DataSnapshot.
property
The key for each record. This is equivalent to each record's path in our database as it would be returned
$key
by ref.key().
If the data for this child node is a primitive (number, string, or boolean), then the record itself will still be an
$value
object. The primitive value will be stored under $value and can be changed and saved like any other field.
AngularFire2 unwraps the Firebase DataSnapshot by default, but you can get the data as the original
snapshot by specifying the preserveSnapshot option.
this.items = af.database.list('/items', { preserveSnapshot: true });
this.items
.subscribe(snapshots => {
snapshots.forEach(snapshot => {
console.log(snapshot.key)
console.log(snapshot.val())
});
})
4. Querying lists
Querying is a killer feature of AngularFire2. You can specify query values as observables, and when those
observables emit new values, the query is automatically re-run.
method purpose
equalTo 1
Limit list to items that contain certain value.
limitToFirst Sets the maximum number of items to return from the beginning of the ordered list of results.
limitToLast Sets the maximum number of items to return from the end of the ordered list of results.
1
Return items greater than or equal to the specified key or value, depending on the order-by method
startAt
chosen.
1
Return items less than or equal to the specified key or value, depending on the order-by method
endAt
chosen.
1
The Firebase SDK supports an optional key parameter for startAt, endAt, and equalTo when ordering by
child, value, or priority. You can specify the key parameter using an object literal that contains
the value and the key. For example: startAt: { value: 'some-value', key: 'some-key' }.
Queries can only be ordered by one method. This means you can only
specify orderByChild, orderByKey, orderByPriority, or orderByValue.
// WARNING: Do not copy and paste. This will not work!
const queryObservable = af.database.list('/items', {
query: {
orderByChild: 'size',
equalTo: 'large',
orderByKey: true,
}
});
You can only use limitToFirst or limitToLast, but not both in combination.
// WARNING: Do not copy and paste. This will not work!
const queryObservable = af.database.list('/items', {
query: {
limitToFirst: 10,
limitToLast: 100,
}
});
Rather than specifying regular values, observables can be used to dynamically re-run queries when the
observable emits a new value.
// subscribe to changes
queryObservable.subscribe(queriedItems => {
console.log(queriedItems);
});
Example app:
@Component({
selector: 'app-root',
template: `
<ul>
<li *ngFor="let item of items | async">
{{ item.text }}
</li>
</ul>
<div>
<h4>Filter by size</h4>
<button (click)="filterBy('small')">Small</button>
<button (click)="filterBy('medium')">Medium</button>
<button (click)="filterBy('large')">Large</button>
</div>
`,
})
export class AppComponent {
items: FirebaseListObservable<any[]>;
sizeSubject: Subject<any>;
constructor(af: AngularFire) {
this.sizeSubject = new Subject();
this.items = af.database.list('/items', {
query: {
orderByChild: 'size',
equalTo: this.sizeSubject
}
});
}
filterBy(size: string) {
this.sizeSubject.next(size);
}
}
To run the above example as is, you need to have sample data in you firebase database with the
following structure:"
-|items
-|item1
-|size: small
-|text: sample small text
-|item2
-|size: medium
-|text: sample medium text
-|item3
-|size: large
-|text: sample large text
5. User authentication
AngularFire2 allows you configure your authentication methods in the bootstrap phase of your
application. This means that if your app only uses one form of authentication, you can specify that ahead
of time so you only need to callaf.auth.login() later, and with no parameters.
To specify your authentication ahead of time, you provide the AngularFireModule.initializeApp function
with an AuthProvider and an AuthMethod.
const myFirebaseConfig = {
apiKey: '<your-key>',
authDomain: '<your-project-authdomain>',
databaseURL: '<your-database-URL>',
storageBucket: '<your-storage-bucket>',
messagingSenderId: '<your-messaging-sender-id>'
};
const myFirebaseAuthConfig = {
provider: AuthProviders.Google,
method: AuthMethods.Redirect
};
@NgModule({
imports: [
BrowserModule,
AngularFireModule.initializeApp(myFirebaseConfig, myFirebaseAuthConfig)
],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule {}
Login users
If you have setup authentication in bootstrap like above, then all you need to do is call login
on af.auth.login()
The lone exception is if you're using username and password, then you'll have to call af.auth.login() with
the user's credentials.
af.auth.login({ email: 'email', password: 'pass' });
Example app:
@Component({
selector: 'app-root',
template: `
<div> {{ (af.auth | async)?.uid }} </div>
<button (click)="login()">Login</button>
<button (click)="logout()">Logout</button>
`,
})
export class AppComponent {
constructor(public af: AngularFire) {}
login() {
this.af.auth.login();
}
logout() {
this.af.auth.logout();
}
}
Logout users
Deletes the authentication token issued by Firebase and signs user out. See Auth.signOut() in the Firebase
API reference.
Sample Usage:
signOut(): {
this.af.auth.logout();
}
Authentication works without configuration, and even if you have setup authentication in the bootstrap
phase, you can still override the configuration.
// Anonymous
af.auth.login({
provider: AuthProviders.Anonymous,
method: AuthMethods.Anonymous,
});
Before running the below example, make sure you've correctly enabled the appropriate sign-in providers in
your Firebase console under Auth tab to avoid any exceptions.
@Component({
selector: 'app-root',
template: `
<div> {{ (af.auth | async)?.uid }} </div>
<button (click)="login()">Login With Twitter</button>
<button (click)="overrideLogin()">Login Anonymously</button>
`,
})
export class AppComponent {
constructor(public af: AngularFire) {
this.af.auth.subscribe(auth => console.log(auth));
}
login() {
this.af.auth.login({
provider: AuthProviders.Twitter,
method: AuthMethods.Popup,
});
}
overrideLogin() {
this.af.auth.login({
provider: AuthProviders.Anonymous,
method: AuthMethods.Anonymous,
});
}
}
Cordova case
Firebase authentication wasn't entirely compatible with cordova. You need to add some specific
operations.
Example:
Login with Facebook.
firebase.auth().signInWithCredential(provider)
.then((success) => {
console.log("Firebase success: " + JSON.stringify(success));
})
.catch((error) => {
console.log("Firebase failure: " + JSON.stringify(error));
});
Ensure that you're executing these commands as Administrator on Windows and sudo on Mac/Linux to
avoid any errors.
Prerequisites
The first step is to ensure you've latest version of Node installed. You can get the latest version from here.
This will install both node and npm.
After installing node, check the version by executing the following command in your prompt window.
C:\projects>node -v
v6.9.1
As of writting this document, this is the most stable version. If you're not on this version, please upgrade
yourself to latest version by installing node from here.
C:\projects>npm -v
3.10.8
Your Application code sits on top of Ionic Framework and the Ionic Framework sits on top of
Cordova.
Let's get them installed globally, so that all projects can use them.
C:\projects>cordova -v
6.4.0
C:\projects>ionic -v
2.1.8
These are the latest versions as of writting this document.
On successful execution of above commands, you're all set to create your Ionic 2 app. To create your app,
change into the directory where you want your app to reside and execute the following command
C:\projects\Ionic_AngularFire2_Project>
To start your app, execute the following command
Note:- typings is not required for our current application to work, but it will be helpful incase you want to
bring in external libraries and extend this application.
C:\projects\Ionic_AngularFire2_Project>typings -v
2.0.0
C:\projects\Ionic_AngularFire2_Project>tsc -v
Version 2.0.10
"angularfire2": "^2.0.0-beta.6",
"firebase": "^3.6.1",
Setup @NgModule
Open your project in your favourite editor and open the app.module.ts file, under src/app and add the
following three entries.
1) Import AngularFireModule at top.
@NgModule({
declarations: [
MyApp,
HomePage
],
imports: [
IonicModule.forRoot(MyApp),
AngularFireModule.initializeApp(firebaseConfig)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage
],
providers: []
})
export class AppModule { }
items: FirebaseListObservable<any[]>;
}
Update your home.html at src/pages/home/home.html, with following entry
<ion-header>
<ion-navbar>
<ion-title>
Ionic Blank
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-list>
<ion-item class="text" *ngFor="let item of items | async">
{{item.$value}}
</ion-item>
</ion-list>
</ion-content>
** Run your app by executing the following command **
Continuing with the above example stop your server by pressing ctrl+c and go to command prompt and
generate a service by executing the following command
C:\projects\Ionic_AngularFire2_Project> ionic g provider AuthService
This should create the AuthService under src/providers/auth-service.ts. Update the service with the
following code.
import { Injectable } from '@angular/core';
import { AuthProviders, AngularFireAuth, FirebaseAuthState, AuthMethods } from 'angularfire2';
@Injectable()
export class AuthService {
private authState: FirebaseAuthState;
signOut(): void {
this.auth$.logout();
}
displayName(): string {
if (this.authState != null) {
return this.authState.facebook.displayName;
} else {
return '';
}
}
}
Add your service in app.module.ts. Your app.module.ts should look like this
import { NgModule } from '@angular/core';
import { IonicApp, IonicModule } from 'ionic-angular';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
@NgModule({
declarations: [
MyApp,
HomePage
],
imports: [
IonicModule.forRoot(MyApp),
AngularFireModule.initializeApp(firebaseConfig)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage
],
providers: [AuthService]
})
export class AppModule { }
Update your home.html to add a login button. Your home.html should look like this
<ion-header>
<ion-navbar>
<ion-title>
Ionic Blank
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-list>
<ion-item class="text" *ngFor="let item of items | async">
{{item | json}}
</ion-item>
</ion-list>
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
items: FirebaseListObservable<any[]>;
signInWithFacebook(): void {
this._auth.signInWithFacebook()
.then(() => this.onSignInSuccess());
}
}
Now run your app and if everything is configured correctly, you should be able to click on the login
button in your app, which should open the facebook pop-up.
Once you authenticate yourself, you should see your Facebook display name in console.
You can try redirecting yourself to another page to grab additional details from Facebook.
Ensure you've the platform added to your project. If not add the platform by executing the following
command.
This adds android platform for your project. Replace android with ios, if you're on Mac book or add both.
The generic command is ionic platform add <platform-name>
Now, let's try to run the app in browser. Execute the command
We can access these corodva plugins, using Ionic Native, which are nothing but wrappers for
cordova plugins.
List of all Ionic Native API's for corodova plugins can be found here.
Let's look at configuring and installing facebook plugin here. Ensure you follow the steps correctly to
configure your app.
Once you create your app and make a note of your App ID, go to command prompt in your project
directory and execute the following command
C:\projects\Ionic_AngularFire2_Project>
ionic plugin add cordova-plugin-facebook4 --save --variable APP_ID="123456789" --variable
APP_NAME="myApp"
Replace App ID with your app id from portal and provide your app name.
Add the platform to your facebook portal as mentioned in the document here.
@Injectable()
export class AuthService {
private authState: FirebaseAuthState;
signInWithFacebook(): firebase.Promise<FirebaseAuthState> {
if (this.platform.is('cordova')) {
return Facebook.login(['email', 'public_profile']).then(res => {
const facebookCredential =
firebase.auth.FacebookAuthProvider.credential(res.authResponse.accessToken);
return firebase.auth().signInWithCredential(facebookCredential);
});
} else {
return this.auth$.login({
provider: AuthProviders.Facebook,
method: AuthMethods.Popup
});
}
signOut(): void {
this.auth$.logout();
}
displayName(): string {
if (this.authState != null) {
return this.authState.facebook.displayName;
} else {
return '';
}
}
}
C:\projects\Ionic_AngularFire2_Project>ionic serve
Everything should work. Now trying running the app on your android phone
Once the App launches click on the Facebook login button and it should open up the native facebook app
for authentication and once your enter credentials and gets succesfully authenticated, it should redirect
you back to the home page.