In the last two episodes, we talked about creating a simple IoT-like service, using Kafka to produce and consume messages in real-time and Flask to create a REST API to allow us producing messages via HTTP and consume them using server-sent events (SSE). In the end, we managed to get our messages pushed to clients using SSE, and now it is time to have a closer look at the client code.

SSE and Javascript

Assume you have a webpage that should react on events sent by your web server. The first thing you’ll have to do is add an event listener. This is really easy and takes only a few lines of Javascript code.

var source = new EventSource('/topic/test');
source.addEventListener('message', function(e){
    console.log('Message: ');
    console.log(e.data);
}, false);

Adding this into an HTML document should be enough to see messages you produce with CURL, like we described last time, logged to your browser’s console. But this is not very exciting. You know what would be exciting? If your webpage got updated automatically with new messages. Enter Angular.

AngularJS

AngularJS is a complete, Javascript based implementation of the model-view-controller pattern, which is nerd-speak for saying that it helps you write dynamic HTML pages with minimal effort and maximal effect, separating completely the presentation of the user interface from the program logic. Read the Wikipedia article linked above if you’re interested in why this is such a good idea.

Let’s dive right in. Assume you have a list of things that you want to display on a web page, but maybe you want to update that list by, say, SSE in the future and hence don’t want to hard-code it in HTML. How would you go about it? Well, you write a Javascript file thusly.

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

myApp.controller('myController', function myController($scope) {
    $scope.messages = ["One", "Two", "Three"];
});

This tells Angular that we have an app called “myApp”, and define the the underlying controller. In the controller’s scope, we have a single variable, our list of messages. In our web page, we now have to load Angular, but instead of downloading it, we use an easier path prepared for us by the kind people at Google who host a number of popular libraries for us. Our web page looks like this.

<html ng-app="myApp">
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
    <script src="app.js"></script>
  </head>
  <body ng-controller="myController">
    <ul>
      <li ng-repeat="msg in messages"></li>
  </body>
</html>

If everything went right, you should now see a list like this in your browser.

  • One

  • Two

  • Three

Putting Everything Together

Let’s put all the moving parts together. Our modified app will add the messages sent by the server to the list and thus dynamically update the web page with server-sent events.

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

myApp.controller('myController', function myController($scope) {
    $scope.messages = [];
    console.log("Foo");
    console.log('Adding listener.');
    var source = new EventSource('/topic/test');
    source.addEventListener('message', function(e){
    	console.log('Message');
        console.log(e.data);
        $scope.$apply(function() {
            $scope.messages.push(e.data);
        });
    }, false);
    console.log('Added listener.');
});

Keep in mind that by default, the base URL of your SSE and your webpage must be identical, so you’ll have to host your static HTML and Javascript files through Flask or your web server should you use one (which is a good idea, at least in a production environment).

I hope you’ve enjoyed this episode and stay tuned for the next data adventure.