1.4.8 • Published 5 years ago

pure-form v1.4.8

Weekly downloads
3
License
MIT
Repository
github
Last release
5 years ago

pure-form

Shippable branch Linked In Twitter Follow

<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:

Cucumber HTML report

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):

AttributeDescription
actionSpecifies where to send the form-data when a form is submitted
enctypeThe standard form enctype attribute specifies how the form-data should be encoded
methodSpecifies how to send form-data, options are get (send as URL variables or post (as HTTP post transaction)
srcPath to the JSON Schema that defines the form
readonlyIf true, adds read only property to all form fields
data-titleThe title to be rendered before the form
descriptionThe description before the form
buttonsComma separated list of button labels (excludes buttons created by schema links)
show-schema-buttonsIf true, renders a button for each of the schema links
persistPartially completed forms survive refreshes (until browser is closed)
storagelocation to store presistant content, defaults to sessionStorage
disable-validationDisables all form validation
placeholder-max-lengthMax description length to be used as placeholder, values greater than are inlined
autofocus-errorIf set to true, sets focus to the first input containing an error after validation
validate-on-blurIf set to true, validates each form field as it looses focus (disabled .autofocusError)
tab-on-enterIf true, moves focus to the next field in the form when enter is pressed
submit-on-enterIf true, clicks the first button in the form when the enter key is pressed
use-form-tagIf false, uses a DIV rather than a HTML FORM tag (helps overcome iOS GO button issue)
enforce-max-lengthIf true, does not allow the user to enter more characters that the value of schema maxLength property
schema-idthe id of the current schema (readonly)
autofocus-idid of the field to set autofocus
auth-tokenHTTP Authorization Bearer token
http-headersTODO: document this!
number-range-selectorIf true, renders a HTML select for number ranges (default true)

Properties

Assuming contactForm = document.getElementById('contactForm'), the following properties can be set via JavaScript:

PropertyTypeDefaultDescription
actionstring""Specifies where to send the form-data when a form is submitted (if empty posts to current page)
enctypestringapplication/x-www-form-urlencodedThe standard form enctype attribute specifies how the form-data should be encoded
methodstringgetSpecifies how to send form-data, options are get (send as URL variables or post (as HTTP post transaction)
srcstring""Path to the JSON Schema that defines the form
schemaobjectnullSchema object to use
valueobject{}Object containing key/value pair of values
readonlybooleanfalseIf true, adds readonly property to all form fields
titlestring""The title to be rendered before the form
descriptionstring""The description before the form
buttonsstring""Comma separated list of button labels (excludes buttons created by schema links)
showSchemaButtonsstringtrueIf true, renders a button for each of the schema links
persistbooleanfalsePartially completed forms survive refreshes (until browser is closed)
storagestringsessionStorageLocation to store partially completed content
disableValidationbooleanfalseDisables all form validation
placeholderMaxLengthboolean75Max description length to be used as placeholder, values greater than are inlined
autofocusErrorbooleanfalseIf set to true, sets focus to the first input containing an error after validation
validateOnBlurbooleanfalseIf set to true, validates each form field as it looses focus (disabled .autofocusError)
tabOnEnterbooleanfalseIf true, moves focus to the next field in the form when enter is pressed
submitOnEnterbooleanfalseIf true, clicks the first button in the form as would a normal HTML form
useFormTagbooleantrueIf false, uses a DIV rather than a HTML FORM tag (helps overcome iOS GO button issue)
enforceMaxLengthbooleanfalseIf true, does not allow the user to enter more characters that the value of schema maxLength property
schemaIdstringemptyid valid of the currently rendered schema (readonly)
autoResizebooleanfalseif true, expands the height of text and html inputs to display content - default false
isDirtybooleanfalsetrue if the form value was changed by the user
autofocusIdstring | id of the field to set autofocus
linksarrayemptyget/set array of HATEOAS that are rendered as buttons
authTokenstringemptyHTTP 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:

MethodDescription
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):

EventDescription
pure-form-submitFired when the form is submitted to the server
pure-form-schema-loadingFires when a form schema is loading
pure-form-schema-loadedFired when .src url is successfully loaded
pure-form-schema-errorFired when .src url fails to return a JSON Schema
pure-form-render-completeFired when pure-form completes rendering
pure-form-button-clickedFired when a pure-form button is clicked
pure-form-value-setFired when the form value is set
pure-form-value-set-completeFires when the value-set has finished updating the form
pure-form-validation-failedValidation failed, some field items are invalid
pure-form-validation-passedValidation passed, all field values are valid
pure-form-submit-completeData posted to server and response received
pure-form-value-changedChange 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:

FilenameDescription
pure-form-event-spec.jsTest pure-form events
pure-form-interface-spec.jsTests that check the correct properties and methods are exposed
pure-form-method-spec.jsTests that execute methods and verify their functionality
pure-form-rendering-spec.jsTests 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

1.4.8

5 years ago

1.4.7

5 years ago

1.4.6

6 years ago

1.4.5

6 years ago

1.4.4

6 years ago

1.4.3

6 years ago

1.4.2

6 years ago

1.4.1

6 years ago

1.4.0

6 years ago

1.3.9

6 years ago

1.3.8

6 years ago

1.3.7

6 years ago

1.3.6

6 years ago

1.3.5

6 years ago

1.3.4

6 years ago

1.3.3

6 years ago

1.3.2

6 years ago

1.3.1

6 years ago

1.3.0

6 years ago

1.2.8

6 years ago

1.2.7

6 years ago

1.2.6

6 years ago

1.2.5

6 years ago

1.2.4

6 years ago

1.2.3

6 years ago

1.2.2

6 years ago

1.2.1

6 years ago

1.2.0

6 years ago