pure-form v1.4.8
pure-form
<pure-form>
is a 7k, pure JavaScript, drop-in replacement for the HTML <form>
that allows forms to be created with validation, UX and styling using a JSON Schema.
Example:
<pure-form src="space-camp-application.json"></pure-form>
Produces:
Live Demo. Works in all modern web/mobile browsers from IE9+ - including Cordova/PhoneGap and iconic projects.
Features
- Creates a schematic HTML FORM from a JSON Schema
- Automatically handles client side validation
- Provides visual validation on blur of form fields (optional)
- Get/set the value of the entire form using JSON
- Displays character limits (including characters remaining) when maxLength set
- Preserves partially completed form values between refreshes (optional)
- Renders a button for each REST endpoint included in the schema links collection
- Automatically executes REST request when button clicked
Table of Contents
Install
Add the dist/pure-form.min.js and dist/pure-form.min.css files to your project:
Usage
HTML
<html>
<head>
<!-- add document.registerElement polyfill if IE/Safari support required -->
<script src="document-register-element.js"></script>
<!-- add pure-form script & css -->
<link href="pure-form.min.css" rel="stylesheet" />
<script src="pure-form.min.js"></script>
</head>
<body>
<!-- create just like a regular HTML tag (can also be created using document.createElement) -->
<pure-form id="contactForm" src="contact-form.json" validate-on-blur="true"></pure-form>
</body>
</html>
JSON Schema
Pure form uses JSON Schema (v3) to define the forms input requirements and validation. The space-camp-application.json
referenced above:
{
"type": "object",
"id": "space-camp-application",
"$schema": "http://json-schema.org/draft-03/schema#",
"title": "Space Camp Scholarship Application",
"description": "Please provide as much detail as possible",
"links": [
{ "rel": "self", "title": "Apply", "href": "http://localhost:8080/223423423342", "method": "PUT" }
],
"additionalProperties": false,
"properties": {
"title": {
"title": "Title",
"id": "order1",
"type": "string",
"required": true,
"enum": [
"Dr",
"Mr",
"Mrs",
"Ms"
]
},
"firstName": {
"title": "First name",
"id": "order2",
"type": "string",
"required": true,
"description": "John"
},
"surname": {
"title": "Surname",
"id": "order3",
"type": "string",
"required": true,
"description": "Appleseed"
},
"email": {
"title": "Email",
"id": "order4",
"type": "string",
"format": "email",
"required": true,
"description": "example@domain.com"
},
"phone": {
"title": "Telephone",
"id": "order5",
"type": "string"
},
"message": {
"title": "Experience",
"id": "order6",
"type": "string",
"format": "textarea",
"required": true,
"maxLength": 100
}
}
}
A button is rendered for each link item, the title property is used as the button label. If a link contains a rel that starts with describedby:
, it is assumed to be a link to another schema, clicking that button therefore simply loads that schema.
The markup created within the pure-form
tag is just regular HTML and can be styled accordingly.
Attributes
The following attributes can be set in HTML or via .setAttribute(propertyName, value)
:
Attribute | Description |
---|---|
action | Specifies where to send the form-data when a form is submitted |
enctype | The standard form enctype attribute specifies how the form-data should be encoded |
method | Specifies how to send form-data, options are get (send as URL variables or post (as HTTP post transaction) |
src | Path to the JSON Schema that defines the form |
readonly | If true, adds read only property to all form fields |
data-title | The title to be rendered before the form |
description | The description before the form |
buttons | Comma separated list of button labels (excludes buttons created by schema links) |
show-schema-buttons | If true, renders a button for each of the schema links |
persist | Partially completed forms survive refreshes (until browser is closed) |
storage | location to store presistant content, defaults to sessionStorage |
disable-validation | Disables all form validation |
placeholder-max-length | Max description length to be used as placeholder, values greater than are inlined |
autofocus-error | If set to true, sets focus to the first input containing an error after validation |
validate-on-blur | If set to true, validates each form field as it looses focus (disabled .autofocusError) |
tab-on-enter | If true, moves focus to the next field in the form when enter is pressed |
submit-on-enter | If true, clicks the first button in the form when the enter key is pressed |
use-form-tag | If false, uses a DIV rather than a HTML FORM tag (helps overcome iOS GO button issue) |
enforce-max-length | If true, does not allow the user to enter more characters that the value of schema maxLength property |
schema-id | the id of the current schema (readonly) |
autofocus-id | id of the field to set autofocus |
auth-token | HTTP Authorization Bearer token |
http-headers | TODO: document this! |
number-range-selector | If true, renders a HTML select for number ranges (default true) |
Properties
Assuming contactForm = document.getElementById('contactForm')
, the following properties can be set via JavaScript:
Property | Type | Default | Description |
---|---|---|---|
action | string | "" | Specifies where to send the form-data when a form is submitted (if empty posts to current page) |
enctype | string | application/x-www-form-urlencoded | The standard form enctype attribute specifies how the form-data should be encoded |
method | string | get | Specifies how to send form-data, options are get (send as URL variables or post (as HTTP post transaction) |
src | string | "" | Path to the JSON Schema that defines the form |
schema | object | null | Schema object to use |
value | object | {} | Object containing key/value pair of values |
readonly | boolean | false | If true, adds readonly property to all form fields |
title | string | "" | The title to be rendered before the form |
description | string | "" | The description before the form |
buttons | string | "" | Comma separated list of button labels (excludes buttons created by schema links) |
showSchemaButtons | string | true | If true, renders a button for each of the schema links |
persist | boolean | false | Partially completed forms survive refreshes (until browser is closed) |
storage | string | sessionStorage | Location to store partially completed content |
disableValidation | boolean | false | Disables all form validation |
placeholderMaxLength | boolean | 75 | Max description length to be used as placeholder, values greater than are inlined |
autofocusError | boolean | false | If set to true, sets focus to the first input containing an error after validation |
validateOnBlur | boolean | false | If set to true, validates each form field as it looses focus (disabled .autofocusError) |
tabOnEnter | boolean | false | If true, moves focus to the next field in the form when enter is pressed |
submitOnEnter | boolean | false | If true, clicks the first button in the form as would a normal HTML form |
useFormTag | boolean | true | If false, uses a DIV rather than a HTML FORM tag (helps overcome iOS GO button issue) |
enforceMaxLength | boolean | false | If true, does not allow the user to enter more characters that the value of schema maxLength property |
schemaId | string | empty | id valid of the currently rendered schema (readonly) |
autoResize | boolean | false | if true, expands the height of text and html inputs to display content - default false |
isDirty | boolean | false | true if the form value was changed by the user |
autofocusId | string | | id of the field to set autofocus | |
links | array | empty | get/set array of HATEOAS that are rendered as buttons |
authToken | string | empty | HTTP Authorization Bearer token |
action
// change the form post destination url
contactForm.action = 'http://www.example.com/example';
enctype
// change the format data is sent to the server
contactForm.enctype = 'multipart/form-data';
method
// post the form data to the server
contactForm.method = 'post';
src
// setting the src triggers loading of the JSON Schema:
contactForm.src = 'http://domain.com/schema-name.json';
schema
// get the schema retrieved from the server
var jsonSchema = contactForm.schema;
value
// get the value of the form as a JSON object
var formValue = contactForm.value;
// set the value of the form using a JSON object with keys matching fields
contactForm.value = {
title: "Mr",
firstName: "John",
surname: "Doherty"
};
readonly
// set all form fields to readonly
contactForm.readonly = true;
title
// set the title rendered at the top of the form
contactForm.title = 'Contact Us';
description
// set the description rendered at the top of the form
contactForm.description = 'Got a question? Get in touch.';
buttons
// Add Ok and Cancel buttons
contactForm.buttons = 'Ok, Cancel';
contactForm.addEventListener('pure-form-button-clicked', function(e) {
if (e.detail === 'Ok') {
// Ok clicked, do something
}
});
showSchemaButtons
// will cause a re-render and display a button for each schema .link
contactForm.showSchemaButtons = true;
persist
// preserve form data between page reloads
contactForm.persist = true;
storage
// store partially complete content in localStorage
contactForm.storage = 'localStorage';
disableValidation
// disable form validation
contactForm.disableValidation = true;
placeholderMaxLength
// the maximum number of characters to use a input placeholder before
// rendering the item description under the field
contactForm.placeholderMaxLength = 25;
autofocusError
// automatically set focus to the first error found within a form
contactForm.autofocusError = true;
validateOnBlur
// automatically validate each field as it looses focus
contactForm.validateOnBlur = true;
tabOnEnter
// move to the next field when the user presses the enter key
contactForm.tabOnEnter = true;
submitOnEnter
// click the first button in the form when enter pressed similar to regular HTML form
contactForm.submitOnEnter = true;
useFormTag
// do not use a <form> tag to render form elements (helps fix iOS/Safari GO button issue)
contactForm.useFormTag = true;
enforceMaxLength
// stop the user from entering more character than the allowed amount (default is to inform user of overuse)
contactForm.enforceMaxLength = true;
schemaId
// get the currently rendered schema id
var schemaId = contactForm.schemaId;
autoResize
// allow textareas and contenteditable iframe (used for format=html) to auto expand height based on content
var schemaId = contactForm.autoResize = true;
isDirty
// use the .isDirty property to determine if the user have changed the values since the form was loaded or .value was set
var hasFormBeenUpdated = contactForm.isDirty;
autofocusId
// auto focus first name ready for input
contactForm.autofocusId = 'firstName';
links
// add a hateoas link object - renders a button that when clicked, sends form data to API link
contactForm.links = [
// title is used as button text!
{ title: 'Save', rel: 'save', href: 'http://localhost:8080/api' }
]
authToken
// set authToken to be used as HTTP Authorization Bearer value in subsequent requests
contactForm.authToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9';
Methods
Each pure-form element exposes the following methods:
Method | Description |
---|---|
submit(optionalRel) | Submits the form to the server |
clearError(fieldName) | Clears error message on a form field |
clearErrors() | Clear all form error messages |
setError(fieldName, errorMessage) | Sets a custom error message on a form field |
isValid(object, silent, ignoreRequired) | Checks if the entire form is valid and updates UI |
reset() | Clears all form values and errors |
checkValid() | Checks if a value is valid against current schema |
submit
// submit the form to the server just like a regular HTML form
document.getElementById('contactForm').submit();
// submit the form to the server using one of the schema link objects
document.getElementById('contactForm').submit('self');
clearError(fieldName)
// remove error message from email field
document.getElementById('contactForm').clearError('email');
clearErrors()
// remove all error messages
document.getElementById('contactForm').clearErrors();
setError(fieldName, errorMessage)
// set a custom error message on the phone field
document.getElementById('contactForm').setError('phone', 'Phone number is missing country code');
isValid(object, silent, ignoreRequired)
var contactForm = document.getElementById('contactForm');
if (contactForm.isValid()) {
// form is valid, do some stuff
}
var contactForm = document.getElementById('contactForm');
// check if a value is valid against the loaded schema (pass true as second param to avoid updating the UI)
var isValidPhone = contactForm.isValid({ phone: '+441223 223 223' }, true);
if (isValidPhone) {
// the phone number is valid according to form schema, do something
}
reset()
// Clear all form values and errors
document.getElementById('contactForm').reset();
checkValid()
// checks if a value is valid against the current schema
document.getElementById('contactForm').checkValid(propName, value);
Events
pure-form fires the following events (additional information is stored event.detail property):
Event | Description |
---|---|
pure-form-submit | Fired when the form is submitted to the server |
pure-form-schema-loading | Fires when a form schema is loading |
pure-form-schema-loaded | Fired when .src url is successfully loaded |
pure-form-schema-error | Fired when .src url fails to return a JSON Schema |
pure-form-render-complete | Fired when pure-form completes rendering |
pure-form-button-clicked | Fired when a pure-form button is clicked |
pure-form-value-set | Fired when the form value is set |
pure-form-value-set-complete | Fires when the value-set has finished updating the form |
pure-form-validation-failed | Validation failed, some field items are invalid |
pure-form-validation-passed | Validation passed, all field values are valid |
pure-form-submit-complete | Data posted to server and response received |
pure-form-value-changed | Change event fires whenever any item in the form changes |
pure-form-submit
var contactForm = document.getElementById('contactForm');
contactForm.addEventListener('pure-form-submit', function(e) {
console.log(this); // pure form element
console.log(e.target); // pure form element
console.log(e.detail.url); // schema URL
console.log(e.detail.status); // http status 200
console.log(e.detail.body); // JSON schema as a string
});
// trigger form submit
contactForm.submit();
pure-form-schema-loading
Fires when a JSON schema has started loading.
var contactForm = document.getElementById('contactForm');
contactForm.addEventListener('pure-form-schema-loading', function(e) {
console.log(this); // pure form element
console.log(e.target); // pure form element
console.log(e.detail); // schema URL
});
pure-form-schema-loaded
Fires when the JSON schema has loaded from .src
url and assigned to .schema
property.
var contactForm = document.getElementById('contactForm');
contactForm.addEventListener('pure-form-schema-loaded', function(e) {
console.log(this); // pure form element
console.log(e.target); // pure form element
console.log(e.detail.url); // schema URL
console.log(e.detail.status); // http status 200
console.log(e.detail.body); // JSON schema as a string
});
pure-form-schema-error
Fires when JSON schema fails to load for whatever reason.
var contactForm = document.getElementById('contactForm');
contactForm.addEventListener('pure-form-schema-error', function(e) {
console.log(this); // pure form element
console.log(e.target); // pure form element
console.log(e.detail.url); // schema URL
console.log(e.detail.status); // http status 404 (500 etc)
console.log(e.detail.body); // empty string
});
pure-form-render-complete
Fires when pure-form rendering has completed.
var contactForm = document.getElementById('contactForm');
contactForm.addEventListener('pure-form-render-complete', function(e) {
console.log(this); // pure form element
console.log(e.target); // pure form element
});
pure-form-button-clicked
<pure-form id="contact" src="path-to-json-schema.json" buttons="Delete,Disable"></pure-form>
var contactForm = document.getElementById('contactForm');
contactForm.addEventListener('pure-form-button-clicked', function(e) {
console.log(e.target); // pure form element
console.log(e.detail.value); // value of button clicked (Delete or Disable)
console.log(e.detail.link); // associated schema link object if present
});
pure-form-value-set
var contactForm = document.getElementById('contactForm');
contactForm.addEventListener('pure-form-value-set', function(e) {
console.log(e.target); // pure form element
console.log(e.detail.oldValue); // OLD value of the form before the new value was set
console.log(e.detail.newValue); // NEW value of the form
// call e.preventDefault() to cancel value set
});
pure-form-value-set-complete
var contactForm = document.getElementById('contactForm');
contactForm.addEventListener('pure-form-value-set-complete', function(e) {
console.log(e.target); // pure form element
console.log(e.detail.oldValue); // OLD value of the form before the new value was set
console.log(e.detail.newValue); // NEW value of the form
});
pure-form-validation-failed
Fires when pure-form .isValid method returns false.
var contactForm = document.getElementById('contactForm');
contactForm.addEventListener('pure-form-validation-failed', function(e) {
console.log(e.target); // pure form element
console.log(e.target.value) // form value
});
pure-form-validation-passed
Fires when pure-form .isValid method returns true.
var contactForm = document.getElementById('contactForm');
contactForm.addEventListener('pure-form-validation-passed', function(e) {
console.log(e.target); // pure form element
console.log(e.target.value) // form value
});
pure-form-submit-complete
// data submitted and response received
document.addEventListener('pure-form-submit-complete', function(e) {
console.log(e.type); // event name
console.log(e.target); // actual HTML element clicked
console.log(e.detail.url); // requested URL
console.log(e.detail.status); // HTTP status code
console.log(e.detail.body); // HTTP response body
});
pure-form-submit-error
// the server rejected the submission for some reason
document.addEventListener('pure-form-submit-error', function(e) {
console.log(e.type); // event name
console.log(e.target); // actual HTML element clicked
console.log(e.detail.url); // requested URL
console.log(e.detail.status); // HTTP status code
console.log(e.detail.body); // HTTP response body
});
pure-form-value-changed
// the value of a field change within the form
document.addEventListener('pure-form-value-changed', function(e) {
console.log('--------------------');
console.log(e.type); // event name
console.log(e.detail); // detail.srcElement contains element that triggered the change
console.log(this); // pure form element
console.log(e.target); // pure form element
console.log(e.target.value) // form value
console.log('--------------------');
});
Data Types
TODO: Add JSON Schema data type/format to html type documentation
Contribute
The Vision
My objective is to create a single script, drop in replacement for the HTML FORM tag that separates a forms requirements from the UI, UX, validation logic. The end result should be a more intuitive, user-friendly & consistent web form with a collection of predefined schemas to handle things such as user registration, online payment etc.
It's a big goal and one that I will be advancing daily, it would be great if you could join me!
Your contribution could be as simple as suggesting a feature, reporting a bug, adding a little more documentation or by forking the project and coding a new feature/fix/unit test.
Local Development
The project includes everything needed to continue development, including a node webserver. If you'd like to help out, run the following to get started:
git clone https://github.com/john-doherty/pure-form
cd pure-form
npm install
npm start
Then visit http://localhost:8080 in your browser.
Testing
The project includes Unit Test. To run tests, execute the following from within the pure-form folder:
npm install
npm test
Tests are written using jsdom, nock and jasmine-node and are broken into 4 files:
Filename | Description |
---|---|
pure-form-event-spec.js | Test pure-form events |
pure-form-interface-spec.js | Tests that check the correct properties and methods are exposed |
pure-form-method-spec.js | Tests that execute methods and verify their functionality |
pure-form-rendering-spec.js | Tests that tweak properties and verify the rendered output |
Generate .min Files
The minified pure-form.min.js and pure-form.min.css files included in this project are the latest version. To generate .min files, execute the following:
npm run build
The version number is picked up from the package.json
file.
Reporting Bugs
If you find a bug, please create an issue and provide as much detail as possible - include a jsfiddle if possible.
Pull Requests
Feel free to submit a pull requests, but please ensure your work is covered with unit tests and your code follows the current ESLint coding style defined in the package.json file.
License
Licensed under MIT License © John Doherty
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago