Introducing Supler: a Functional Reactive Form Library

Let’s face it. Creating websites with complex forms is a pain. Writing the HTML in the frontend, the supporting javascript, defining mappings in the backed, server-side validation, and – let’s not forget, it’s 2014 – corresponding client-side validation, cause a lot of duplication of code and effort, and result in frustration.

Screen Shot 2014-09-17 at 11.51.00

There’s quite a lot of frameworks offering end-to-end solutions to the problem, most often generating and rendering whole pages on the server. However, using them usually ends in even more pain. The generated HTML and Javascript is hard to customize and extend with custom bindings. You end up being constrained with what the framework authors envisioned.

That’s where Supler comes in. It’s a library, with a very focused set of functionality:

  • backend: Scala DSL for defining forms based on a backing class (with validation)
  • backend: generating a JSON description of the form
  • frontend: generating HTML basing on the JSON form description
  • frontend: serializing a form to JSON and backend: applying the values
  • frontend: running client-side validations, for supported validations
  • backend: running server-side validations, and frontend: showing the results to the client

As important as Supler’s features, are its non-features. Supler does not define or manage:

  • what web framework you use
  • how is the JSON data served
  • how you write your JavaScript
  • the lifecycle of the entities on the backend
  • the lifecycle of the data on the frontend
  • if and what ORM you use
  • how are your entities defined on the backend

Looks interesting? You can find the sources on GitHub. Give us a star if you’d like to see Supler developed further!

Demo

But, to the important parts – how does Supler look in action? You can see a live demo here. Try to remove some values, enter incorrect values, submit the form, refresh the page. You should sometimes get client-side validation errors, sometimes server-side validation errors, and sometimes, hopefully, success. A more detailed description of how the code works is below.

First, we need to define the form on the backend. Here you can see the model, three simple case classes:

1
2
3
4
5
6
7
8
case class Person(
  firstName: String, lastName: String, age: Int,
  address1: Option[String], address2: Option[String],
  gender: String, cars: List[Car], legoSets: List[LegoSet])
 
case class Car(make: String, year: Int)
 
case class LegoSet(name: String, theme: String, number: Int, age: Int)

Next, we can use Supler’s DSL to define forms for editing Cars and LegoSets:

1
2
3
4
5
6
7
8
9
10
11
val carForm = form[Car](f => List(
  f.field(_.make).use(dataProvider(_ => List("Ford", "Toyota", "Mondeo", "Transit"))).label("Make"),
  f.field(_.year).validate(gt(1900)).label("Year")
))
 
val legoSetForm = form[LegoSet](f => List(
  f.field(_.name).label("Name"),
  f.field(_.theme).label("Theme").use(dataProvider(_ => List("City", "Technic", "Duplo", "Space", "Friends", "Universal"))),
  f.field(_.number).label("Set number").validate(lt(100000)),
  f.field(_.age).label("Age").validate(ge(0), le(50))
))

As you can see, a form is a list of fields. Each field is defined using a type-safe closure, with an optional label, select options, and validation rules (which will be run both on the frontend and backend).

We can now define a form for Persons, re-using the previously defined forms:

1
2
3
4
5
6
7
8
9
10
11
val personForm = form[Person](f => List(
  f.field(_.firstName).label("First name"),
  f.field(_.lastName).label("Last name")
    .validate(custom((e, v) => v.length <= e.firstName.length, (e, v) => ValidationError("Last name must be longer than first name!"))),
  f.field(_.age).label("Age"),
  f.field(_.address1).label("Address 1"),
  f.field(_.address2).label("Address 2"),
  f.field(_.gender).label("Gender").use(dataProvider(_ => List("Male", "Female"))),
  f.subform(_.cars, carForm, Car(null, 0)).label("Cars").renderHint(asList()),
  f.subform(_.legoSets, legoSetForm, LegoSet(null, null, 0, 0)).label("Lego sets")
))

In addition to the previous definitions, here we have a custom validation rule, which will be run on the backend only – no translation to JavaScript is provided. Apart from simple fields, this form also contains two subforms – for lists of a person’s cars and Lego sets. The default rendering of a subform list is a table; an alternative is a list of forms, which is used for cars.

And that’s it for the backend part. How you serve the JSON is entirely up to you. In our demo server we use spray.io for HTTP part, but it can be any web framework.

Now, the frontend. We need to designate a place, where our form will be rendered. A simple div will suffice. We also need a submit button and some placeholder for feedback:

1
2
3
4
5
<div>
  <a href="#" class="btn btn-primary btn-lg" id="submit" role="button">Submit</a>
  <p id="feedback"></p>
  <div id="form-container"></div>
</div>

Next, when the page loads and we obtain the JSON description of the form (e.g. via a simple ajax call), we create a SuplerForm instance and instruct it to create and render the form:

1
2
3
var formContainer = document.getElementById('form-container');
var form =  = new SuplerForm(formContainer, {});
form.render(formJson);

And finally, we need some JavaScript to call the correct methods; here I’m using JQuery to send the data to the server and handle the response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$('#submit').click(function() {
  var hasErrors = form.validate();
 
  if (hasErrors) {
    feedback.html('There are client-side validation errors.');
    feedback.show();
  } else {
    $.ajax({
      url: '/rest/form1.json',
      type: 'POST',
      data: JSON.stringify(form.getValue()),
      dataType: 'json',
      contentType: 'application/json; charset=utf-8',
      success: function (data) {
        if (form.processServerValidationErrors(data.validation_errors)) {
          feedback.html('There are server-side validation errors');
        } else {
          feedback.html(data.msg);
        }
 
        feedback.show();
      }
    });
  }
 
  return false;
});

Remember that form is an instance of the SuplerForm class. Upon an invocation of validate(), client-side validations are run, and incorrect fields are marked as such. If there are no errors, the form is submitted, and depending if server-side errors where returned or not, they are applied to the form, or a success message is shown.

The full sources are in the example subproject of the Supler codebase.

Other important Supler traits

An important note, is that both Form and Field instances in Supler are re-useable and composable: you can compose a form of previously defined fields and forms; you can also have stand-alone field definitions.

The generated HTML has a simple structure and predictable naming, following 1-1 what’s in the form definition. Hence it’s easy to lookup a form element or form section generated by Supler and further customize it, either by adding styling, or additional client-side JavaScript logic.

It is also possible to customize the rendering process and influence how all parts of the HTML are rendered by providing custom render functions. Individual HTML tags, depending on type, templates of rendering a field, and more, can be overriden.

What’s next?

What you can see currently is only the beginning for what we have in mind for Supler. A rough list of the features that we hope will form the future Supler 1.0:

  • customization of form rendering via HTML snippets
  • dynamically re-rendering form parts basing on user input
  • support for all kinds of form controls
  • i18n support
  • server-side value conversion

If Supler looks interesting to you, let us know by starring the project!

Brought to you by Adam & Tomek

  • Andy

    Does this library really work with any web framework? How would it work with a Python or Haskell web framework?

  • You got me there, it should be Java/Scala web framework. Or not even a framework, you can use plain sockets if you like ;) But you still need a way to invoke our Scala code at some point.

  • Daniel Kos

    I’m sorry but IMO this is (however somehow tempting) the wrong direction. You’ll end up in extending your framework endlessly because new dependencies between controls will show up or new way of layouting them will be needed (but I guess html snippets should somehow cover this problem). I think it’s better to start using scalajs as it’s better and better with each release (there is even ajax communication wrapped by client actors :) ) and start porting reactjs to it or write something more suitable for scala but based on the same idea.

  • I don’t really see how Scalajs is involved here, it’s “just” a language which compiles to Javascript. Currently to write the javascript part of Supler we use Typescript, but I suppose we could use Scalajs, Clojurejs, Coffeescript, Jsjs or anything else ;)

    As for React, it is a great library, but I think it solves a different problem. React tries to solve the problem of writing stateful pure-javascript apps. Supler tries to solve the problem of writing complex forms, which have to be synchronized between a client and a server, and validated on both.

    Btw. we really want to create a library, not a framework :) What kind of dependencies do you have in mind? We want the layout to be entirely changeable, to the point of specifying completely custom per-field HTML snippets, which will only have to include the correct meta-data.

  • Daniel Kos

    For example if you select something in one combobox then content of another must be reloaded. If you set a checkbox two other fields will popup and so on. There is a lot of interaction between controls (dependencies). That’s why I thought about reactjs (or any other lib that simplifies dom modifications) because you need javascript to handle it in one form or another. That javascript could be expressed in your API somehow (for exmple: f.field(_.age).label(“Age”).showIfvalue(‘male’).inField(“sex”)) and generated by render method but there always will be a case you didn’t think of.

    I mentioned scalajs because I had an impression that you want to express “html part” entirely in scala (to define validation model and form layout at the same time). With scalajs your library could generate complex form with interactions seamlessly expressed in scala (that will be translated to js by scalajs) as well as server validation but this way you won’t be constrained when it comes to define complex controls relations.

    But if you have a solution for those problems I’m very interested to see it :)

  • Ok, so to solve the dependencies/re-rendering problem we want to take a bit different approach. While it would be nice to have it all done on client-side, as you write, it would be actually quite complex to specify it with one API for the client&server; and even then, there would be cases when you would have to go to the server (e.g. to fetch some data for a dropdown from the DB). So the approach we want to take is simpler: all such dependencies will be resolved on the server, and the form will get updated on the client accordingly. So if a field depends on another field, or if there are fields that depend on it, in case it changes, a request will be sent to the server, and if there are changes, a new form definition will be sent back.

    I’m aware that this won’t work for all cases, sometimes you will want to have a hand-crafted form, with as much logic on the client as possible. Then you should use ReactJS, or any other of the many JS libraries.

    If, however, you have a large number of forms, and you want the basic validation logic to be on the client, while the rest can be done on the server, then Supler is for you.

    As for scalajs, we would still like to separate the form definition part (server), from the layout (client). While the form definition specifies the default order of fields, and can provide “render hints”, the client can customize that in any way that is deemed suitable. That way we aim to have fully reuseable server-side form definitions, decoupled from the presentation.

  • Radek Beran

    Starred! Looking forward for the next progress. I really appreciate using type-safe closures to define fields and your effort to solve also the client-side parts of form creation (client-side validation messages and implemented default way of HTML snippets rendering). I am quite interested in planned ways of customization of form rendering, because of such non-invasiveness of the library is important. As you have stated, the generated HTML and Javascript should be customized more easily in form frameworks/libraries.

    I have written somewhat similar open source form library – Formio, available at http://www.formio.net, but it is written in Java language (inspired by form handling in Play! framework). It also supports immutable form definitions that can be composed from reusable parts – form fields and nested forms (mappings); and supports (immutable) backing classes.

    The library has an abstraction for loading parameters from a request (RequestParams) with various implementations (ServletRequestParams, PortletRequestParams, …), thus it is independent on (non-)web framework you use, like the Supler library is.

    Formio is simple tool with helpful bi-directional data binding and server validations, it does not address itself client-side validations or rendering the HTML snippets for fields (that is left to the user that can use additional client-side validation JavaScript tool and create HTML snippets itself – the form definition filled with data can be simply mapped to predefined reusable template parts).

    Passing of values via JSON in Supler is more client-focused solution. What about uploaded files? Are they somehow represented in transfered JSON and how the uploaded file can be bound to the model?

    I thing the discussed Supler’s approach of updating state of related form fields on the server is fine (some server-side logic can react on such events), until there is no restriction in implementing custom client-side logic.

    I agree that creation of complex forms is (long time annoying) pain that should be eased by non-invasive configurable libraries. There are also lot of related follow-up areas that can be covered by the library or some cooperating libraries: Protection against various attacks or spam, storing/resending of submitted form data (for feedback forms) and more.

    Btw: The live demo is Super or Supler (maybe both :-))?

  • Thanks for the pointer to Formio – we’ll certainly take a look for inspiration :) We haven’t yet thought about support for uploading files, but it will probably end up being bound to an InputStream, byte[] or such.

    Supler is at a very early stage, so it probably will take a while before we get to stuff like spam protection. But very interesting ideas, nonetheless!

    The live demo is of course both, thanks for spotting that ;)

  • Kabir Idris

    This look good to start with, but how do you deal with class object id’s field that are not meant for the “form”
    e.g case class Person(id: Int, name: String)

    Person.id should not be validated by supler because its from the db.

    can you help on this?

  • You don’t have to include every field of the case class in the form, you can just have a selection. Depending on your exact situation, the id can be omitted entirely, or it can be a hidden field, or it can be a meta-field which can be read before applying values to the entity (to lookup the entity in the first place).

  • Kabir Idris

    thanks for the response. However, do you have any code sample of supler and play framework?

  • There’s an open PR with one example that I know of: https://github.com/softwaremill/supler/pull/58