Typesafe Activator

Play Example Form

Play Example Form

jamesward
Source
February 3, 2014
playframework webjars bootstrap

This application provides an example of form processing with Play Framework, WebJars, and Bootstrap 3.

How to get "Play Example Form" on your computer

There are several ways to get this template.

Option 1: Choose play-example-form in the Typesafe Activator UI.

Already have Typesafe Activator (get it here)? Launch the UI then search for play-example-form in the list of templates.

Option 2: Download the play-example-form project as a zip archive

If you haven't installed Activator, you can get the code by downloading the template bundle for play-example-form.

  1. Download the Template Bundle for "Play Example Form"
  2. Extract the downloaded zip file to your system
  3. The bundle includes a small bootstrap script that can start Activator. To start Typesafe Activator's UI:

    In your File Explorer, navigate into the directory that the template was extracted to, right-click on the file named "activator.bat", then select "Open", and if prompted with a warning, click to continue:

    Or from a command line:

     C:\Users\typesafe\play-example-form> activator ui 
    This will start Typesafe Activator and open this template in your browser.

Option 3: Create a play-example-form project from the command line

If you have Typesafe Activator, use its command line mode to create a new project from this template. Type activator new PROJECTNAME play-example-form on the command line.

Option 4: View the template source

The creator of this template maintains it at https://github.com/jamesward/play-example-form#master.

Option 5: Preview the tutorial below

We've included the text of this template's tutorial below, but it may work better if you view it inside Activator on your computer. Activator tutorials are often designed to be interactive.

Preview the tutorial

Overview

This application provides an example of form processing with the following features:

The design of this example differs in two significant ways from the traditional Play form processing examples.

  1. Distinct model and form classes. Most examples of form processing in Play "overload" the model classes to serve two tasks: (1) specify the database schema structure; and (2) provide the "backing class" for forms. Requiring a single class to perform these two tasks appears to work well only when the models and forms are both simple and similar in structure. In this example system, the views.formdata package provides classes for form processing, and the models package provides classes for database schemas. Since Play requires the backing classes for forms to have public fields, this separation means that model classes can have private fields, avoiding well documented problems.

  2. Explicit field constructors for Twitter Bootstrap 3.x. The canonical recommendation by Play developers for users of Twitter Bootstrap is to create a single "implicit" field constructor. The problem with this recommendation is that a single implicit field constructor cannot satisfy all of Twitter Bootstrap's layout requirements for form controls (for example, multiple checkboxes). This example illustrates a more general solution in which normal (i.e. "explicit") scala templates (i.e. field constructors) are defined in the views.bootstrap3 package for each of the Twitter Bootstrap controls. IMHO, the code is significantly easier to understand and debug for Java-based Play framework users.

Steps to understanding the system

Run the application.

Open Run and after the application is compiled it will be automatically started. You can then open http://localhost:9000 in your browser. To display the single form illustrated at the top of this page. The form displays the fields associated with a "Student": Name, Password, Hobbies, Level, GPA, and Majors. Some of these are required, some are optional. Try filling out the form in both valid and invalid ways and pressing Submit to see what happens.

When you submit a valid version of the form, the system will redisplay the form with exactly the same data that you entered, and also show a representation of the Student model instance created from the form values.

Run the tests.

The tests will automatically run and you can see the results by navigating to Test in Activator.

We'll come back to this later, but this step verifies that tests run correctly in your environment.

Review the controllers.

Now review the controller class. Application has just two methods: getIndex() which displays the form in the index page and postIndex() that processes a form submission from the index page. See the routes file for how this is wired up.

The getIndex method takes a Student ID as a parameter. If the value is 0, then an empty form is displayed, otherwise the form is displayed pre-filled with the data associated with the Student ID. For example, you can retrieve the data for the student with ID 1 using: http://localhost:9000/?id=1. The system instantiates a couple of students on startup.

By looking at the controller, you can see the basic approach for either form display (HTTP GET) or form submission (HTTP POST):

  • An instance of StudentFormData is passed to the templates for rendering. This class has public fields as required by Play, and they are all String or List[String] because binding only works on strings.

  • Other component entities (Hobby, GradeLevel, GradePointAverage, Major) provide helper methods to support display of their values as strings along with the student's current value(s) for those components.

  • The Student.makeInstance and Student.makeStudentFormData methods provide conversion between the form data and model representations of a Student.

Review the models.

Skim through the models package. There should be no surprises; it parallels the form pretty closely. Some things to note:

  • A Student entity contains non-primitive, complex components such as a list of Hobby entities and a list of Major entities.

  • The models have private fields and getters/setters. (Sorry, I'm old school that way.)

Review the views.

The views package is where things get most interesting. The main and index templates are pretty much what you'd expect.

Note that the main template shows how to import JQuery in case you want to use Bootstrap Javascript components.

The second thing to review is the views.bootstrap3 subpackage, containing Bootstrap 3 Scala templates for various form controls. Kudos to Jason Pearson to writing these templates and making other helpful changes.

Finally, the views.formdata subpackage contains the single backing class (StudentFormData) required for this application. Note that the backing class consists of public fields containing the String data to be displayed/bound in the form, as well as a validate() method that determines if the submitted form data is valid or not.

Review the tests.

Testing is pretty straightforward, uses Fluentlenium, and implements the page object pattern.

Start by looking at IndexPage. This class implements a bunch of methods to fill out the form and check to see whether the displayed form contains a success or error message. Note that most of these methods depend upon finding an HTML element with a specific ID, and so the Bootstrap Scala templates need to make sure these ID fields are set correctly.

The actual test code is in ViewTest. There are four tests: one that just checks that we can retrieve the page, one that tests that submitting an empty form generates a validation error, one that submits a form filled out from a valid Student ID, and a final one that fills out a valid form manually by using the IndexPage methods.

Getting tests to work exposes an unfortunate library versioning issue: HTMLUnit requires a version of JQuery no later than 1.8.3, while recent versions of Twitter Bootstrap have a Maven dependency of JQuery 1.9.0. build.sbt illustrates how to load a newer version of Bootstrap with an older, HTMLUnit-compliant version of JQuery. Another solution is to use PhantomJS rather than HTMLUnit; then you can use current versions of JQuery. This fork shows how to use PhantomJS.

Issues

While this code works and is pretty easy to understand, there are at least two design problems with it that I can see:

  • Verbosity. It's kind of a drag to have two representations for a Student, one as a model and one as a backing class for forms. I know that I presented this as a feature, but at the end of the day it's born of necessity. Perhaps there exists an elegant way to implement composite entities (i.e. a Student that contains a List of Hobbies) in which display, binding, and validation can be done easily and understandably with a single class.

  • Integrity. The code provides validation in the StudentFormData class, and certain methods (such as Student.makeInstance) must assume that they are being passed a valid StudentFormData instance. That kind of assumption is worrisome, and annotation-based constraints could avoid it. Annotation-based constraints also offer the potential to simultaneously apply to both the database model and the form validation, which would be really nice. As a first step, I played around for a while with Custom Data Binding, but could not get it to work correctly for lists of Hobbies.

If you see ways to solve these problems, please feel free to fork this code and implement your changes.

Screencast

Click the image below to watch a 28 minute walkthrough of this system.

Note that the following screencast documents a previous version of this system which used Twitter Bootstrap 2.3.2 and Play 2.1. While the templates in the bootstrap3 package have been rewritten, the rest of the system remains unchanged.

Acknowledgements

This example is a descendent of the original play-form-kludge and Jason Pearson's very helpful improvements.

comments powered by Disqus