13 February 2016

Play Framework Conditional Form Mappings

I'm rather partial to Play Framework's form mappings. Their expressiveness sets a benchmark, bettered by no other web framework I've encountered. And I especially love that there is no need for reflection.

However, my colleagues and I at the VOA uncovered a key use-case that the validations do not handle well - conditional mappings; where validation for one field is based on the value of another field.

Initially we hacked away at potential solutions which were complex and messy. We then submitted a pull request to Play which was rejected. Finally, we created and open sourced the play-conditional-form-mapping library.

We believe this library complements the declarative nature of play's inherent form mappings and solves the problem as simply as we could possibly make it. This post is an introduction to the library.

First, let's quickly reason about why this is important from a user's perspective.

Usability is Crucial

As a user I get immensely annoyed by complex web forms. One scenario that is particularly aggravating is when you submit a form, get some validation errors, fix those errors, submit the form again, and are kindly rewarded with some completely different errors.

I'd expect many who have witnessed user research  to be familiar with this frustration, if they haven't had counselling for it themselves. I'd also be shocked if empirical data did not support the idea that failed form validations correlates inversely with conversion rates (if you have the data to prove or disprove, please share).

Also importantly, as a user I want to be directed to the problem. I want to know which field I need to make changes to, even if the rule spans multiple fields. Our good friends GDS have conducted research showing the importance of this although I'm sure common sense will suffice for most.

Play Framework's Default Solution

Out of the box Play Framework's mappings provide a verifying() function that allows you to interrogate the model after it has been bound to. However, if any of the individual fields cannot be bound to, the model-level rules cannot be applied.

This leads to the user-frustrating scenario just discussed, where multiple iterations of submitting the form and fixing the validation errors are necessary.

To demonstrate this problem, have a look at the unit test below (taken from the library's unit tests)

In this scenario, the user must specify their country if indicating they are not a resident of the UK. They must also provide an email regardless of their residence. But as you can see from the first test case, if they omit their email, they will not see an error about country being required - that will come after they submit the form again.

The second test case demonstrates the other important impact on UX - that model-level errors cannot be assigned to a specific field,  prohibiting the ability to direct users to where they need to take action.

Under the governance of GDS and based on feedback from user research, this is not good enough. So please let me introduce to you our micro-library that solves these problems.

Play Conditional Form Mappings

So how does our micro-library solve the above problems? It uses an expressive micro-dsl that creates field-level errors. The example below shows how the library can be used to improve the previous scenario.

Voila, all we had to do was use the mandatoryIfTrue() mapping, that makes one field madatory if the referenced field is true. And importantly, as the test shows, the error is at the field level (so users can be directed to it) and both errors are shown in one iteration of validation

Inside the library you can currently find a selection of these conditional mappings, including: mandatoryIfFalse(), mandatoryIfNot(), and onlyIfAny(). However, the library also provides the ability to compose and create your own multi-condition conditions as demonstrated below:

Pull Requests & Feedback Much Appreciated

It's a very small library, but one we believe fills an important void in Play Frameworks mappings; a void that leads to a diminished user experience of your website.

We'd love to hear your feedback if you use the library and we'd be chuffed if you decided to contribute your own great ideas to improve it.