Improving Play! 2.1 with Knockout.js

This post is about form binding and the model-view-view-model (MVVM) strengths and weaknesses of Play!, and how those weaknesses can be eliminated by using Knockout.js. The techniques, problems and solutions mentioned in the post foremost applies to single page/single model applications, but might very well apply to other scenarios as well.

Recently we started working on the new researcher CV application. When choosing which web framework to use for the application, we stuck with what we’d already used in our previous search solution, namely Play! framework. The most current version of the framework that was accessible when we started working on the CV app was 2.1, so for the remainder of this post, this is the version I’ll be talking about. Worth noting is that we use Java as our language when working with the Play! framework.

So, what’s this post all about?

This post is about form binding and the model-view-view-model (MVVM) strengths and weaknesses of Play!, and how those weaknesses can be eliminated by using Knockout.js. The techniques, problems and solutions mentioned in the post foremost applies to single page/single model applications, but might very well apply to other scenarios as well.

The data model

Since the actual data model in the CV app is a little too large to be effectively used as an example, I’m just going to define a small model that we use through out this post, which hopefully will make it easier to follow.
Our example model:

public class User {
  public String name;
  public List cars;
}

public class Car {
  public String model;
  public String brand;
}

Form binding and MVVM in Play!

Play! comes with a collection of form helpers, that makes implementing the scala templates it uses much more easy and compact. For instance; if we want to loop over and create textboxes for all the Cars for a User, and their respective properties, we could write this:

 

(userForm : Form[User])
  @defining(userForm("cars")) { field =>
  @inputText(field("model"), '_label -> "Car model")
  @inputText(field("brand"), '_label -> "Car brand")
}

Play! provides us with the @inputText macro for creating an <input type=”text”>, connected to the corresponding entity in our model. When @inputText is rendered by the scala engine, it’s replaced by HTML-tags with id and name set to specific values understood by Play!. This is required, as Play! then uses its internal mechanics in its controllers to bind the POST or GET parameters from the submitted form back to the data model. You can read more about form submissions in Play! here.

Problems, solutions and pitfalls

Switching between editing and viewing a row

The problem: We want to be able to quickly switch between viewing and editing data in the model.

The solution: We’ll create one <div> with all the controls necessary for editing, and another <div> with all the controls for viewing. We’ll then toggle the visibility between these two using jQuery.

The pitfall: Let’s say our User has 10 Cars. We then start with 10 <div>, all containing their own element to trigger visibility, like a <a> or button. When a trigger element is pressed, we first have to find out which of the 10 elements that was pressed. So we’ll make jQuery listen to the ’mousedown’-event of the containing Cars-div, which we first select by using a specific class (for instance ’.carRowDiv’). In the handler for the ’mousedown’-event, we’ll then see if the target of the invocation has a specific class (for instance ’.edit’ or ’.view’), which is only used for the trigger elements, and then we’ll find either the view- or edit <div> inside the triggering <div> and toggle the visibility between them.
Just writing this down makes my head spin.

Apart from generating really messy code, we’ve also created tight coupling between our DOM and our data model. Certain HTML element, having certain CSS-classes are now tightly connected to certain functionality and properties in our data model.

Adding a row

The problem: We want to be able to add a new Car for the current User.

The solution: We’ll create a trigger element that, when clicked, creates a new edit mode <div> with all the HTML entities needed for the particular model entity (in our case a Car).

The pitfall: To understand the pitfall that Play! creates for us, we have to take a closer look on how the framework handles binding from a GET/POST to an actual Java class. When we use a form helper in Play!, the name and id of the HTML entities created are automatically generated. In the case of our Cars, the output from our @inputText(..)-block would be something along the lines of:

<input id="cars_0__model" type="text" name="cars[0].model" value="V70" />
<input id="cars_0__brand" type="text" name="cars[0].brand" value="Volvo" />

As you can see, both the id and the name corresponds to the name of the collection in the User model, the list index of that particular Car and the name of the property in the Car model. When we submit these values through a regular HTML form submission, Play! facilitates the translation of these parameters to an actual User class, with Cars and everything. It does this through the function:

Form Form.bindFromRequest(...)

If this call fails for some reason, no User class is created, but instead the returned Form<User> only contains a map of error messages, with information about what went wrong.

Now that we’ve gotten to know a little more about how Play! works with form submission and binding, we’ll go back to the actual problem of creating new rows. Let’s say we have an existing Car in our User model, and we want to add a new one. First of all, how do we know what HTML we should generate to display this new, empty, editable row? If we make a copy of the edit controls for the existing Car model, we run into a few problems. First of all; we’re dependent on already having an existing Car model to copy. Secondly; all the controls of the existing model will have values that we would have to replace with some default ”new Car”-values. Third; We would, after the copy, have duplicated of collection indexes in our HTML tags. Both Cars would for instance have an input with id ”cars_0__model”. Finding the index, parsing it as an integer and then increasing it is a delicate operation.

In the CV application, we opted for a different solution. We generate all our edit controls through a template which accepts the name and index of the model entity as a parameter. So when creating our ”cars[0]”, that’s what we’re providing to the template. We also generate a hidden version of this template for ”cars[x]”, which we keep for when the user decides to create a new Car. When this is to be done, we copy the HTML from the hidden template into where in the DOM it should be places and make it visible. We then have a jQuery script that runs through the entire DOM, looking for the regular expression ”cars[.+]”, and replacing each one of these with an sequentially incremented counter. So, we’re actually renaming all of our ids and names of all our Cars in the HTML every time we add a new one. This works, but is – as you can tell – far from optimal. Not to mention all the jQuery code that is required to perform the copying, positioning and renaming.

Deleting a row

The problem: We want to be able to delete a row from the model, but be able to let the user undo that removal.

The solution: We’ll create some sort of trigger entity in HTML, like a button or checkbox, and when the user presses it we’ll highlight the corresponding HTML tags as deleted and mark the entity as something that should be removed upon submission in the data model.

The pitfall: First of all, we’ll have to perform the same sort of HTML-jQuery-Play! magic that we had to do when switching between view- and edit mode, to dim all the different HTML entities in the <div> containing the relevant data. On top of that, we also have to mark the row as deleted, so when submitted to the controller, it can be handled correctly. In our case, we’ll start by adding a property to our Car model:

public boolean flaggedForDelete

This property will denote whenever a Car for the submitted User should be deleted. Now we have to use jQuery again to find the HTML entity that corresponds to that particular Cars flaggedForDelete-property, and toggle it back and forth between the it’s boolean values. Once again we’re coupling the model to the DOM in an unhealthy way.

You might wonder why we just don’t remove the HTML that makes up a Car when the user wants to remove it? We’ll, as stated above; we want to be able to undo that removal. This is hard when the HTML of the Car has been removed from the DOM. This approach is also problematic because we have to use the jQuery script from adding rows that renames ids and names (see above), to fill gaps that can be created in the index sequence. Now, when the controller binds the request, cars[0] might in fact be a re-indexed cars[1], and the total number of cars is 1 instead of 2. Then we have to figure out a way to know what car should be deleted, and what car should be kept.

Server side validation

The problem: We want to be able to validate the submitted data server side.

The solution: Play! fixes this for us when we use the bindFromRequest(…)-function. Just add one of the many included validation annotations for your class and/or field, or your own custom one.

The pitfall: Play! actually handles this pretty neatly with the error message map returned from bindFromRequest(…). Too bad the validation part of that function isn’t optional.

Let’s say our Car model has a @Required validation annotation for the brand property. This means that the user of the system has to provide a brand for each Car he/she tries to submit. Now let’s say the user creates a new Car, fills in the model, but not the brand, and then decides not to save that Car after all. The user marks the row for deletion and submits. Since the only way for us to get a hold of the actual data model is through the bindFromRequest(…) function, we’re also forced to perform server side validation. We can’t delete the created Car before we’ve bound the request parameters to the model, and we can’t bind the request parameters since the validation fails on the missing brand of the Car.

Validation in Play! should have been split into two steps. A first step that just makes sure that the binding of supplied parameters to our model is in fact possible. This part would fail in cases where, for example, the user tried to enter a string value into a input field that is that bound to an integer in the model. If the first step of the validation is successful, the model should be available for modification and altering. The second step should then go through the rest of the validation, to make sure things like allowed ranges for integers and length of string values are correct.

Knockout.js to the rescue!

First of all; what is Knockout.js? Knockout is a javascript framework of the likes of Angular.js, and provides bindings between models and views. It tracks dependencies, does auto refreshes when data in either the view or model changes and facilitates for things like templating.

One of the first things that you notice regarding the differences between Play! and Knockout.js is that the latter uses JSON based models stored in javascript variables, while Play! keeps its models in Java classes and uses Forms to wrap around these and pass as parameters when rendering templates. One of the first things we’ll have to change to integrate our backend with Knockout.js is therefore to replace our usage of Form<User> and instead render our templates with a JSON representation of the Java model. Transforming a POJO to it’s JSON representation can be easily performed by using Jackson. Now that we have our model as a JSON string, we can send that to our template, instead of a Form<User>. We can then map this to a variable in javascript and let Knockout.js use that for all it’s functionality. One thing we have to do with this model before we can actually use it is to transform the models values to what Knockout.js calls observables. Each value we want to be able to observe and react to we have to wrap in a little function. To make this easier we use the knockout-viewmodel plugin. This let’s us take a JSON string and directly convert it to a valid model, converting all field values to observables in the process.

Problems, solutions and pitfalls

Switching between editing and viewing a row

The solution: So, Knockout.js uses the custom HTML-attribute ”data-bind” to keep track of bindings between the view and the model. To make use of this, we’ll start the same way as we did with Play!, by creating two <div> elements. One with the edit controls and one with the view controls. For the <div> containing the edit controls, it would be perfect if we could write something along the lines of:

<div data-bind="visible: editing()">...</div>

Well, guess what? We can. Visible is a binding type of Knockout.js that tells the framework that the visibility of this HTML element should depend on the evaluation of the following statement. To make this work, we just have to add the editing property to our model (since it’s not part of the original model). Our viewmodel plugin lets us do this pretty easily, by making it possible to extend our model as we map it from JSON to our Knockout model, and passing this exended mapping as a parameter to the mapping function of knockout-viewmodel:

var extendedMapping = {
  extend: {
    "{root}.cars[i]": function(data) {
      data.editing = ko.observable(false);
      return data;
    }
  }
}

Now our Car model has a property called ”editing” which is just an observable boolean. We’ll create our second element (the one with the view controls) and just set the data-bind  attribute to !editing(). The parenthesis after editing just tells Knockout to run the function that watches the value of editing and return its most recent value.

Boom! Done.

Adding a row

The solution: This one is even easier. Since User is a JSON object, and Cars is just a JSON array belonging to that object, adding a new Car to a model is as easy as pushing a new object into the array. The only thing that really needs to be done is to create a constructor function for an empty Car:

function createEmptyCar() {
  return { brand:ko.observable(), model:ko.observable() };
}

Now we data-bind our HTML trigger element that adds the new row to a function that does the push to the array:

<input type="button" data-bind="click: addNewCar()" />
function addNewCar(model) {
  model.cars.push(new createEmptyCar());
}

Then we render all of our Cars using the built in template binding type of Knockout.js:

<ul data-bind="template: {name: 'carTemplate', foreach : cars}"></ul>

’carTemplate’ being a script template:

<script id="carTemplate" type="text/html">
  ... HTML code here ...
</script>

Knockout.js handles everything else for us. It detects that the cars-array has gotten a new entry, renders it using our script template and makes sure that all the values we might have added in the constructor is present and up to date.

Deleting a row

The solution: Another difference between using the form helpers of Play! and the JSON models of Knockout.js is that when we submit our page to be handled by a controller on the backend, we won’t have access to a Form, but instead just map our JSON model directly to an instance of our User class. This way we can bypass the semi ugly bindFromRequest(…) and therefore handle deletion of entities and manually call the validation routine on our User. We can still send the handy map of errors that the Play! validation annotations gives us back to our template, but now we’re not forced to perform this validation before we actually get a hold of our model.

So we’re still going to use our delete flag (flaggedForDelete), but this time we can make use of Knockouts great data bindings:

<div data-bind="css: {deletedRow: flaggedForDelete()}">
<input type="text" /> ...</div>
<label>
  <input type="checkbox" data-bind="checked: flaggedForDelete" />
  Delete
</label>

So, the checkbox is databound to our current entities (this code goes in the script template for cars – carsTemplate – which make the current entity the currently rendered Car) flaggedForDelete property. The

that holds all the controls for editing then uses the CSS binding type, which translates to ”the CSS class deletedRow should be added if the value of the statement flaggedForDelete() is true”.

Server side validation

The solution: Since we’re no longer forced to use bindFromRequest(…) to translate our passed parameters, but rather just use jackson to map our JSON model to our Java class, server side validation just became much easier. First of all, any problems with constructing a class from our model will manifest itself as an error in jackson. These can be catched and returned back down to the web page for display. If the mapping is successful, we can now manually call validate on our Java class (which still has the validation annotations from Play!) and see if it all goes well. If not, we can take the map of errors that the validation routine returned and use that display that on the web page.

Another benefit we get is more integrated client side validation. Previously we could have of course have used jQuery or some other javascript framework to perform runtime validation on the client side. This would however probably have been problematic, as all our values are static from the time of the rendering of the scala template. With our Knockout version all of our data is rendered as needed, and easily available through the JSON model. One great plugin for Knockout.js that provides mechanisms for validation is knockout-validation.

Conclusions

Using Play! as a ”stupid” JSON API kind of makes a lot of sense. We still get the frameworks great routing system, the possibility to integrate our datamodels with EBean (even though we perhaps should consider migrating to MongoDB now that all of our models are submitted to our controllers as JSON?), server side validation through annotations, scala templating and much more. Knockout.js is however a much more natural choice when it comes to the frontend. Creating dynamic data and form handling is just much easier, and makes a lot more sense.

By making the interface towards the controller much simpler (JSON instead of Play! Forms), we’ve also opened the door for a more asynchronous approach to saving, deleting and updating our model. We can now do background polling against the backend to make updates of the model (and therefore the rendered HTML) as its values change in the database.

The javascript code base is also significantly smaller using Knockout.js. Previously we had around 400 lines of jQuery code. Every part tightly coupled to the DOM. By using Knockout.js we’ve reduced that down by ~75%. There is some very slight coupling to the data models kept in our Java classes, but no coupling what so ever against the DOM.

Play! <3 Knockout.js == true
Profilbild
System architect at the IT-department at KTH

3 reaktioner till “Improving Play! 2.1 with Knockout.js”

  1. Thx so much for the post…it was very informative and I am very excited to use play and knockout. I am starting out with play but I have extensive experience with knockout. I was very happy to see that they can coincide.

    Can you post the play-knockout project on github or zip it and email it to me…I would greatly appreciate it.

    Also, if you have some good links/tutorials to get started with play. I am coming from spring jpa background and have been developing for 10 years

  2. Hi Jeff!

    Glad too see that my post was of some use. Unfortunately the project using Play and Knockout on our side is nothing I can publish online. As far as tutorials I think that knockouts own homepage has some excellent right-to-the-point documentation.
    As I’ve more and more gravitated towards using nodejs (you should check that out btw) I’m not really up to date on the best tutorials for the latest Play versions.

    Drop me a mail if you have any questions!

    /J

Lämna ett svar

E-postadressen publiceras inte. Obligatoriska fält är märkta *