- Initialization
$firebaseObject
$firebaseArray
$firebaseAuth
- Authentication
- User Management
- Router Helpers
- Extending the Services
- SDK Compatibility
- Browser Compatibility
var app = angular.module("app", ["firebase"]);
app.config(function() {
var config = {
apiKey: "<API_Key>", // Your Firebase API key
authDomain: "<AUTH_DOMAIN>", // Your Firebase Auth domain ("*.firebaseapp.com")
databaseURL: "<DATABASE_URL>", // Your Firebase Database URL ("https://*.firebaseio.com")
storageBucket: "<STORAGE_BUCKET>" // Your Firebase Storage bucket ("*.appspot.com")
};
firebase.initializeApp(config);
});
The $firebaseObject
service takes an optional firebase.database.Reference
or
firebase.database.Query
and returns a JavaScript object which contains the data at the
provided location in Firebase and some extra AngularFire-specific fields. If no Reference
or Query
is provided, then the root of the Firebase Database will be used.
Note that the data will
not be available immediately since retrieving it is an asynchronous operation. You can use the
$loaded()
promise to get notified when the data has loaded.
This service automatically keeps local objects in sync with any changes made to the remote Firebase database.
However, note that any changes to that object will not automatically result in any changes
to the remote data. All such changes will have to be performed by updating the object directly and
then calling $save()
on the object, or by utilizing $bindTo()
(see more below).
app.controller("MyCtrl", ["$scope", "$firebaseObject",
function($scope, $firebaseObject) {
var ref = firebase.database().ref();
var obj = $firebaseObject(ref);
// to take an action after the data loads, use the $loaded() promise
obj.$loaded().then(function() {
console.log("loaded record:", obj.$id, obj.someOtherKeyInData);
// To iterate the key/value pairs of the object, use angular.forEach()
angular.forEach(obj, function(value, key) {
console.log(key, value);
});
});
// To make the data available in the DOM, assign it to $scope
$scope.data = obj;
// For three-way data bindings, bind it to the scope instead
obj.$bindTo($scope, "data");
}
]);
The key where this record is stored. The same as obj.$ref().key
.
The priority for this record according to the last update we received. Modifying this value
and then calling $save()
will also update the server's priority.
IMPORTANT NOTE: Because Angular's $watch()
function ignores keys prefixed with $
, changing
this field inside the $bindTo()
function will not trigger an update unless a field without a $
prefix is also updated. It is best to avoid using $bindTo()
for editing $
variables and just
rely on the $save()
method.
If the value in the database is a primitive (boolean, string, or number) then the value will
be stored under this $value
key. Modifying this value and then calling $save()
will also
update the server's value.
Note that any time other keys exist, this one will be ignored. To change an object to a primitive value, delete the other keys and add this key to the object. As a shortcut, we can use:
var obj = $firebaseObject(ref); // an object with data keys
$firebaseUtils.updateRec(obj, newPrimitiveValue); // updateRec will delete the other keys for us
IMPORTANT NOTE: Because Angular's $watch()
function ignores keys prefixed with $
, changing
this field inside the $bindTo()
function will not trigger an update unless a field without a $
prefix is also updated. It is best to avoid using $bindTo()
for editing $
variables and just
rely on the $save()
method.
Removes the entire object locally and from the database. This method returns a promise that will be
fulfilled when the data has been removed from the server. The promise will be resolved with a
Firebase
reference for the exterminated record.
var obj = $firebaseObject(ref);
obj.$remove().then(function(ref) {
// data has been deleted locally and in the database
}, function(error) {
console.log("Error:", error);
});
If changes are made to data, then calling $save()
will push those changes to the server. This
method returns a promise that will resolve with this object's Firebase
reference when the write
is completed.
var obj = $firebaseObject(ref);
obj.foo = "bar";
obj.$save().then(function(ref) {
ref.key === obj.$id; // true
}, function(error) {
console.log("Error:", error);
});
Returns a promise which is resolved asynchronously when the initial object data has been downloaded
from the database. The promise resolves to the $firebaseObject
itself.
var obj = $firebaseObject(ref);
obj.$loaded()
.then(function(data) {
console.log(data === obj); // true
})
.catch(function(error) {
console.error("Error:", error);
});
As a shortcut, the resolve()
/ reject()
methods can optionally be passed directly into $loaded()
:
var obj = $firebaseObject(ref);
obj.$loaded(
function(data) {
console.log(data === obj); // true
},
function(error) {
console.error("Error:", error);
}
);
Returns the Firebase
reference used to create this object.
var obj = $firebaseObject(ref);
obj.$ref() === ref; // true
Creates a three-way binding between a scope variable and the database data. When the scope
data is
updated, changes are pushed to the database, and when changes occur in the database, they are pushed
instantly into scope
. This method returns a promise that resolves after the initial value is
pulled from the database and set in the scope
variable.
var ref = firebase.database().ref(); // assume value here is { foo: "bar" }
var obj = $firebaseObject(ref);
obj.$bindTo($scope, "data").then(function() {
console.log($scope.data); // { foo: "bar" }
$scope.data.foo = "baz"; // will be saved to the database
ref.set({ foo: "baz" }); // this would update the database and $scope.data
});
We can now bind to any property on our object directly in the HTML, and have it saved instantly to the database. Security and Firebase Rules can be used for validation to ensure data is formatted correctly at the server.
<!--
This input field has three-way data binding to the database
(changing value updates remote data; remote changes are applied here)
-->
<input type="text" ng-model="data.foo" />
Only one scope variable can be bound at a time. If a second attempts to bind to the same
$firebaseObject
instance, the promise will be rejected and the bind will fail.
IMPORTANT NOTE: Angular does not report variables prefixed with $
to any $watch()
listeners.
a simple workaround here is to use a variable prefixed with _
, which will not be saved to the
server, but will trigger $watch()
.
var obj = $firebaseObject(ref);
obj.$bindTo($scope, "widget").then(function() {
$scope.widget.$priority = 99;
$scope.widget._updated = true;
})
If $destroy()
is emitted by scope
(this happens when a controller is destroyed), then this
object is automatically unbound from scope
. It can also be manually unbound using the
unbind()
method, which is passed into the promise callback.
var obj = $firebaseObject(ref);
obj.$bindTo($scope, "data").then(function(unbind) {
// unbind this later
//unbind();
});
Registers an event listener which will be notified any time there is a change to the data. Returns an unregister function that, when invoked, will stop notifying the callback of changes.
var obj = $firebaseObject(ref);
var unwatch = obj.$watch(function() {
console.log("data changed!");
});
// at some time in the future, we can unregister using
unwatch();
Calling this method cancels event listeners and frees memory used by this object (deletes the local data). Changes are no longer synchronized to or from the database.
Attribute which represents the loaded state for this object. Its value will be true
if the initial
object data has been downloaded from the database; otherwise, its value will be false
. This
attribute is complementary to the $loaded()
method. If the $loaded()
promise is completed
(either with success or rejection), then $resolved
will be true
. $resolved
will be
false
before that.
Knowing if the object has been resolved is useful to conditionally show certain parts of your view:
$scope.obj = $firebaseObject(ref);
<!-- Loading state -->
<div ng-if="!obj.$resolved">
...
</div>
<!-- Loaded state -->
<div ng-if="obj.$resolved">
...
</div>
The $firebaseArray
service takes an optional firebase.database.Reference
or
firebase.database.Query
and returns a JavaScript array which contains the data at the
provided location in Firebase and some extra AngularFire-specific fields. If no Reference
or Query
is provided, then the root of the Firebase Database will be used. Note that the data will not be available immediately since retrieving
it is an asynchronous operation. You can use the $loaded()
promise to get notified when the data
has loaded.
This service automatically keeps this local array in sync with any changes made to the remote
database. This is a PSEUDO READ-ONLY ARRAY suitable for use in directives like ng-repeat
and with Angular filters (which expect an array).
While using read attributes and methods like length
and toString()
will work great on this array,
you should avoid directly manipulating the array. Methods like splice()
, push()
, pop()
,
shift()
, unshift()
, and reverse()
will cause the local data to become out of sync with the
server. Instead, utilize the $add()
, $remove()
, and $save()
methods provided by the service to
change the structure of the array. To get the id of an item in a $firebaseArray within ng-repeat
, call $id
on that item.
// JavaScript
app.controller("MyCtrl", ["$scope", "$firebaseArray",
function($scope, $firebaseArray) {
var ref = firebase.database().ref();
var list = $firebaseArray(ref);
// add an item
list.$add({ foo: "bar" }).then(...);
// remove an item
list.$remove(2).then(...);
// make the list available in the DOM
$scope.list = list;
}
]);
<!-- HTML -->
<li ng-repeat="item in list | filter:name">{{ item | json }}</li>
The $firebaseArray
service can also take a
query to only sync
a subset of data.
app.controller("MyCtrl", ["$scope", "$firebaseArray",
function($scope, $firebaseArray) {
var ref = firebase.database().ref();
var messagesRef = ref.child("messages");
var query = messagesRef.orderByChild("timestamp").limitToLast(10);
var list = $firebaseArray(query);
}
]);
Note that, while the array itself should not be modified, it is practical to change specific elements of the array and save them back to the remote database:
// JavaScript
var list = $firebaseArray(ref);
list[2].foo = "bar";
list.$save(2);
<!-- HTML -->
<li ng-repeat="item in list">
<input ng-model="item.foo" ng-change="list.$save(item)" />
</li>
Creates a new record in the database and adds the record to our local synchronized array.
This method returns a promise which is resolved after data has been saved to the server.
The promise resolves to the Firebase
reference for the newly added record, providing
easy access to its key.
var list = $firebaseArray(ref);
list.$add({ foo: "bar" }).then(function(ref) {
var id = ref.key;
console.log("added record with id " + id);
list.$indexFor(id); // returns location in the array
});
Remove a record from the database and from the local array. This method returns a promise that
resolves after the record is deleted at the server. It will contain a Firebase
reference to
the deleted record. It accepts either an array index or a reference to an item that
exists in the array.
var list = $firebaseArray(ref);
var item = list[2];
list.$remove(item).then(function(ref) {
ref.key === item.$id; // true
});
The array itself cannot be modified, but records in the array can be updated and saved back to the database individually. This method saves an existing, modified local record back to the database. It accepts either an array index or a reference to an item that exists in the array.
$scope.list = $firebaseArray(ref);
<li ng-repeat="item in list">
<input type="text" ng-model="item.title" ng-change="list.$save(item)" />
</li>
This method returns a promise which is resolved after data has been saved to the server.
The promise resolves to the Firebase
reference for the saved record, providing easy
access to its key.
var list = $firebaseArray(ref);
list[2].foo = "bar";
list.$save(2).then(function(ref) {
ref.key === list[2].$id; // true
});
Returns the record from the array for the given key. If the key is not found, returns null
.
This method utilizes $indexFor(key)
to find the appropriate record.
var list = $firebaseArray(ref);
var rec = list.$getRecord("foo"); // record with $id === "foo" or null
Returns the key for a record in the array. It accepts either an array index or a reference to an item that exists in the array.
// assume records "alpha", "bravo", and "charlie"
var list = $firebaseArray(ref);
list.$keyAt(1); // bravo
list.$keyAt( list[1] ); // bravo
The inverse of $keyAt()
, this method takes a key and finds the associated record in the array.
If the record does not exist, -1 is returned.
// assume records "alpha", "bravo", and "charlie"
var list = $firebaseArray(ref);
list.$indexFor("alpha"); // 0
list.$indexFor("bravo"); // 1
list.$indexFor("zulu"); // -1
Returns a promise which is resolved asynchronously when the initial array data has been downloaded
from the database. The promise resolves to the $firebaseArray
.
var list = $firebaseArray(ref);
list.$loaded()
.then(function(x) {
x === list; // true
})
.catch(function(error) {
console.log("Error:", error);
});
The resolve/reject methods may also be passed directly into $loaded:
var list = $firebaseArray(ref);
list.$loaded(
function(x) {
x === list; // true
}, function(error) {
console.error("Error:", error);
});
Returns the Firebase
reference used to create this array.
var list = $firebaseArray(ref);
sync === list.$ref(); // true
Any callback passed here will be invoked each time data in the array is updated from the server. The callback receives an object with the following keys:
event
: The database event type which fired (child_added
,child_moved
,child_removed
, orchild_changed
).key
: The ID of the record that triggered the event.prevChild
: If event ischild_added
orchild_moved
, this contains the previous record's key ornull
ifkey
belongs to the first record in the collection.
var list = $firebaseArray(ref);
list.$watch(function(event) {
console.log(event);
});
// logs { event: "child_removed", key: "foo" }
list.$remove("foo");
// logs { event: "child_added", key: "<new_id>", prevId: "<prev_id>" }
list.$add({ hello: "world" });
A common use case for this would be to customize the sorting for a synchronized array. Since
each time an add or update arrives from the server, the data could become out of order, we
can re-sort on each event. We don't have to worry about excessive re-sorts slowing down Angular's
compile process, or creating excessive DOM updates, because the events are already batched
nicely into a single $apply
event (we gather them up and trigger the events in batches before
telling $digest
to dirty check).
var list = $firebaseArray(ref);
// sort our list
list.sort(compare);
// each time the server sends records, re-sort
list.$watch(function() { list.sort(compare); });
// custom sorting routine (sort by last name)
function compare(a, b) {
return a.lastName.localeCompare(b.lastName);
}
Stop listening for events and free memory used by this array (empties the local copy). Changes are no longer synchronized to or from the database.
Attribute which represents the loaded state for this array. Its value will be true
if the initial
array data has been downloaded from the database; otherwise, its value will be false
. This
attribute is complementary to the $loaded()
method. If the $loaded()
promise is completed
(either with success or rejection), then $resolved
will be true
. $resolved
will be
false
before that.
Knowing if the array has been resolved is useful to conditionally show certain parts of your view:
$scope.list = $firebaseArray(ref);
<!-- Loading state -->
<div ng-if="!list.$resolved">
...
</div>
<!-- Loaded state -->
<div ng-if="list.$resolved">
...
</div>
AngularFire includes support for user authentication and management
with the $firebaseAuth
service.
The $firebaseAuth
factory takes an optional Firebase auth instance (firebase.auth()
) as its only
argument. Note that the authentication state is global to your application, even if multiple
$firebaseAuth
objects are created unless you use multiple Firebase apps.
app.controller("MyAuthCtrl", ["$scope", "$firebaseAuth",
function($scope, $firebaseAuth) {
$scope.authObj = $firebaseAuth();
}
]);
The authentication object returned by $firebaseAuth
contains several methods for authenticating
users, responding to changes in authentication state, and managing user accounts for email /
password users.
Authenticates the client using a custom authentication token. This function takes two arguments: an authentication token or a Firebase Secret and an object containing optional client arguments, such as configuring session persistence.
$scope.authObj.$signInWithCustomToken("<CUSTOM_AUTH_TOKEN>").then(function(firebaseUser) {
console.log("Signed in as:", firebaseUser.uid);
}).catch(function(error) {
console.error("Authentication failed:", error);
});
This method returns a promise which is resolved or rejected when the authentication attempt is
completed. If successful, the promise will be fulfilled with an object containing the payload of
the authentication token. If unsuccessful, the promise will be rejected with an Error
object.
Read our Custom Authentication guide for more details about generating your own custom authentication tokens.
Authenticates the client using a new, temporary guest account.
$scope.authObj.$signInAnonymously().then(function(firebaseUser) {
console.log("Signed in as:", firebaseUser.uid);
}).catch(function(error) {
console.error("Authentication failed:", error);
});
This method returns a promise which is resolved or rejected when the authentication attempt is
completed. If successful, the promise will be fulfilled with an object containing authentication
data about the signed-in user. If unsuccessful, the promise will be rejected with an Error
object.
Read our documentation on anonymous authentication for more details about anonymous authentication.
Authenticates the client using an email / password combination. This function takes two
arguments: an object containing email
and password
attributes corresponding to the user account
and an object containing optional client arguments, such as configuring session persistence.
$scope.authObj.$signInWithEmailAndPassword("[email protected]", "password").then(function(firebaseUser) {
console.log("Signed in as:", firebaseUser.uid);
}).catch(function(error) {
console.error("Authentication failed:", error);
});
This method returns a promise which is resolved or rejected when the authentication attempt is
completed. If successful, the promise will be fulfilled with an object containing authentication
data about the signed-in user. If unsuccessful, the promise will be rejected with an Error
object.
Read our documentation on email / password authentication for more details about email / password authentication.
Authenticates the client using a popup-based OAuth flow. This function takes two
arguments: the unique string identifying the OAuth provider to authenticate with (e.g. "google"
).
Optionally, you can pass a provider object (like new firebase.auth.GoogleAuthProvider()
, etc)
which can be configured with additional options.
$scope.authObj.$signInWithPopup("google").then(function(result) {
console.log("Signed in as:", result.user.uid);
}).catch(function(error) {
console.error("Authentication failed:", error);
});
This method returns a promise which is resolved or rejected when the authentication attempt is
completed. If successful, the promise will be fulfilled with an object containing authentication
data about the signed-in user. If unsuccessful, the promise will be rejected with an Error
object.
Firebase currently supports Facebook, GitHub, Google, and Twitter authentication. Refer to authentication documentation for information about configuring each provider.
Authenticates the client using a redirect-based OAuth flow. This function takes two
arguments: the unique string identifying the OAuth provider to authenticate with (e.g. "google"
).
Optionally, you can pass a provider object (like new firebase.auth().GoogleProvider()
, etc)
which can be configured with additional options.
$scope.authObj.$signInWithRedirect("google").then(function() {
// Never called because of page redirect
}).catch(function(error) {
console.error("Authentication failed:", error);
});
This method returns a rejected promise with an Error
object if the authentication attempt fails.
Upon successful authentication, the browser will be redirected as part of the OAuth authentication
flow. As such, the returned promise will never be fulfilled. Instead, you should use the $onAuthStateChanged()
method to detect when the authentication has been successfully completed.
Firebase currently supports Facebook, GitHub, Google, and Twitter authentication. Refer to authentication documentation for information about configuring each provider.
Authenticates the client using a credential (potentially created from OAuth Tokens). This function takes one
arguments: the credential object. This may be obtained from individual auth providers under firebase.auth()
;
$scope.authObj.$signInWithCredential(credential).then(function(firebaseUser) {
console.log("Signed in as:", firebaseUser.uid);
}).catch(function(error) {
console.error("Authentication failed:", error);
});
This method returns a promise which is resolved or rejected when the authentication attempt is
completed. If successful, the promise will be fulfilled with an object containing authentication
data about the signed-in user. If unsuccessful, the promise will be rejected with an Error
object.
Firebase currently supports Facebook, GitHub, Google, and Twitter authentication. Refer to authentication documentation for information about configuring each provider.
Synchronously retrieves the current authentication state of the client. If the user is
authenticated, an object containing the fields uid
(the unique user ID), provider
(string
identifying the provider), auth
(the authentication token payload), and expires
(expiration
time in seconds since the Unix epoch) - and more, depending upon the provider used to authenticate -
will be returned. Otherwise, the return value will be null
.
var firebaseUser = $scope.authObj.$getAuth();
if (firebaseUser) {
console.log("Signed in as:", firebaseUser.uid);
} else {
console.log("Signed out");
}
Listens for changes to the client's authentication state. The provided callback
will fire when
the client's authenticate state changes. If authenticated, the callback will be passed an object
containing the fields uid
(the unique user ID), provider
(string identifying the provider),
auth
(the authentication token payload), and expires
(expiration time in seconds since the Unix
epoch) - and more, depending upon the provider used to authenticate. Otherwise, the callback will
be passed null
.
$scope.authObj.$onAuthStateChanged(function(firebaseUser) {
if (firebaseUser) {
console.log("Signed in as:", firebaseUser.uid);
} else {
console.log("Signed out");
}
});
This method can also take an optional second argument which, if provided, will be used as this
when calling your callback.
This method returns a function which can be used to unregister the provided callback
. Once the
callback
is unregistered, changes in authentication state will not cause the callback
to fire.
var offAuth = $scope.authObj.$onAuthStateChanged(callback);
// ... sometime later, unregister the callback
offAuth();
Signs out a client. It takes no arguments and returns an empty Promise
when the client has been
signed out. Upon fulfillment, the $onAuthStateChanged()
callback(s) will be triggered.
<span ng-show="firebaseUser">
{{ firebaseUser.displayName }} | <a href="#" ng-click="authObj.$signOut()">Sign out</a>
</span>
Creates a new user account using an email / password combination. This function returns a promise that is resolved with an object containing user data about the created user.
$scope.authObj.$createUserWithEmailAndPassword("[email protected]", "mypassword")
.then(function(firebaseUser) {
console.log("User " + firebaseUser.uid + " created successfully!");
}).catch(function(error) {
console.error("Error: ", error);
});
Note that this function both creates the new user and authenticates as the new user.
Changes the password of the currently signed-in user. This function returns a promise that is resolved when the password has been successfully changed on the Firebase Authentication servers.
$scope.authObj.$updatePassword("newPassword").then(function() {
console.log("Password changed successfully!");
}).catch(function(error) {
console.error("Error: ", error);
});
Changes the email of the currently signed-in user. This function returns a promise that is resolved when the email has been successfully changed on the Firebase Authentication servers.
$scope.authObj.$updateEmail("[email protected]").then(function() {
console.log("Email changed successfully!");
}).catch(function(error) {
console.error("Error: ", error);
});
Deletes the currently authenticated user. This function returns a promise that is resolved when the user has been successfully removed on the Firebase Authentication servers.
$scope.authObj.$deleteUser().then(function() {
console.log("User removed successfully!");
}).catch(function(error) {
console.error("Error: ", error);
});
Note that removing a user also logs that user out and will therefore fire any onAuthStateChanged()
callbacks that you have created.
Sends a password-reset email to the owner of the account, containing a token that may be used to authenticate and change the user's password. This function returns a promise that is resolved when the email notification has been sent successfully.
$scope.authObj.$sendPasswordResetEmail("[email protected]").then(function() {
console.log("Password reset email sent successfully!");
}).catch(function(error) {
console.error("Error: ", error);
});
Helper method which returns a promise fulfilled with the current authentication state. This is
intended to be used in the resolve()
method of Angular routers. See the
"Using Authentication with Routers"
section of our AngularFire guide for more information and a full example.
Helper method which returns a promise fulfilled with the current authentication state if the user
is authenticated but otherwise rejects the promise. This is intended to be used in the resolve()
method of Angular routers to prevented unauthenticated users from seeing authenticated pages
momentarily during page load. See the
"Using Authentication with Routers"
section of our AngularFire guide for more information and a full example.
There are several powerful techniques for transforming the data downloaded and saved
by $firebaseArray
and $firebaseObject
. These techniques should only be attempted
by advanced Angular users who know their way around the code.
You can create a new factory from a $firebaseObject
. It can add additional methods or override any existing method.
var ColorFactory = $firebaseObject.$extend({
getMyFavoriteColor: function() {
return this.favoriteColor + ", no green!"; // obscure Monty Python reference
}
});
var factory = new ColorFactory(ref);
var favColor = factory.getMyFavoriteColor();
This technique can also be used to transform how data is stored and saved by overriding the following private methods:
- $$updated: Called with a snapshot any time a
value
event is received from the database, must apply the updates and return true if any changes occurred. - $$error: Passed an
Error
any time a security error occurs. These are generally not recoverable. - $$notify: Sends notifications to any listeners registered with
$watch()
. - toJSON: As with any object, if a
toJSON()
method is provided, it will be used byJSON.stringify()
to parse the JSON content before it is saved to the database. - $$defaults: A key / value pair that can be used to create default values for any fields which are not found in the server data (i.e. undefined fields). By default, they are applied each time
$$updated
is invoked. If that method is overridden, it would need to implement this behavior.
// Add a counter to our object...
var FactoryWithCounter = $firebaseObject.$extend({
// add a method to the prototype that returns our counter
getUpdateCount: function() { return this._counter; },
// each time an update arrives from the server, apply the change locally
$$updated: function(snap) {
// apply the changes using the super method
var changed = $firebaseObject.prototype.$$updated.apply(this, arguments);
// add / increment a counter each time there is an update
if( !this._counter ) { this._counter = 0; }
this._counter++;
// return whether or not changes occurred
return changed;
}
});
You can create a new factory from a $firebaseArray
. It can add additional methods or override any existing method.
app.factory("ArrayWithSum", function($firebaseArray) {
return $firebaseArray.$extend({
sum: function() {
var total = 0;
angular.forEach(this.$list, function(rec) {
total += rec.x;
});
return total;
}
});
})
We can then use this factory with by instantiating it:
var list = new ArrayWithSum(ref);
list.$loaded().then(function() {
console.log("List has " + list.sum() + " items");
});
This technique can be used to transform how data is stored by overriding the following private methods:
- $$added: Called with a snapshot and prevChild any time a
child_added
event occurs. - $$updated: Called with a snapshot any time a
child_changed
event occurs. - $$moved: Called with a snapshot and prevChild any time
child_moved
event occurs. - $$removed: Called with a snapshot any time a
child_removed
event occurs. - $$error: Passed an
Error
any time a security error occurs. These are generally not recoverable. - $$getKey: Tells AngularFire what the unique ID is for each record (the default just returns
this.$id
). - $$notify: Notifies any listeners registered with $watch; normally this method would not be modified.
- $$process: Handles the actual splicing of data into and out of the array. Normally this method would not be modified.
- $$defaults: A key / value pair that can be used to create default values for any fields which are not found in the server data (i.e. undefined fields). By default, they are applied each time
$$added
or$$updated
are invoked. If those methods are overridden, they would need to implement this behavior.
To illustrate, let's create a factory that creates Widget
instances, and transforms dates:
// an object to return in our WidgetFactory
app.factory("Widget", function($firebaseUtils) {
function Widget(snapshot) {
// store the record id so AngularFire can identify it
this.$id = snapshot.key;
// apply the data
this.update(snapshot);
}
Widget.prototype = {
update: function(snapshot) {
var oldData = angular.extend({}, this.data);
// apply changes to this.data instead of directly on `this`
this.data = snapshot.val();
// add a parsed date to our widget
this._date = new Date(this.data.date);
// determine if anything changed, note that angular.equals will not check
// $value or $priority (since it excludes anything that starts with $)
// so be careful when using angular.equals()
return !angular.equals(this.data, oldData);
},
getDate: function() {
return this._date;
},
toJSON: function() {
// since we changed where our data is stored, we need to tell AngularFire how
// to get the JSON version of it. We can use $firebaseUtils.toJSON() to remove
// private variables, copy the data into a shippable format, and do validation
return $firebaseUtils.toJSON(this.data);
}
};
return Widget;
});
// now let's create a synchronized array factory that uses our Widget
app.factory("WidgetFactory", function($firebaseArray, Widget) {
return $firebaseArray.$extend({
// change the added behavior to return Widget objects
$$added: function(snap) {
// instead of creating the default POJO (plain old JavaScript object)
// we will return an instance of the Widget class each time a child_added
// event is received from the server
return new Widget(snap);
},
// override the update behavior to call Widget.update()
$$updated: function(snap) {
// we need to return true/false here or $watch listeners will not get triggered
// luckily, our Widget.prototype.update() method already returns a boolean if
// anything has changed
return this.$getRecord(snap.key.update(snap);
}
});
});
Instead of just a list of functions, we can also pass in a class constructor to inherit methods from
$firebaseArray
. The prototype for this class will be preserved, and it will inherit
from $firebaseArray
.
This is an extremely advanced feature. Do not use this unless you know that you need it
This class constructor is expected to call $firebaseArray
's constructor (i.e. the super constructor).
The following factory adds an update counter which is incremented each time $$added()
or $$updated()
is called:
app.factory("ArrayWithCounter", function($firebaseArray, Widget) {
// $firebaseArray and $firebaseObject constructors both accept a single argument, a `Firebase` ref
function ArrayWithCounter(ref) {
// initialize a counter
this.counter = 0;
// call the super constructor
return $firebaseArray.call(this, ref);
}
// override the add behavior to return a Widget
ArrayWithCounter.prototype.$$added = function(snap) {
return new Widget(snap);
};
// override the update behavior to call Widget.update()
ArrayWithCounter.prototype.$$updated = function(snap) {
var widget = this.$getRecord(snap.key;
return widget.update();
};
// pass our constructor to $extend, which will automatically extract the
// prototype methods and call the constructor appropriately
return $firebaseArray.$extend(ArrayWithCounter);
});
In general, it will be more useful to extend the services to create new services than
to use this technique. However, it is also possible to modify $firebaseArray
or
$firebaseObject
globally by using Angular's $decorate()
method.
app.config(function($provide) {
$provide.decorator("$firebaseObject", function($delegate, $firebaseUtils) {
var _super = $delegate.prototype.$$updated;
// override any instance of $firebaseObject to look for a date field
// and transforms it to a Date object.
$delegate.prototype.$$updated = function(snap) {
var changed = _super.call(this, snap);
if( this.hasOwnProperty("date") ) {
this._dateObj = new Date(this.date);
}
return changed;
};
// add a method that fetches the date object we just created
$delegate.prototype.getDate = function() {
return this._dateObj;
};
// variables starting with _ are ignored by AngularFire so we don't need
// to worry about the toJSON method here
return $delegate;
});
});
With the ability to extend the AngularFire services, services can be built to represent
our synchronized collections with a minimal amount of code. For example, we can create
a User
factory:
// create a User factory with a getFullName() method
app.factory("UserFactory", function($firebaseObject) {
return $firebaseObject.$extend({
getFullName: function() {
// concatenate first and last name
return this.first_name + " " + this.last_name;
}
});
});
And create a new instance:
// create a User object from our Factory
app.factory("User", function(UserFactory) {
var ref = firebase.database().ref();
var usersRef = ref.child("users");
return function(userid) {
return new UserFactory(usersRef.child(userid));
}
});
Similarly, we can extend $firebaseArray
by creating a Message
object:
app.factory("Message", function($firebaseArray) {
function Message(snap) {
// store the user ID so AngularFire can identify the records
// in this case, we store it in a custom location, so we'll need
// to override $$getKey
this.message_id = snap.key;
this.message = snap.val();
}
Message.prototype = {
update: function(snap) {
// store a string into this.message (instead of the default $value)
if( snap.val() !== this.message ) {
this.message = snap.val();
return true;
}
return false;
},
toJSON: function() {
// tell AngularFire what data to save, in this case a string
return this.message;
}
};
return Message;
});
We can then use that to extend the $firebaseArray
factory:
app.factory("MessageFactory", function($firebaseArray, Message) {
return $firebaseArray.$extend({
// override the $createObject behavior to return a Message object
$$added: function(snap) {
return new Message(snap);
},
// override the $$updated behavior to call a method on the Message
$$updated: function(snap) {
var msg = this.$getRecord(snap.key);
return msg.update(snap);
},
// our messages store the unique id in a special location, so tell $firebaseArray
// how to find the id for each record
$$getKey: function(rec) {
return rec.message_id;
}
});
});
And finally, we can put it all together into a synchronized list of messages:
app.factory("MessageList", function(MessageFactory) {
return function(ref) {
return new MessageFactory(ref);
}
});
This documentation is for AngularFire 2.x.x with Firebase SDK 3.x.x.
SDK Version | AngularFire Version Supported |
---|---|
3.x.x | 2.x.x |
2.x.x | 1.x.x |
Browser | Version Supported | With Polyfill |
---|---|---|
Internet Explorer | 9+ | 9+ (Angular 1.3 only supports 9+) |
Firefox | 4.0 | 3.0? |
Chrome | 7 | 5? |
Safari | 5.1.4 | ? |
Opera | 11.6 | ? |
Polyfills are automatically included to support older browsers. See src/polyfills.js
for links
and details.