Outline
Now that we're able to authenticate users, let's create the ability for users to have custom display names to use in our app (rather than showing the user's email or uid)
Users
in app/users/users.service.js
. This factory will
depend on $firebaseArray
and $firebaseObject
.
angular.module('angularfireSlackApp')
.factory('Users', function($firebaseArray, $firebaseObject){
var Users = {};
return Users;
});
The purpose of this factory is to provide us with the ability to get either a specific user's data, or to get a list of all of our users. Note that while Firebase provides us with a means of authentication, all of the authentication data is separate from our Firebase data and can't be queried. It is up to us to store any custom user data within Firebase manually.
users
node
angular.module('angularfireSlackApp')
.factory('Users', function($firebaseArray, $firebaseObject){
var usersRef = firebase.database().ref('users');
var Users = {};
return Users;
});
Data in Firebase is stored in a tree structure
and child nodes can be referenced by passing a string to the ref()
function,
so firebase.database().ref('users')
refers to the users
node.
$firebaseArray
using the reference we just created.
angular.module('angularfireSlackApp')
.factory('Users', function($firebaseArray, $firebaseObject){
var usersRef = firebase.database().ref('users');
var users = $firebaseArray(usersRef);
var Users = {};
return Users;
});
It's also good to know that $firebaseArray
will return a pseudo-array,
meaning it will act a lot like an array in javascript, however, methods like
splice()
, push()
, pop()
will only affect data locally and not on the
Firebase. Instead, $firebaseArray
provides methods named $add
and $remove
to provide similar functionality while keeping your data in sync. Read the
$firebaseArray Documentation
For a complete understanding of how $firebaseArray
should be used.
Users
to the following object:
var Users = {
getProfile: function(uid){
return $firebaseObject(usersRef.child(uid));
},
getDisplayName: function(uid){
return users.$getRecord(uid).displayName;
},
all: users
};
getProfile(uid)
allows us to get a $firebaseObject
of a specific user's
profile, while all
returns a $firebaseArray
of all the users.
getDisplayName(uid)
is a helper function that returns a user's displayName
when given a uid
. We will be keying our data by the uid
that comes back from our Firebase auth data, so data in our Firebase will look similar to:
{
"users": {
"userid1":{
"displayName": "Blake"
},
"userid2":{
"displayName": "Marie"
}
}
}
Now that our Users
service is created, let's create a controller for updating
a user's profile. First we'll need to create a new state in app/app.js
to
resolve
a couple dependencies. We want to have the user's auth
data
and their profile available to us before our controller is instantiated.
.state('profile', {
url: '/profile',
resolve: {
auth: function($state, Users, Auth){
return Auth.$requireSignIn().catch(function(){
$state.go('home');
});
},
profile: function(Users, Auth){
return Auth.$requireSignIn().then(function(auth){
return Users.getProfile(auth.uid).$loaded();
});
}
}
})
We left the controller
and templateUrl
properties out of the state
configuration temporarily because we haven't created them yet. The auth
dependency is similar to the requireNoAuth
dependency we created for
login
and register
, except it does the inverse, where the user is
redirected to the home
state if they're not authenticated. The .catch
function is a shorthand for handling promises if we don't want to provide
a success handler. The profile
dependency also ensures authentication,
but resolves to the user's profile using the getProfile
function we
created in our Users
service. $loaded
is a function provided by
both $firebaseObject
and $firebaseArray
that returns a promise that
gets resolved when the data from Firebase is available locally.
ProfileCtrl
in app/users/profile.controller.js
, injecting $state
,
md5
, auth
, and profile
angular.module('angularfireSlackApp')
.controller('ProfileCtrl', function($state, md5, auth, profile){
var profileCtrl = this;
});
We'll be using Gravatar to get profile picture functionality in our application. Gravatar is a service that provides us with a user's profile picture when given an email, however the email needs to be md5 hashed. Luckily, there are many modules available that can do this for us, and angular-md5 was already included in our seed codebase.
profile
on the controller to the one that was resolved by the router
profileCtrl.profile = profile;
updateProfile
function on our controller:
profileCtrl.updateProfile = function(){
profileCtrl.profile.emailHash = md5.createHash(auth.email);
profileCtrl.profile.$save();
};
Here we're getting the current user's email from the auth
data that was
resolved from our router, hashing it and setting to emailHash
on profile
.
displayName
will be set from the template we'll be creating next using
ng-model
.
getGravatar
to the Users
service that will return the
url to a user's gravatar image when provided a uid
.
getGravatar: function(uid){
return '//www.gravatar.com/avatar/' + users.$getRecord(uid).emailHash;
},
app/index.html
, include the user service and profile controller
<script src="auth/auth.service.js"></script>
<script src="users/users.service.js"></script>
<script src="users/profile.controller.js"></script>
controller
and templateUrl
in the profile
state to the following:
url: '/profile',
controller: 'ProfileCtrl as profileCtrl',
templateUrl: 'users/profile.html',
users/profile.html
with the following code:
<div class="page-wrapper">
<div class="page-header">
<h1>Edit Profile</h1>
</div>
<form ng-submit="profileCtrl.updateProfile()">
<p ng-hide="profileCtrl.profile.displayName">
You'll need a display name before you can start chatting.
</p>
<div class="input-group">
<input required type="text" class="form-control" placeholder="Display Name" ng-model="profileCtrl.profile.displayName">
</div>
<input type="submit" class="btn btn-default" value="Set Display Name">
</form>
</div>
We should now be able to navigate to http://localhost:4000/#/profile
, specify
a display name for our user, submit the form and it should persist when we
refresh the page.
Check your work
You can view the completed & working code for this tutorial here: