Outline
angular.module('angularfireSlackApp')
.factory('Messages', function($firebaseArray){
var channelMessagesRef = firebase.database().ref('channelMessages');
return {
forChannel: function(channelId){
return $firebaseArray(channelMessagesRef.child(channelId));
}
};
});
The forChannel
function on our service returns a $firebaseArray
of messages
when provided a channelId
. Later in this tutorial, we'll create a forUsers
function for retrieving direct messages.
channels.messages
with the following code:
.state('channels.messages', {
url: '/{channelId}/messages',
resolve: {
messages: function($stateParams, Messages){
return Messages.forChannel($stateParams.channelId).$loaded();
},
channelName: function($stateParams, channels){
return '#'+channels.$getRecord($stateParams.channelId).name;
}
}
})
This state will again be a child state of channels
. Our url will have a
channelId
parameter. We can access this parameter with $stateParams
,
provided by ui-router
. We're resolving messages
, which is using the
forChannel
function from our Messages
service, and channelName
which we'll be using to display the channel's name in our messages pane.
Channel names will be prefixed with a #
. The channels
dependency we're
injecting is coming from the parent state channels
since child states
inherit their parent's dependencies. We'll come back and add the controller
and templateUrl
properties once we create our controller and template.
MessagesCtrl
in app/channels/messages.controller.js
.
We'll be injecting profile
, channelName
, and messages
angular.module('angularfireSlackApp')
.controller('MessagesCtrl', function(profile, channelName, messages){
var messagesCtrl = this;
});
Again, the profile
dependency that we're injecting will actually come from
the parent state channels
that resolves to the current user's profile.
messages
and channelName
on messagesCtrl
to the respective
dependencies
messagesCtrl.messages = messages;
messagesCtrl.channelName = channelName;
message
on messagesCtrl
to an empty string.
messagesCtrl.message = '';
sendMessage
to $add
a message to messages
.
messagesCtrl.sendMessage = function (){
if(messagesCtrl.message.length > 0){
messagesCtrl.messages.$add({
uid: profile.$id,
body: messagesCtrl.message,
timestamp: firebase.database.ServerValue.TIMESTAMP
}).then(function (){
messagesCtrl.message = '';
});
}
};
A message object will need to contain uid
, which will be how we identify
who sent the message. body
contains the message our user input, and
timestamp
is a constant from Firebase
that tells the Firebase servers
to use the their clock for the timestamp. When a message sends successfully,
we'll want to clear out messagesCtrl.message
so the user can type a new
message.
app/index.html
, include our messages service and controller.
<script src="channels/channels.service.js"></script>
<script src="channels/messages.service.js"></script>
<script src="channels/messages.controller.js"></script>
app/channels/messages.html
:
<div class="header">
<h1>{{ messagesCtrl.channelName }}</h1>
</div>
<div class="message-wrap" ng-repeat="message in messagesCtrl.messages">
<img class="user-pic" ng-src="{{ channelsCtrl.getGravatar(message.uid) }}" />
<div class="message-info">
<div class="user-name">
{{ channelsCtrl.getDisplayName(message.uid) }}
<span class="timestamp">{{ message.timestamp | date:'short' }}</span>
</div>
<div class="message">
{{ message.body }}
</div>
</div>
</div>
<form class="message-form" ng-submit="messagesCtrl.sendMessage()">
<div class="input-group">
<input type="text" class="form-control" ng-model="messagesCtrl.message" placeholder="Type a message...">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">Send</button>
</span>
</div>
</form>
Here we're creating a header to display the channelName
from our controller.
Then we're ng-repeat
ing over messages
and using message.uid
and the
helper functions from channelsCtrl
to get the user's display name and
Gravatar. We're also using Angular's date
filter on the timestamp to display
a short timestamp. Finally, at the bottom of our view we have the form for
sending messages which submits to the sendMessage
function from our
controller.
channels.messages
state to use the template and controller we
just created.
url: '/{channelId}/messages',
templateUrl: 'channels/messages.html',
controller: 'MessagesCtrl as messagesCtrl',
<a ui-sref="channels.messages({channelId: channel.$id})" ui-sref-active="selected"># {{ channel.name }}</a>
We're specifying the parameters for the channels.messages
state within the
ui-sref
directive. The ui-sref-active
directive will add the specified
class (selected
in our case) to the element when a state specified in a
sibling or child ui-sref
directive. Now we should be able to navigate between
channels and start chatting!
createChannel
function to send the user to the newly created
channel upon creation.
channelsCtrl.createChannel = function(){
channelsCtrl.channels.$add(channelsCtrl.newChannel).then(function(ref){
$state.go('channels.messages', {channelId: ref.key});
});
};
Check your work
You can view the completed & working code for this tutorial here: