Chapter 6. Moving to Real-time Web Applications

A web application, nowadays, is expected to be as reactive as a desktop one; moving statically from one page to another is no longer accepted. Furthermore, to enhance the user experience, we need to reduce as much as possible the amount of actions needed to have the content updated constantly.

We entered the real-time era some time back, and the mobile explosion has definitively confirmed that. Given this fact, the problem we face when creating a web application dedicated to mobile devices is, still, the bandwidth (this doesn't seem like we have entered the same era as our needs). So, we'll have to think more about some optimizations while communicating with the server. This chapter is dedicated to the utilities Play! Framework 2 is offering us to enable us to satisfy these points.

The following is what will be covered in this chapter:

  • Creating a dashboard, where the data will be updated in the background
  • Following the naïve approach using a polling service over HTTP
  • Introducing CoffeeScript before using it for client-side logic
  • Adapting the dashboard a bit to use dynamically updated forms
  • Doing a final update of the dashboard using WebSockets

At the end, we'll have a good overview on how to replicate the solution to other similar needs.

Ready, JSON, poll

In the earlier chapters, we built an application that mixed the notion of a chat and a forum. If we use it, we'll face some problems for sure; indeed, when we post a new message or image, the other users that are connected won't be notified unless they refresh their whole page. This kind of workflow is a pain in terms of performance and user experience. As it requires several users' actions, and because all data has to be provided by the server (which will give the same stuff again and again); think about big images that are loaded each time the application is refreshed. All of this tells us that such a workflow is not optimal at all. How are we going to tackle this? First we will use the ancestral polling system.

Polling is a system asking the server (or a bunch of services) the same resources repeatedly, and, normally, at a high rate (the higher it is, the better user experience you should have). So, it's trivial that it'll consume a lot of power, and often wasted because the requested state in the server hasn't changed. Project this problem in a mobile application and it can empty the battery quickly. We'll achieve this by rendering our resources in a more convenient way using JSON, and by having some JavaScript scripts on the client side to fetch them.

To make our application more user-friendly, we'll create a kind of dashboard that can be customized to include those interesting chat instances / topics for the user. Thanks to this use case, we'll also see some other helpers provided by Play! 2 in order to dynamize forms on the client and the server side, through the need of the non-deterministic structure (list).

The second thing we will resort to here will be the use of CoffeeScript rather than JavaScript (in some sense). Because the Play! 2 Framework is perfectly integrated with CoffeeScript, we will be able to use it like we were using simple JavaScript scripts. Indeed, Play! 2 will handle the compilation and hot refresh on its own. Its official website (http://coffeescript.org/) defines CoffeeScript as follows:

CoffeeScript is a little language that compiles into JavaScript.

Note

CoffeeScript is a language that eliminates boilerplates or enables class definition (among other things). Furthermore, it has the advantage to be compiled into a readable JavaScript file (helpful for debugging, for instance).

One of the reasons we'll use CoffeeScript over JavaScript is because it is more readable for most server-side programmers (the syntax is similar to Python).

So, we want a dashboard , which is something that presents a lot of things at the same time, in order to multitask optimally. Let's do it.

Configuring a dashboard

A dashboard is something that can be configured to present the exact amount of information that a user wants to see. So, in our case, where only chat instances are involved, we're going to provide a way to see several chat instances at once.

For that, we'll have to deal with a dynamic form on the server side. This form will be such that the number of values passed to the server is neither predetermined nor fixed (non-deterministic).

First of all, we'll need a new template for this and its related server-side action. The template will present an HTML form, where the user can select which chat instances he/she wants to add to his/her dashboard. So we'll need all available chat instances as a parameter of this template in order to create the UI that enables such selections.

On the server side, the action should be able to retrieve as many chat instances as the user has configured; hence we're going to have a kind of dynamic list of chat references to be bound to the request content.

The following screenshot shows an example of how we can define our template:

Configuring a dashboard

What we see in this example is interesting in several ways. First of all, while defining a dashboard for our chat system, we tried to split the tasks in order to improve readability.

For that, we created an inner template createSelect that takes a form field and a sequence of a tuple of strings. The result of this template is an Html block that shows an HTML select element. It has been defined against a parameter of type Field, which is just a wrapper around all information that is necessary to show an HTML element in an elegant and valuable way—the name, ID, errors, constraints, and so on. These instances can be easily built from the Form instance based on their expected name in the request.

We can also see that in order to define an inner template, we simply have to define a function. This is quite intuitive because we already saw that Scala templates are compiled into a Scala function.

Then we took all the existing chat instances (available as a parameter of the template) in a sequence and mapped them to a representation that is easily usable by a select input field.

As this computation will be done more than once, we can define it as a new variable, preparedChats, through the use of the helper defining. As it's not possible to create new variables within a template, we can use this helper that takes a computation and a block of code that uses it. This is done by providing a function that takes the result of the computation as the only argument. In this case, the function block of defining is creating Html content to be rendered.

Now we're reaching two exciting blocks as we're about to define the dynamic part of the form. Recall that we're trying to have a form that enables the user to select several chat instances to be shown on a single page.

Let's skip the very next block with the class sampleJsBlock, and first look at form one, where we start directly with the action definition that targets a new dedicated action (routes.Dashboard.open), followed by the usage of another helper: repeat. This helper is indeed a very useful one (because it will hide for us all boilerplates necessary to create form elements that must be serialized under the same name) and is followed by an array index.

Note

HTML enables a parameter to be defined multiple times by simply using the same name. However, there is a common pattern to handle a use case that adapts this specification. The solution is to follow the name with either empty brackets or brackets holding the index of a value. Play! 2 uses the second convention, and so using the repeat helper we'll have solutions such as param[0]=a and param[1]=b.

Furthermore, this helper has a specific argument telling us how many times its body definition has to be generated. In our case, we want at least two chat instances to be shown. This so-called body, in our case, is exactly a call of our inner template createSelect, which will create a select element containing all available chat instances.

We shall go to the action now in order to check how the heck this parameter list will be handled. But before that, it'd be worth looking at the block we kept aside, which is there to enable the user to add more than two chat instances to his/her dashboard, dynamically.

To do that, there is a common trick (a kind of pattern) that will enable us to create, on the client side, a UI that matches exactly what is produced on the server side (using a template and thus helpers). This is achieved by creating a dummy excerpt of HTML, and hiding it outside the form tag. This is what has been done with the div tag having the class sampleJsBlock. In fact, this one just contains a generated select element, but with a dummy field (giving dashboardForm the expected name, but with a fake index).

In order to catch what will be necessary to do with it, it's important to check what is produced:

Configuring a dashboard

We can see that there is a hidden div tag defining some elements, which has attributes containing the provided dummy index, namely _x_ and [x].

So you can surmise that we'll have to produce a client-side code (we'll do it in CoffeeScript) that takes this bunch of HTML and replaces all such instances of the dummy index by the current count of selects being shown (starting at 2, though).

Let's keep this in mind and perform a quick hook in the controller to check how this form is bound to an instance of controllers.Dashboard.Data according to the type of template parameter, dashboardForm.

The controller that we're talking about is the one targeted by the form in its action attribute, which is Dashboard:

Configuring a dashboard

The Dashboard controller is very straightforward and doesn't include any new stuff that we need to point out. The only thing that is really interesting is the Data inner class, which stands as a container for the received chat instances' IDs.

As done earlier, we use the form method to create the binding by reflection. This binding will take into account the list and will expect several values with the same indexed name. So, we're binding it with the request as usual in the open action.

Something to note from here is that we're going to use the same template for rendering the form and the dashboard itself; that's because we'll not change the dashboard's configuration even if one is already opened. As a consequence, we'll have everything in the same template.

It's now time to dynamize the form using CoffeeScript.

Some sugar with your Coffee(Script)

So what we need to do is enable the + button to take the sample HTML block created on the server side to add a new select element after the existing ones. This block must be reworked a bit in order to use the correct index; we'll do that in CoffeeScript.

To be able to code in CoffeeScript using the same techniques as Java or Scala (hot recompile and reload, for instance) we can put our .coffee files in the folder app/assets/javascripts/. Hence, we'll create the file app/assets/javascripts/dashboard.coffee.

Note

Obviously, coffee is the extension of a CoffeeScript file.

As said earlier, CoffeeScript is there to help developers by eliminating a lot of boilerplates, mostly in order to code in a more object-oriented fashion using classes—for those who want this paradigm back.

The code in the following screenshot shows how we can implement our use case:

Some sugar with your Coffee(Script)

Even though we're using the basic features of CoffeeScript in this example, it is intuitive enough to get a big picture. However, there are still some concepts that might be worth mentioning.

Words about CoffeeScript's syntax

An important thing to know about CoffeeScript is that its layout is actually driven by the spaces used to indent the lines (Python style). Take a simple example: the body of a function must be indented one time more than the function declaration's indent.

As a consequence, CoffeeScript doesn't require parenthesis for a parameter's block and nor does it require a comma to separate the array's items or properties in an object (if they are separated by well-indented blank lines).

A class can be defined as easily as in an object-oriented language, that is, by simply using the class keyword followed by the class name. Methods and fields can be defined using the Object notation, that is, the name followed by a colon, and then the definition of the field or the function. A commonly used method is the constructor one, which will be used when using the new operator.

A variable declaration is not defined by any special keyword (such as var); actually, all newly used variables will be created locally by default (rather than globally, as in JavaScript).

A function can be declared in two ways: with an arrow (->) or a fat arrow (=>). These arrows will separate the function parameters between parenthesis and the body of the function (the implementation). The only difference between the arrow and the fat arrow is that the latter will keep the actual scope (which is graceful when playing with closures ); the this object can be kept as the original class' object, for instance.

The following screenshot shows two functions for illustration purposes:

Words about CoffeeScript's syntax

To read the example, we must mention that the this variable doesn't exist, but @ can be used to refer it; so @prop is compiled as this.prop.

Now we can understand that the g function, which makes use of the fat arrow, will never lose its initial scope, that is, the object that holds it.

Explaining CoffeeScript in action

Back to our Dashboard class, we can see that it only defines its logic in its constructor, so it doesn't provide any other functionalities besides starting itself with some actions. These actions are twofold (using jQuery that was imported in the main template):

  • The first action is retrieving all the elements that have the header class. On them, it will register a click event that will toggle the next DOM element. Another initial parameter is used to tell if it should close these elements on startup.
  • The second action registers a click event on the + button that will look for the block of HTML code in the form that wraps all select elements, then keeps count of the already present ones, and finally retrieves the hidden template's excerpt, so it will update the template with the relevant count and then append it to the block.

In brief, this .coffee file defines the Dashboard class and makes it available in the global scope in the very last statement. Having created this file, there are two things to be done – add the compiled JavaScript file to the web page and use it.

Play! 2 will compile our CoffeeScript file in the same folder as our traditional JavaScript files, that is, in the public/javascripts folder. Because of this, adding them to the application is as simple as adding a new JavaScript library in the scope (most of the time, this will be done in the main.scala.html file). This way, Play! 2 will be able to cache it and do some JavaScript minification for the production mode.

The way we're going to use it is not very conventional, but it will do the trick. In our dashboard/index.scala.html file, we'll just add a bootstrap code creating an instance of this Dashboard class:

Explaining CoffeeScript in action

Note

In a real application, it's worth considering libraries such as Spine.js or Backbone.js with Require.js for organizing your code and loading them.

Rendering the dashboard

Until now, we've been able to tell the server which chat instances are to be shown in the dashboard, but we haven't showed them yet. However, in the action, we saw that we're rendering the same template by providing the updated form with a Data instance, which has a helper method to fetch the chat instances.

Here again, we'll take advantage of the composability of our templates, by re-using the chatroom template for each chat in the chat instances list; the only thing we have to do in it is remove the call to main.

As this template requires two more parameters (the form enabling new messages or uploading of images), we're going to add them to the dashboard/index.scala.html template's signature.

What comes next is fairly obvious. Check out the following screenshot:

Rendering the dashboard

Note

The action doesn't need to be changed as it has foreseen them.

That was easy game. We just checked whether the form contains some data; if so, we looped on the embedded list of chat instances and called the relevant template.

The following screenshot shows the result (with a bit of skinning) we have so far:

Rendering the dashboard

Note

This is what we get after having selected two chat instances in the form; we can also see that the form is hidden (that came from the bootstrapping JavaScript that told to close it when a Data instance is available).

Updating the dashboard in live mode

So far so good—we have an aggregated view on several chat instances, but what hasn't been satisfied yet is the "liveness" of the updates. Actually, this problem has taken on more significance now, as several chat instances are involved. So let's resolve it.

What we're about to do is enable a poller for each chat to fetch its last updates that are available. For that we'll need two kinds of things, as follows:

  • A bunch of JavaScript files that run in the background in order to constantly fetch items and images based on the last timestamp. The result, a JSON, will be used to update the UI.
  • An action on the server side taking a timestamp and a chat that will return a JSON-encoded response with the items and images created since the given timestamp.

For the first part, we'll do quite the same as we did in the earlier section, that is, create a .coffee file that will contain the logic, add it to the main template, and initialize it in the relevant template.

First, the .coffee file! We'll create a new one, named chatroom.coffee, which could be as shown in the following screenshot:

Updating the dashboard in live mode

So what's the big deal here?

The constructor of ChatRoom takes a configuration object with three properties kept as fields of the class: the chat's ID (id), the element (el) wherein the UI has been added, and the last updated timestamp (since).

At the end of the class definition, there is a poll method, which simply starts an endless loop over the fetchContent method right above it, every 5 seconds. This latter method (thanks to the fat arrow) is always resolved to the actual instance, and its work is actually to call the server-side action that will be tackled next.

For that, we used jQuery's ajax function (get is just a jQuery abstraction over it) by giving it a URL filled with the expected parameters and a success function that will handle the response (a data encoded in JSON).

When the call succeeds we ask the ChatRoom class to update its UI with the new data, and we call poll again.

Note

Think about what is so bad here—! The URL was hardcoded! This will be resolved in the next chapter. Other things that can be improved here are the building of the items and images on the UI. Actually, we could re-use the tip we used earlier with the hidden HTML excerpt. That is, we could create the HTML with some placeholders to be replaced using JavaScript.

After including this script in the main page (<script src="..."), we must update the chatroom.scala.html template to initialize an instance of ChatRoom, shown as follows:

Updating the dashboard in live mode

One last thing to have the whole polling system in place is to add the action to be routed from URLs such as /chat/content/:id?timestamp=:since. So this action should take two arguments: two Long types (one for the ID and the other for the last update timestamp).

Updating the dashboard in live mode

Quite obvious, we just retrieve the Chat instance and filter the items and images based on the given timestamp (wouldn't it be great to have a higher function filter on the lists?). Then we asked the JSON from Play! 2 to encode an object containing both resulting lists (using reflection and thus without our help). And we're done. We can now open several browsers, configure our dashboard, and do cross-chatting with them all. And magically, everything is updated automatically.

Amazing, right? But not enough. Because, at first, we hardcoded URLs, then we polled for each chat instance. We also have the same forms for posting new items or images for each chat instance, but using them will cause us to leave the page and will force us to go back in order to go back to the dashboard.

So annoying! That's why we're about to change that in the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset