Server Sent Events with Rails 4 and AngularJS

It is a common requirement in modern web applications to have the client view responding to server events. By doing so, end users can be notified in real-time of any relevant state or data update. Supporting push notifications in your web applications is a way of making them more interactive, respond better to server events, and ultimately deliver an improved user experience.

Pushing notifications to the browser can easily be achieved by streaming JSON data to the client. An HTTP connection is kept open between the server and the browser and on reception of events, the web page is updated to reflect the change.

Streaming JSON data has become easier than ever using Rails 4 and its ActionController::Live. Coupling that with the ability of AngularJS to react to events, you get a very simple way of handling Server Sent Events in your web application.

Let’s say we want to display a subset of the share market prices updated periodically where new values will be “pushed” by the server to the browser in a JSON format. Using a push mechanism in this scenario will keep the user informed as soon as market prices are updated.

In this example, we will simulate a data update by generating new market values every 5 seconds, storing them in a database and streaming the new data to the client.

class SharesController < ApplicationController
include ActionController::Live
Mime::Type.register “text/event-stream”, :stream
def index
response.headers[‘Content-Type’] = ‘text/event-stream’
# Stream content here
end
end

  • Use the method response.stream.write to push data to the client
def index
response.headers[‘Content-Type’] = ‘text/event-stream’
begin
loop do
response.stream.write “data: #{generate_new_values}\n\n” # Add 2 line breaks to delimitate events
sleep 5.second
end
rescue IOError # Raised when browser interrupts the connection
ensure
response.stream.close # Prevents stream from being open forever
end
end

  • Generate new market prices and a price variation indicator (up or down) in JSON formatFor reference, the model looks like
def generate_new_values
now = Time.now
current_values = []
companies = Company.all(order: :code)
companies.each do |company|
previous_share = Share.where(company: company).order(‘timestamp DESC’).first
new_value = previous_share.value + (rand().round(2) – 0.5)
share = Share.create(company: company, value: new_value, timestamp: Time.now)
variation = share.value > previous_share.value ? ‘up’ : ‘down’
current_values << {company: company, share: share, variation: variation}
end
current_values.to_json
end

For reference, the model looks like

create_table “companies”, force: true do |t|
t.string “code”
t.string “name”
t.datetime “created_at”
t.datetime “updated_at”
end
create_table “shares”, force: true do |t|
t.integer “company_id”
t.decimal “value”
t.datetime “timestamp”
t.datetime “created_at”
t.datetime “updated_at”
end
  • On the client side we now use AngularJS to open a stream with the server and process events.HTML Markup:AngularJS Controller
    • HTML Markup:

<div class=”container shares” id=”shares” ng-controller=”SharesCtrl” ng-init=”init()”>
<div class=”share-container” ng-repeat=”entry in entries”>
<div class=”share-code”>{{entry.company.code}}</div>
<div class=”share-name”>{{entry.company.name}}</div>
<div class=”share-value”>${{entry.share.value}}</div>
<div class=”share-variation”><img ng-src=”{{entry.variation == ‘up’ && ‘up.png’ || ‘down.png’}}”></div>
</div>
</div>
  • AngularJS Controller
var shareModule = angular.module(‘shares’, []);
shareModule.factory(‘Shares’, function() {
return {};
});
shareModule.controller(‘SharesCtrl’, function($scope, Shares) {
$scope.init = function() {
var source = new EventSource(‘/shares’);
source.onmessage = function(event) {
$scope.$apply(function () {
$scope.entries = JSON.parse(event.data)
});
};
};
});

On page load, the EventSource object opens a stream with the server and on each message received, AngularJS is notified to apply those changes to the model and view.

The next version of Rails (4.1) will provide a ActionController::Live::SSE which will further reduce the amount of effort needed to stream events.

Example source code on Github

This article Server Sent Events with Rails 4 and AngularJS was originally published on my Blog here.

Want to know more about how DiUS can help you?

Offices

Melbourne

Level 3, 31 Queen St
Melbourne, Victoria, 3000
Phone: 03 9008 5400

Sydney
Level 2, 50 York St
Sydney, NSW, 2000
Phone: 02 8014 6640

DiUS wishes to acknowledge the Traditional Custodians of the lands on which we work and gather at both our Melbourne and Sydney offices. We pay respect to Elders past, present and emerging and celebrate the diversity of Aboriginal peoples and their ongoing cultures and connections to the lands and waters of Australia.

Relay Newsletter

Sign up to receive the latest news, insights and case studies from DiUS straight into your inbox.

Subscribe

* indicates required

© 2021 DiUS®. All rights reserved.

Privacy  |  Terms