Outline
Now that our Users can authenticate and have profiles, we can finally start to create the functionality for Channels. We're going to start by creating the left sidebar for listing channels and display the current user's profile.
Channels
in app/channels/channels.service.js
that will
return a $firebaseArray
at the channels
node.
angular.module('angularfireSlackApp')
.factory('Channels', function($firebaseArray){
var ref = firebase.database().ref('channels');
var channels = $firebaseArray(ref);
return channels;
});
channels
with the following code:
.state('channels', {
url: '/channels',
resolve: {
channels: function (Channels){
return Channels.$loaded();
},
profile: function ($state, Auth, Users){
return Auth.$requireSignIn().then(function(auth){
return Users.getProfile(auth.uid).$loaded().then(function (profile){
if(profile.displayName){
return profile;
} else {
$state.go('profile');
}
});
}, function(error){
$state.go('home');
});
}
}
})
templateUrl
and controller
are temporarily omitted since we haven't created
them yet. We're resolving two dependencies here: channels
, which is promising
our $firebaseArray
of channels, and profile
, which is a lot like the
profile
dependency in the profile
state, but we're ensuring that the user
already a displayName set, otherwise they're taken to the profile
state, and
if they're not authenticated, they get sent to the home
state.
ChannelsCtrl
in app/channels/channels.controller.js
, injecting
$state
, Auth
, profile
, and channels
.
angular.module('angularfireSlackApp')
.controller('ChannelsCtrl', function($state, Auth, Users, profile, channels){
var channelsCtrl = this;
});
channels
and profile
to the resolved dependencies from the router
channelsCtrl.profile = profile;
channelsCtrl.channels = channels;
getDisplayName
and getGravatar
to the respective functions on the
Users
service.
channelsCtrl.getDisplayName = Users.getDisplayName;
channelsCtrl.getGravatar = Users.getGravatar;
logout
function that will allow our users to log out, returning them
to the home
state.
channelsCtrl.logout = function(){
Auth.$signOut().then(function(){
$state.go('home');
});
};
app/index.html
, include the channels service and controller
<script src="users/profile.controller.js"></script>
<script src="channels/channels.controller.js"></script>
<script src="channels/channels.service.js"></script>
app/channels/index.html
with
the following code:
<div class="main">
<div class="sidebar">
<div class="slack-name">
<h2>FireSlack</h2>
</div>
<div class="channel-list">
<div class="list-head">Channels</div>
</div>
<div class="my-info">
<img class="user-pic" ng-src="{{ channelsCtrl.getGravatar(channelsCtrl.profile.$id) }}" />
<div class="user-info">
<div class="user-name">
{{ channelsCtrl.profile.displayName }}
</div>
<div class="options">
<a ui-sref="profile">edit profile</a>
/
<a href="#" ng-click="channelsCtrl.logout()">logout</a>
</div>
</div>
</div>
</div>
</div>
controller
and templateUrl
in channels
state to the following:
url: '/channels',
controller: 'ChannelsCtrl as channelsCtrl',
templateUrl: 'channels/index.html',
resolve
to the home
state:
resolve: {
requireNoAuth: function($state, Auth){
return Auth.$requireSignIn().then(function(auth){
$state.go('channels');
}, function(error){
return;
});
}
}
updateProfile
function in ProfileCtrl
to send the user to the
channels
state after a successful save.
profileCtrl.updateProfile = function(){
profileCtrl.profile.emailHash = md5.createHash(auth.email);
profileCtrl.profile.$save().then(function(){
$state.go('channels');
});
};
This requireNoAuth
dependency is a lot like the one on login
and register
but it sends the user to the channels
state. If you want, you can change the
requireNoAuth
dependency on login
and register
to also send the user to
the channels
state as well. Now when we're logged in and visit
http://localhost:4000
we should be sent to the channels
state. In that
state, we can see the sidebar we just created for our application. There should
be the logged in user's name and Gravatar at the bottom of the sidebar, and an
edit profile and logout link next to it. We're using the ui-sref
directive
that comes with ui-router
to specify what state we should navigate to on
click. If we click on edit profile, we can update the user's displayName
and
it should send us back to the channels
state when we submit the form. The
logout link should log us out and send us back to the home
state. Now let's
create the view to create channels.
channels.create
:
.state('channels.create', {
url: '/create',
templateUrl: 'channels/create.html',
controller: 'ChannelsCtrl as channelsCtrl'
})
This state is a child state of the channels
controller (the dot notation in
the state name specifies parentState.childState
). This state will also use
ChannelsCtrl
. We'll want to render our child states to the right of the
sidebar. We need to declare another ui-view
tag in order for our child
states to appear. You can read more about ui-router
's nested states at this
Github Wiki.
message-pane
div with a ui-view
tag in app/channels/index.html
.
This should be at the end of the main
div after the sidebar
div.
<div class="message-pane">
<ui-view></ui-view>
</div>
newChannel
object on ChannelsCtrl
with a blank name
channelsCtrl.newChannel = {
name: ''
};
createChannel
function on ChannelsCtrl
.
channelsCtrl.createChannel = function(){
channelsCtrl.channels.$add(channelsCtrl.newChannel).then(function(){
channelsCtrl.newChannel = {
name: ''
};
});
};
The $add
function on the channels $firebaseArray
provides similar
functionality to the .push()
function on a Javascript Array
, but keeps our
data in sync with Firebase, while returning a promise. Once the new channel is
created we'll need to clear out the newChannel
object.
app/channels/create.html
for creating a new channel
<div class="header">
<h1>Create a channel</h1>
</div>
<form ng-submit="channelsCtrl.createChannel()">
<div class="input-group">
<input type="text" class="form-control" placeholder="Channel Name" ng-model="channelsCtrl.newChannel.name">
</div>
<input type="submit" class="btn btn-default" value="Create Channel">
</form>
app/channels/index.html
using the following
markup in the channel-list
div below the list-head
div:
<div class="channel-list">
<div class="list-head">Channels</div>
<div class="channel" ng-repeat="channel in channelsCtrl.channels">
<a># {{ channel.name }}</a>
</div>
</div>
We're using the ng-repeat
directive to iterate over our array of channels
.
channels.create
state below the channel listing:
<div class="channel-list">
<div class="list-head">Channels</div>
<div class="channel" ng-repeat="channel in channelsCtrl.channels">
<a># {{ channel.name }}</a>
</div>
<div class="channel create">
<a ui-sref="channels.create">+ create channel</a>
</div>
</div>
We're now able to click on the create channel link and start creating channels!
Check your work
You can view the completed & working code for this tutorial here: