I've been using Google App Engine for a while now, and it is hard to go back to traditional java webapp development given how easy it is to deploy a demo version for a customer, and how great some of the tools provided in the App Engine SDK are.
Among those tools is the Channel API. It offers push messaging with only a few lines of code. There are some drawbacks (mainly regarding the reliability of the communications) but it still is a great tool to provide real-time notifications to the user.
A also recently discovered Angular JS. A great Javascript MVP framework (also by Google, it turns out). If you don't know it already, have a look at their web site. The web-binding part is quite amazing.
Now, I read Angular JS provides some support for Comet notifications, but nothing for the Channel API. On a project I'm developping on my spare time there's this need to send chat messages to various users.
Here's how I did it with AngularJS and the Channel API :
- Create an AngularJS service to hold the list of the chats. This is not required but it is the recommended way to share an observable array (which means with two-way data binding) between controllers.
- Initiate the Channel API inside that service. Now there's the trick : for the modifications to the observable array to be pushed to the controllers, we need to use the $rootScope method.
So here's how the service looks ( the chat repository which receives the chats from the Channel API is AllChats):
angular.module('hatChatServices', ['ngResource']) .factory('Chat', function($resource){ return $resource('chats/:chatId', {}, { query: {method:'GET', params:{chatId:'latest'}, isArray:true}, }); }) .factory('AllChats', function($rootScope,Chat){ var chatsList = Chat.query(); var unreadChats = { "PENDING" : 0, "APPROVED" : 0, "ANSWERED" : 0 } //that's where we connect var socket = new SocketHandler(); socket.onMessage(function(data){ $rootScope.$apply(function () { var newChat = new Chat(data); angular.copy(removeChatIfAlreadyExists(newChat, chatsList), chatsList); chatsList.push(newChat); unreadChats[newChat.type]++; }); }); return { list : chatsList, unreadChats : unreadChats } }); function removeChatIfAlreadyExists(chat, array){ var result = array.filter(function(potentialMatch){ return potentialMatch.id != chat.id; }) return result; }
And here's the definition of the SocketHandler :
var SocketHandler = function(){ this.messageCallback = function(){}; this.onMessage = function(callback){ var theCallback = function(message){ callback(JSON.parse(message.data)); } if(this.channelSocket ==undefined){ this.messageCallback = theCallback; }else{ this.channelSocket.onmessage = theCallback; } } var context = this; this.socketCreationCallback = function(channelData){ var channel = new goog.appengine.Channel(channelData.channelToken); context.channelId = channelData.channelId; var socket = channel.open(); socket.onerror = function(){ console.log("Channel error"); }; socket.onclose = function(){ console.log("Channel closed, reopening"); //We reopen the channel context.messageCallback = context.channelSocket.onmessage; context.channelSocket = undefined; $.getJSON("chats/channel",context.socketCreationCallback); }; context.channelSocket = socket; console.log("Channel info received"); console.log(channelData.channelId); context.channelSocket.onmessage = context.messageCallback; }; $.getJSON("chats/channel",this.socketCreationCallback); }
No comments:
Post a Comment