1.2.2 • Published 4 years ago

form-validate.js v1.2.2

Weekly downloads
89
License
MIT
Repository
github
Last release
4 years ago

form-validate.js

form-validate.js is a form validation library, for validating web client forms.

Introduction

form-validate.js was created to give an effective yet convenient way of validating forms (in react components and html). The library is flexible and gives you control of its effects including what errors are shown, it's styling, and flow. Scroll to the bottom of this page to see a sample react component with form validation

Requirements

  • Node and npm
  • Transpilation (conventionally with babel). create-react-app has this setup already.

Installation

npm install form-validate.js

Dependency

form-validate.js relies on validate.js for its validation rules and is shipped together with the library.

Usage

The examples in this doc are targeted for react however, the principles can be applied to forms using other tools including vanilla JS

Validating forms

  • Import form-validate.js to your js code
import FormValidate from 'form-validate.js';
  • Create a constant for an instance of FormValidate class
const validator = new FormValidate(<constraints>, <options>, <defaultValues>);

constraints is an object holding validation rules.

Note: all rules are based on validate.js rules, access the docs here to know what rules are usable and how to customize validation error messages. However, there are two rules custom and customAsync peculiar to form-validate.js

const constraint = {
  username: {
    presence: true,
  },
  password: {
    presence: true,
    length: {
      minimum: 8,
    },
  },
};

options (optional) is an object which indicates how the validate.js library handles validation errors and messages. Allowed options include fullMessages, prettify and format as seen here

defaultValues (optional) an object which indicates the values to be validated against initially, if not provided, all field values would be treated as null.

const defaultValues = {
  username: 'john',
  password: 'password',
};

custom and customAsync constriants

with the custom rule, you can have validation based on conditions you provide, simply return a string or array of messages of the error if validation fails or null if validation passes

customAsync rule makes you perform validation asynchronously in case you need to call endpoint to validate a field. To do this, return a function in which you resolve a string or array of messages if validation fails or simple call resolve() if there are no errors. You can still return plain values, in which case customAsync handles the validation as synchronous Sample custom and customAsync rules are shown below

const constraint = {
  username: {
    customAsync: (value, attributes, attributeName) => {
      if (value && value.trim() === '') return;
      return function(resolve) {
        setTimeout(() => {
          if (['joshua', 'john', 'rita'].includes(value)) {
            resolve('%{value} is taken');
          } else {
            resolve();
          }
        }, 1000);
      };
    },
  },
  unique: {
    custom: (value, attributes, attributeName) => {
      if (attributes.username === attributes.password) {
        return '^the username and password cannot be thesame';
      }
      return null;
    },
  },
};

custom constraint can be used on a control not associated with any input, provided it is the only constrain specified on the control

customAsync should return a function taking resolve as an argument, resolve should be called to indicate validation is done passing in the validation errors or without any argument if the validation passes.

  • Using the validator

Ensure the name of the input field corresponds to the object key in the validation constraints otherwise, the validator would not be associated with an input field unless it meets the condition to act as a stand-alone custom validator as stated above

<input type="text" name="username" />

If for any reason the name given to the input does not match the validation constraint key, use the validate-control or data-validate-control attribute on the input element to specify the constrian key the input is associated with.

<input type="text" name="alt-input-name" validate-control="username" />

the validation instance has a controls property validator.controls that holds the control object of each field. The control object has three important fields

  • errors - an array holding all validation errors of the field
  • touched - indicating if the control field has been interacted with
  • loading - indicating if an asynchronous validation is processing

getting a reference to a control associated with a field can be done thus

const usernameControl = validator.controls.username;

OR

const usernameControl = validator.get('username');

The errors, touched, loading and other properties and methods of the control can be access from the control object directly

const usernameErrors = validator.controls.username.errors;

const usernameTouched = validator.controls.username.touched;

The errors can be displayed in a react app as follows

<div>{usernameTouched && usernameErrors.map((error, i) => <div key={i}>{error}</div>)}</div>

the touched check should be done, otherwise errors would show up without the user interacting with the form.

  • Validating a form To validate input values in a form, add an onChange listener to the form and call the validate method in its callback passing the event and a callback function to be executed once validation is done.

    Note: Other listeners e.g. onBlur can be used to perform validation at the occurence of corresponding events.

onChange = event => {
  // Note: in a react app, the event should be the native event which can be gotten with event.nativeEvent
  // callback to be run once validation is done, the valid argument indicates if the form is valid or not, and controls is a collection of all form controls
  validator.validate(event.nativeEvent, (valid, controls) => {
	  // perform logic after validation
  });
};

A check can also be added on submit of the form, in case the user tends to bypass onchange validation. Conventionally, all errors should show up after submitting the form, this can be done by calling the validator.touchAll() function in the onsubmit handler which bypassing the touched check

onSubmit  = (event) => {
  event.preventDefault();
  if (!validator.valid()) {
    validator.touchAll();
    return;
  }
  // ...
}

Custom and Variable controls

You may need to include custom contraints later on in your code, luckily, form-validator.js provides a means of accomplishing this, you can always add controls and contraints using the validator.addControl function and remove existing ones with validator.removeControl at appropriate places in your code

validator.addControl takes in 3 arguments, controlName, rule and defaultValue validator.removeControl takes in only the controlName as an argument

validator.addControl('my-custom-control', {presence: true}, 'default-value');
...
validator.removeControl('existing-control');

Rendering Validation Errors

To display validation errors on your view, call the validator.render(callback) function. The callback is a function which takes valid and controls as arguments and its body should be the logic to render errors to the screen, Call to validator.render can be done immediately after creating the validator instance.

// the valid argument indicates if the form is valid or not, and controls is a collection of all form controls
validator.render((valid, controls) => {
  // perform logic to display errors to the users here.
  // in a react app this can be as easy as setting the state to trigger a rerender.
  // for a regular html form, certain elements can be updated to contain the validation error in controls
  this.setState({});
});

NOTE: If you used earlier version of form-validate.js, the logic to render errors should not be in the validate method callback, also the callback argument in touchAll and unTouchAll methods is deprecated.

Deprecated methods

  • validator.setReactComponent
  • validator.getValid
  • validator.isValid

See an example of full react component with form validation below

import React from 'react';
import FormValidate from 'form-validate.js';

const constraint = {
  username: {
    presence: true,
    // async validation
    customAsync: (value, attributes, attributeName) => {
      // it is possible for value to be null or undefined
      if ((value || "").trim() === "") return;

      return resolve => {
        if (value.trim() === "") {
          resolve();
          return;
        }

        setTimeout(() => {
          if (["joshua", "john", "rita"].includes(value)) {
            resolve("%{value} is taken");
          } else {
            resolve();
          }
        }, 1000);
      };
    }
  },
  password: {
    presence: true,
    length: {
      minimum: 8
    }
  },
  unique: {
    // custom validation can work on controls not associate with an input field if it is the only rule specified otherwise, it must be associated with an input field
    custom: (value, attributes, attributeName) => {
      if (attributes.username === attributes.password) {
        return "^the username and password cannot be thesame";
      }
      return null;
    }
  }
};

class Component extends React.Component {
  constructor(props) {
    super(props);

    this.validator = new FormValidate(constraint);
    this.validator.render((valid, controls) => {
      // rerender validation errors and perform actions after validation
      this.setState({});
    });
  }

  render() {
    // destructure out the controls property
    const { controls } = this.validator;

    return (
      <div>
        <form
          onChange={this.validateForm}
          onSubmit={this.onSubmit}
          autoComplete="off"
        >
          <input type="text" name="username" />
          {/* display loader if username control is loading or all username errors if field is touched */}
          {controls.username.loading ? (
            <div>checking...</div>
          ) : (
            <div>
              {controls.username.touched &&
                controls.username.errors.map((error, i) => (
                  <div key={i}>{error}</div>
                ))}
            </div>
          )}
          <input type="password" name="password" />
          {/* display first password error at all times if any exists */}
          <div>{controls.password.errors[0]}</div>
          <div>{controls.unique.touched && controls.unique.errors[0]}</div>
          {/* disable submit button based on the form valid state */}
          <button disabled={!this.validator.valid()}>Submit</button>
        </form>
      </div>
    );
  }

  validateForm = event => {
    // get nativeEvent out of the react change event.
    this.validator.validate(event.nativeEvent, (valid, controls) => {
      // perform some logic after validation
    });
  };

  onSubmit = event => {
    event.preventDefault();
    if (!this.validator.valid) {
      this.validator.touchAll();
      return;
    }
    // ...
  };
}

export default Component;
1.2.2

4 years ago

1.2.1

4 years ago

1.2.0

5 years ago

1.1.1

5 years ago

1.1.0

5 years ago

1.0.6

5 years ago

1.0.5

5 years ago

1.0.3

5 years ago

1.0.1

5 years ago

1.0.0

5 years ago