Play, Server Sent Events and AngularJS

I am currently involved in a project that uses a lot of server push notification. I could use Web Sockets but, as I only need of one-way of communication is being used SSE to implement all features that need push notifications. In its Scala version, Play has a nice support to SSE, let’s see an example:

	object UpdateCurrentAllocationController extends Controller {

	  val (chatOut, chatChannel) = Concurrent.broadcast[JsValue]

	  def attempts = Action {
	    implicit req =>
	      {
	        Ok.feed(chatOut &>  EventSource()).as("text/event-stream")
	      }
	  }
	}

In the view layer, it is easy to use the SSE support offered by all browsers. Let’s see one more example of code:

	var feed = new EventSource('@br.com.celerate.controllers.routes.UpdateCurrentAllocationController.attempts');
	var handler = function(event){
		//code to update the view
	};
	feed.addEventListener('message', handler, false);

After each notification, we need to update our view with the new data. A common way to accomplish this task is, for example, use JQuery to manipulate the DOM elements from your template. In a Javascript centric application, update all parts of the page becomes really annoying. Take a look in this example:

	var feed = new EventSource('@controllers.routes.UpdateCurrentAllocationController.attempts');
	var handler = function(event){
		var tbody = $("#solution").empty();;
		var solution = JSON.parse(event.data);

		var classes = solution.data.classRooms;
		$.each(classes,function(index,room){
			var tr = $("<tr>").append(
					$("<td>").text(room.course.code),
					$("<td>").text(room.number),
					$("<td>").text(room.interval.begin),
					$("<td>").text(room.interval.end),
					$("<td>").text(room.formatedIntervalOfDay),
					$("<td>").text(room.instructor.name)
					);

			tr.hide().appendTo(tbody).fadeIn(1000);
		});

	};
	feed.addEventListener('message', handler, false);

You end with a javascript code highly coupled with the page elements! Every change you make in a page element, you have to update your javascript code.  This is the scenario where AngularJS better fits. You leave the code necessary to update the elements in your page directly in the html and the code necessary to retrieve the data in other part. Let’s see how it works.  Let’s pick the same table that was updated using JQuery and change to use the AngularJS syntax.

	<tbody id="solution" ng-app="celerateApp" ng-controller="InstructorsController">
		<tr ng-repeat="room in rooms">
	    	<td>{{room.course.code}}</td>
	        <td>{{room.number}}</td>
	        <td>{{room.interval.begin}}</td>
	        <td>{{room.interval.end}}</td>
	        <td>{{room.formatedIntervalOfDay}}</td>
	    	<td>{{room.instructor.name}}</td>
		</tr>
	</tbody>

The ng directives is the way you bind your elements with AngularJS. For example, room in rooms is a Angular syntax to repeat a element in your page. And the {{variable}} is the syntax to use some variables that were defined elsewhere. Now you should be asking yourself, where these variables were created? That is why we are using the ng-controller. The controller class is responsible for updating all variables bound with the elements and this is the biggest difference with the older approach, your js code is bound with variables and not with html elements. Let’s see the controller code:

	var celerateApp = angular.module('celerateApp', []);

	celerateApp.controller('InstructorsController', function($scope) {

	  var feed = new EventSource('/newSolutionListener');
	  var handler = function(event){
			var solution = JSON.parse(event.data);
			$scope.$apply(function () {
				$scope.rooms = solution.data.classRooms;
			});

	  }
	  feed.addEventListener('message', handler, false);
	});

Here we have a lot of things happening. The first thing you have to do is to declare a module. The number of modules will depend on the size of your app. After that, we can configure ours controllers. You need to provide a name and a function that will be executed when the element bound with this controller is found. Now, every time that a new notification is sent from the server, we update the room variable inside the controller scope. We had to use the $apply function to notify Angular listeners which are responsible to update all variables bound with page elements. This method is needed, only if you are trying to update a scope outside from AngularJS callback, which is our case here.

So, if you are planning to build a app that has a lot of ajax or push notifications, I advise you to take a look in Angular.  You can combine the power of this framework with features like REST and push notifications provided by Play.  Enjoy it!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s