Apprentice Blog of the Week: FormsJs

Apprentice Blog of the Week: FormsJs

Mike Danaher
Mike Danaher

July 21, 2014

Working with web forms can be tedious and time-consuming. Many of the tools that exist today require individual setup and handling for each form element. Furthermore, the current set of tools can be rigid and limiting in their functionality.

FormsJs hopes to solve a few of these challenges. A lightweight JavaScript library, FormsJs allows you to work on an entire form at once. Using an array of JavaScript objects, you can populate a form with default values, check all validations, gather a list of error messages, and serialize all the data to be passed to the server. These configuration objects also make it easy to change functionality as your form evolves over time.

FormsJs was built with three main classes: Populator, Validator, and Serializer. Each class is instantiated with a different configuration, giving you the flexibility to use the appropriate tool for the job.

Populator

The job of the populator is, naturally, to populate forms. You give it a list of all the elements you want populated, with the values you want, and it does the rest. Here's a sample configuration array:


var populatorConfig = [
		{
				type: "text",
				elementSelector: "[data-id='email']",
				value: "me@example.com"
		},
		{
				type: "checkbox",
				elementSelector: "[data-id='opt-in']",
				value: ['Sign me up for the newsletter']
		}
]

With this configuration, we are telling the form to autofill the email address and check the box to sign them up for newsletters. The type parameter specifies the type of element to target. Currently, FormsJs supports 'text,' 'radio,' 'checkbox,' and 'select' as strings.

The elementSelector parameter specifies the specific element to target using a jQuery selector (i.e. '[name=email],' '#email,' etc.).

The value parameter is the value to be populated. Text boxes, radio buttons, and select lists all take strings. Checkboxes take an array of strings to account for the possibility of groups of checkboxes with the same selector.

To use this configuration on your form, create a new Populator with the populatorConfig:

var populator = new FormsJs.Populator(populatorConfig)

Then, when you're ready to populate the form, you just call the populate method on your populator.

populator.populate()

Validator

Once the user has completed the form, you'll want to validate the information before submitting. The Validator class handles this for all of the form's elements. Using a configuration array similar to the populator, the validator also looks for a type and an elementSelector. Additionally, it also looks for an array of validations giving each element the ability to match several different validations (i.e. 'required' and 'minLength'). Here's a sample validator configuration:

var validatorConfig = [
		{
				type: "text",
				elementSelector: "[data-id='email']",
				validations: [
						{
								type: "required",
								errorMessage: "Email is required"
						},
						{
								type: "email",
								errorMessage: "Please enter a valid email address"
						}
				]
		},
		{
				type: "text",
				elementSelector: "[name=phone]",
				validations: [
						{
								type: "regExp",
								pattern: /(?:\d{3}|\(\d{3}\))([-\/\.])\d{3}\1\d{4}/,
								errorMessage: "Please enter a valid phone number as ###-###-####"
						}
				]
		}
]

As you can see, within the validations parameter, a 'type' and an 'errorMessage' are specified. Certain validations also have an additional parameter depending on their type (i.e. 'minLength' and 'maxLength' both also require a 'length'). Currently, FormsJs supports the following validation types: 'required,' 'email,' 'minLength,' 'maxLength,' 'regExp,' and 'matchingInput'.

These types should cover the majority of form validation needs. However, we also included a 'customMatcher' should you need any specific validating. This validator requires a third parameter—a callback function that takes a value to be matched against and returns true or false.


validations: [
	{
		type: "customMatcher",
		errorMessage: "Phone type is required when a phone number is entered",
		matcher: function(value) {
			otherField = $("[name=phone]").val();
			if (otherField === "") {
				return true;
			} else if (value !== "") {
				return true;
			} else {
				return false;
			}
		}
]

To use the validatorConfig in your form, create a new validator with your configuration:

var validator = new FormsJs.Validator(validatorConfig)

The Validator Class has two methods: isValid() and errors().

Calling validator.isValid() will return true or false. Calling validator.errors() will return an object with the elementSelector and an array of its error messages. If our email address was left blank, this would return:

{ "[data-id='email']" : [ "Email is required", "Please enter a valid email address" ] }

As you can see, FormsJs gives you complete control over how to validate a form and display the errors.

Serializer

After you have a complete and valid form, you need to serialize the data to be passed to the server. As with the other classes, FormsJs gives you complete control over how to do this. Start by creating a serializer configuration:


var serializerConfig = [
		{
				type: "text",
				elementSelector: "[name=firstName]",
				dataKey: "first_name"
		},
		{
				type: "text",
				elementSelector: "[name=lastName]",
				dataKey: "last_name"
		},
		{
				type: "text",
				elementSelector: "[name=email]",
				dataKey: "email"
		}
]

In addition to the usual 'type' and 'elementSelector' parameters, the serializer has an optional 'dataKey' parameter. This allows you to specify the exact way the field should be sent to your database. If a 'dataKey' is not specified, FormsJs will use the HTML name attribute from your element.

As with the previous classes, create a new Serializer with your configuration:

var serializer = new FormsJs.Serializer(serializerConfig)

Calling serializer.serialize() will return a JSON object, with each key being the specified dataKey (or element name) and the current value of the form:

{ "first_name" : "Mike", "last_name" : "Danaher", "email" : "miked@example.com" }

Scope

When we first used FormsJs for one of our projects, we noticed an interesting problem. We had a page with multiples of the same form (think add additional records). Because of this, our elementSelector was duplicated on our page and we couldn't properly select the form we wanted to work with. To fix this problem, we added a scope to our constructors. The populator, validator, and serializer now all take an optional scope parameter on initialization. This scope paramater is a jQuery selector as a string. Here's an example:

var scope = "#form1"
var populator = new FormsJs.Populator(populatorConfig, scope)

Hopefully you can see the value that FormsJs brings to web forms. For more information and complete sample config files, check out the README on the project’s home page.