If you want to use Jaffa using just the features shown so far you could have a fully functional form already. However one of the goals we originally had for the project was to make handling large forms much easier. In this light the features we've shown so far can really just be considered as building blocks... widgets is where it's really at.

Several widgets ship with Jaffa to make building a feature rich form very easy (a number are demo'd on this page), but you can also build your own widgets to add to the toolkit. This setup is intended for use in environments where parts of the dev team can do the work on widget design, and others can focus on metadata and form layout.

Some Basics

Widgets are generally designed to be called like existing jQuery UI Widgets, in that a basic DOM element is targeted. We do require an ID attribute on this DOM element. eg:

<div id="dropDownWidgetSample"></div>

With regards to styling widgets, the base widget (which all widgets extend) will take care of injecting additional classes you request, as long as you provide them as part of your config ("class-list": widgetBranding). For the sake of brevity the examples below don't show this, but most inject a class structure something like this:

var widgetBranding = {
    "": ["control-group"],
    "label.widgetLabel": ["control-label"],
    "label.radioLabel": ["radio", "inline"],
    ".jaffaValidationError": ["alert", "alert-error"]
};

All this is doing is looking at the selector value on the left and injecting the extra classes on the right (in this case Boostrap classes). All the selectors are constrained to being children of the top-level container element, and the empty selector used for the first value targets the container itself. Given that many widgets implement jQuery UI widgets for controls this usually leaves you with a few options for branding:

  1. Target Jaffa's own class structure. Jaffa makes no efforts to style form elements, but it does usually sprinkle classes lightly through whatever it is doing. Usually just to act as a selector, or for your local customisation.
  2. Target jQuery UI's classes if the widget in question (or your page) is making use of them. Check out the 'Styling' tab as well for some jQuery UI themes.
  3. Inject classes from your local CSS framework as per above. The demo is doing this with Bootstrap to try and play nice with external frameworks. Plus Bootstrap just has some dead-sexy basic form elements.

Default Widgets

Some examples of the basic widgets that ship in Jaffa. With varying configuration there are more options then displayed here, but this covers the high notes. Each widget has its configuration in the contextual help elements attached to it, so just click the question marks if you want to see how things are done.

Text Fields

The humble text input is (at face value) fairly basic, but Jaffa offers a few features that let you do some interesting stuff with a text widget.

Basic

The simplest of examples first though.

As you can see, the config is fairly simple. In fact we've put more effort into attaching this help text then the widget itself takes to get up and running.

<div id="textWidgetSimple"></div>
<div id="textWidgetSimpleHelp">... all this stuff...</div>
$("#textWidgetSimple").jaffaText({
    "label": "A text field:",
    "field": "simpleTextField",
    "help-content": "#textWidgetSimpleHelp",
    "mandatory": true
});

Or we can change it into a <textarea/> with very little effort. We also added a default value... cutting edge stuff.

All we had to do was provide the textarea config entry and the widget will adjust. The rows and cols are optional, since your styles may be a better way of setting these dimensions. The widget will still create a <textarea/> without them. There is no error checking on what you provide here by the way, so if you provide nonsense values, the browser will decide what it wants to do with them.

<div id="textWidgetTextarea"></div>
<div id="textWidgetTextareaHelp">... all this stuff...</div>
$("#textWidgetTextarea").jaffaText({
    "label": "A textarea:",
    "field": "simpleTextarea",
    "default-value": "I have a default value...",
    "help-content": "#textWidgetTextareaHelp",
    "textarea": {
        "rows": "5",
        "cols": "40"
    }
});

Next, the 'Validating Text' tab shows how the widgets can integrate with Validatious.

Validating Text

Back on the 'Validation' tab we got our first look Validatious, which is especially useful when it comes to validating text fields. Most other controls/widgets tend to deal with fixed vocabularies and/or formats (like dates), so we generally just allow simple mandatory flags in their config (you can target the underlying field with your own validation though if you want to get fancy).

A text field is pretty wide open though, and this widget has no such luxury. With so many validators built in to Validatious for handling text however, the focus of the widget is mainly on letting you pick and choose from validation rules and provide custom responses in the UI. Providing this sort of validation config (v2Rules) does take preference over a basic mandatory flag, so remember to add a Validatious required rule if you want the field to be mandatory.

Here's an example of an mandatory field, and it only accepts URLs.

So the presence of the v2Rules property is the first thing the widget is looking for, then each rule needs to have a name. The widget uses this for its own index, as well as notifying Jaffa of a new rule using this name (as per the core validation process). If you don't provide a message for the rule, this name will also be the only feedback you receive on screen (ie. it failed).

<div id="textWidgetUrl"></div>
<div id="textWidgetUrlHelp">... all this stuff...</div>
$("#textWidgetUrl").jaffaText({
    "label": "Enter a URL:",
    "field": "urlTextField",
    "help-content": "#textWidgetUrlHelp",
    "v2Rules": {
        "mandatory": {
            "validator": "required"
        },
        "needsUrl": {
            "validator": "url",
            "message": "I asked for a URL dumb-ass!"
        }
    }
});

And another example, this time of a slightly more complicated set of rules that also require parameters. We are going to do a trivial password complexity test (REALLY trivial, this isn't an example of good practice in password complexity) using only the Validatious built-in validators.

Config is starting to get a bit longer now, but it's still fairly simple though. We've introduced the syntax for providing parameters to validators that required them, and we also snuck in a new type option to turn our input into a password field.

<div id="textWidgetPassword"></div>
<div id="textWidgetPasswordHelp">... all this stuff...</div>
$("#textWidgetPassword").jaffaText({
    "label": "Enter a password:",
    "field": "passwordComplexityTextField",
    "type": "password",
    "help-content": "#textWidgetPasswordHelp",
    "v2Rules": {
        "mandatory": {
            "validator": "required",
            "message": "A password is required!"
        },
        "noSpecialCharacters": {
            "validator": "alphanum",
            "message": "Only alpha-numeric characters are allowed. eg: a-z, A-Z, 0-9"
        },
        "min4": {
            "validator": "min-length",
            "params": [4],
            "message": "Only passwords of betweeen 4 and 8 characters are allowed."
        },
        "max8": {
            "validator": "max-length",
            "params": [9], // <= Bug in Validatious. It uses '<' rather then '<='
            // http://cjohansen.lighthouseapp.com/projects/16714/tickets/7-max-length#ticket-7-1
            "message": "Only passwords of betweeen 4 and 8 characters are allowed."
        }
    }
});

Lookups

Text widgets support a few different types of lookups; basically anything that jQuery UI's Autocomplete can do. For the more basic types of Autocomplete, there is little more then wrapping going on, and you can provide both local and remote data for identical results. This first example shows the simple static String approach (["value1", "value2"]) for both a local data source.

There's hardly anything to this config at all, since the widget just passes the value of lookup-data straight to jQuery UI.

<div id="textWidgetLookupBasic"></div>
<div id="textWidgetLookupBasicHelp">... all this stuff...</div>
var data = ["value1", "value2"];
$("#textWidgetLookupBasic").jaffaText({
    "label": "Local: ",
    "field": "autoTextLookupStatic1",
    "help-content": "#textWidgetLookupBasicHelp",
    "lookup-data": data
});

This field pulls the autocomplete values from a URL, and has a very small increase to complexity of data structure. The suggestions will look identical to the first, but an underlying value you don't see is actually inserted into the input.

Almost identical to above, except this time we use a URL.

<div id="textWidgetLookupBasicRemote"></div>
<div id="textWidgetLookupBasicRemoteHelp">... all this stuff...</div>
$("#textWidgetLookupBasicRemote").jaffaText({
    "label": "Remote: ",
    "field": "autoTextLookupStatic2",
    "help-content": "#textWidgetLookupBasicRemoteHelp",
    "lookup-data": "data/static.json"
});

Finally, we have a pretty complicated usage of the widget, which involves searching a datasource (cross-domain or otherwise) and handling the data when it returns. For the sake of demonstration this example recreates the geonames.org search that the jQuery UI doco uses.

What the widget brings to the table here is the ability to 'walk' a JSON data structure and find any fields you have flagged as important. The fields can then be pushed into any templated Strings you like and distributed throughout your form as outputs. Try the demo and see what happens, although keep in mind that Geonames can be a tad slow at times. The original authors of Jaffa do something similar to this against a local Geonames cache for speed in production. As always, if you want more details on the particulars of this demo open the help text.

This usage represents a reasonable limit to what is offered by generic widgets. Anything more specific should probably be spun off into its own widget, or even make direct use of underlying library features. As the help text points out, this lookup has constraints you can exceed if you were to make direct use of jQuery UI.

So the top of our config looks fairly normal, but after providing a data source URL we then provide two new config elements that are reasonably complicated. Below they are explored in more detail:

lookup-request All of the config for building a request before it is sent.
  • data-type Defaults to 'json'. You would only need to change this to 'jsonp' if you are making a cross-domain request.
  • term-field The field your search term will be sent in. Defaults to 'q', although you probably should provide this as 'q' is just a guess to try before failing.
  • term-quote Flag whether or not search term(s) should be quoted on sending. Defaults to true.
lookup-parser All of the config for parsing a response after the data source returns.
  • results-path An array of Strings that are used as JSON properties to 'walk' down heirarchical data from the root. This should target an array where each element is considered an 'item' in the suggestions drop-down. There is no default value.
  • fields Each property in this object is an alias you'd like to use to create a 'field' from the data returned. The Arrays assigned to each property are a path used just as above, allowing you to address deeply nested data structures if required (the demo is only lightly nested).
  • outputs The output properties/keys serve two purposes:
    1. Each output will become a property of the 'item' that passes through the autocomplete plugin. For this reason the label and value outputs are very important (the UI uses them). The widget will try to insert them if you forget, but your data structure would have to match the jQuery UI default for this to work. The real value comes from the fact that additional properties beyond those minimums are kept in tact by the plugin and become accessible again after an option is selected... bringing us to...
    2. Once a suggestion is selected, the widget will look through each output (except the mandatories) and try to route them to somewhere meaningful. First it try to find a form field it manages that matches (the <input /> in our demo) and update the value of the field to match. Failing this it will try to treat it as a jQuery selector (anywhere in the document, not inside the widget) and the elements contents (a basic $().html() call, also used in the demo).

    Output values are basic template that will substitute in the field aliases from above wherever you put a placeholder. There is no additional logic going on under the hood when these templates are parsed, so missing values will just leave a placeholder. This would tend to make the widget mostly useful on stable data sources with fairly reliable data.

Overall this approach works, but within the tightly constrained parameters already demonstrated by the configuration. If the search pattern is particularly complicated the simple URL appending used will likely fail, or if the data returned requires massaging and more fine grained decision making during parsing then this widget will probably not help.

<div id="textWidgetLookupGeonames"></div>
<div>
    <input class="jaffa-field span5" type="text" id="textWidgetLookupGeonamesOutput" size="40" value="Don't search here, search up there ^^^" />
    <div id="textWidgetLookupGeonamesLatLong"></div>
</div>
<div id="textWidgetLookupGeonamesHelp">... all this stuff...</div>
$("#textWidgetLookupGeonames").jaffaText({
    "label": "City Name: ",
    "field": "autoTextLookupGeonames",
    "help-content": "#textWidgetLookupGeonamesHelp",
    "lookup-data": "http://ws.geonames.org/searchJSON?featureClass=P&style=full&maxRows=12",
    "lookup-request": {
        "data-type": "jsonp",
        "term-field": "name_startsWith",
        "term-quote": true
    },
    "lookup-parser": {
        "results-path": ["geonames"],
        "fields": {
            "name": ["toponymName"],
            "country": ["countryName"],
            "lat": ["lat"],
            "long": ["lng"]
        },
        "outputs": {
            "label": "${name}, ${country}",
            "value": "${name}, ${country}",
            "textWidgetLookupGeonamesOutput": "${name}, ${country}",
            "#textWidgetLookupGeonamesLatLong": "Lat: ${lat}, Long: ${long}"
        }
    }
});

Selects / Drop Downs

Basic

The most basic of drop downs using static data. This particular widget also happens to back into server managed data that Jaffa grabbed at startup, so you should see that the second value is selected now despite it not being the default or first in the list. All Jaffa fields (and by extension widgets) are synch'd to server data during Jaffa's startup.

We have provided config with what the <option> elements should contain (both value and label).

<div id="formSelectWidget"></div>
<div id="formSelectWidgetHelp">... all this stuff...</div>
$("#formSelectWidget").jaffaDropDown({
    "label": "Drop Down / Select:",
    "field": "formSelect",
    "option-data": [
        {"value": "value3",  "label": "Value 3 (This label is not stored)"},
        {"value": "value4",  "label": "Value 4 (This label is not stored)"}
    ],
    "help-content": "#formSelectWidgetHelp"
});

In counterpoint to the above example, this drop down has not loaded any server data, but we've explicitly defined a default whilst still allowing the user to 'opt-out'.

Vocabularies

A drop-down is a fairly typical control for representing controlled vocabularies. You may load the vocabulary statically as we demonstrated on the previous tab, or you might instruct the widget to go and get the data from a vocabulary service or an AJAX interface lightly wrapping an application's database.

This drop down references a static file showing the colours of the rainbow. We still allow an empty value in the GUI, since we don't want the user to ignore the field and accept a default selection that will accidentally treated as valid. To make them select something we'll make it mandatory as well, so the form submission will not accept it. For the demo you can test with a blur() event however.

So if you were to have a look at the static file we linked above, you'll see there's some additional data embedded in there that we could make use of. This is very similar to the more complicated example we shown in the text widget above.

Related Data

Radio Groups

Basic

$("#formRadioWidget").jaffaRadioGroup({
    "label": "Radio Group: ",
    "field": "formRadio",
    "label-field": "formRadioLabel",
    "radio-data": {
        "formRadio1": "Value 1",
        "formRadio2": "Value 2",
        "formRadio3": "Value 3"
    }
});
Click here for help:

Now we'll go a step further and give Jaffa a jQuery selector for where to put our help GUI elements, moving them above the widget.

TODO

Validation

TODO

Lookups

TODO

Checkboxes

Basic

TODO

Validation

TODO

Lookups

TODO

Calendar Widgets

Basic

TODO

Validation

TODO

Lookups

TODO

Repeatable Fields

Basic

Most widgets also offer a 'repeatable' version as well, adding in GUI elements required to expand/contract and manage the list. At face value, the core Jaffa code has no knowledge of this, but convention amongst widgets is to map this into fields of the form, so Jaffa has been made be aware of this convention in sorting field names.

  • baseField.1.subfieldA
  • baseField.1.subfieldB
  • baseField.2.subfieldA
  • baseField.2.subfieldB
  • etc...
TODO

A text field with validation attached, requiring at least one URL be entered, but allowing others

.

<div id="textWidgetUrlRepeatable"></div>
<div id="textWidgetUrlRepeatableHelp">... all this stuff...</div>

                

This help text has been applied to the top-level list widget, with some basic textual help inside each 'child'.

TODO

Validation

TODO

Lookups

TODO

Designing Your Own