Musings by @thedevel

Musing #26: HTML Form Media Type

926 words · 29 Apr 2012

The very nature and purpose of a Web application is to be responsive and reduce the burden on the end-user of the request/response cycle. Cross-browser DOM libraries, such as jQuery, have made it perceivable to take on building JavaScript-heavy client-side apps. But even then, the overhead required to make the user experience decent with the previous generation's JavaScript engines was quite a tall order for the sole developer.

Today's JavaScript engines are incredibly fast and as a result, server-side-like components are being moved (or mirrored) on the client-side. Full stacks and frameworks are being developed for the client. JavaScript is being used like a real language.

This of course requires a shift in the previous mindset of what logic should be implemented where when handling user data. For example, before Web frameworks such as Django or Ruby on Rails got exceedinly popular, most Web forms were written by hand. With a framework, that is typically all handled for you with a forms API. It not only handles input validation and cleaning, but it also generates the HTML markup for you and with any data that needs to be pre-filled in the form.

This is, of course, a huge convenience! Since the form generation and validation is all being handled through one means, you can be sure the GET params or POST data that is submitted with the form will be recognizable on the server.

I ran into a problem though, and it has to do with this mindset shift. Much of my work these days involve building interfaces for single-page Web apps. This of course can get very complicated, very quickly. For any decent sized app with data entry, this means all the necessary forms and validation needs to be ready to go when the user needs it.

Forms are typically re-used between instances of data (as suppose to creating separates form for each instance), so there is a need to load, validate, submit, and unload data from forms on-the-fly. All that lovely pre-fab HTML generated on the server-side is effectively useless as is.

I have been musing over what a form-data media type would look like. That is, what is the minimum data necessary to construct a form. There are two parts to this including the form/data schema and the data itself. The schema is mainly what I am concerned with since if it can be descriptive enough, the data handling should be trivial.

Example

As an example, the media type could be named application/form-schema+json and look something like this:

// The main property is `fields` which is an array of objects (to preserve
// order) with properties that roughly map to the HTML form field
// attributes.
{
    "url": "/some/endpoint",
    "fields": [{
        "label": "Full Name",
        "name": "name",
        "type": "text",
        "validate": {
            "type": "string",
            "min": null,
            "max": 40,
            "required": true
        }
    }, {
        "label": "Age",
        "name": "age",
        "validate": {
            "type": "number",
            "min": 0,
            "required": false
        }
    }, {
        "label": "Email",
        "name": "email",
        "help_text": "We will never distribute or sell your email address.",
        "validate": {
            "type": "email",
            "required": false
        }
    }, {
        "label": "Sign-up for newsletter",
        "name": "newsletter",
        "validate": {
            "type": "boolean",
            "depends": ["email"]
        }
    }]
}

Form Options

  • url (optional) - The URL this data may be submitted to. This is optional since the URL may be implied (parent page) or not applicable.
  • fields - An array of field objects

Field Options

  • label (optional) - Verbose name of the field. If not supplied, the name will be used
  • name - Property name of the resulting data. This corresponds to the name=foo HTML attribute on form field elements.
  • help_text (optional) - A description or help text for the field.
  • validate (optional) - An object containing various validation parameters
  • type (optional) - An explicit form field type to be used. If not supplied the type will be inferred by the validate options if defined, otherwise a standard text input will be used.

Field Validation Options

  • type - Name of a JavaScript datatype which will be used to perform value cleaning prior to validation. Types include number, date, string, and boolean.
  • required - A boolean denoting if the field is required to have a value.
  • nullable - A boolean denoting if a null value is allowed. If true, when values are being coerced, empty values will be converted to null. This take precedence over the required option.
  • min - A minimum value relative to the type. For strings this is length, for numbers this is value.
  • max - A maximum value relative to the type. For strings this is length, for numbers this is value.
  • regexp - A regular expression to be used to validate the value. Only applies to strings.
  • depends - During validation if presented with a non-falsy value, the fields listed in this array must also have a value (and be valid). This enables light dependency checking between related fields.
  • choices - An array of choices to be used to validate against.
  • url - If more robust server-side validation is necessary, a URL can be specified that will be used to validate the input data. If the value is true, it will use the top-level defined URL. This validation will have happen after any client-side validation necessary by the above options.

With a formal media type defined, clients could be implemented to understand this definition and be able to create forms on-the-fly, validate the form data and submit the data in one shot.