Skip to content

React form with full customizable and extensible. This library help you create form with your own style.

License

Notifications You must be signed in to change notification settings

Elecweb/react-craftform

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

90 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

react-craftform

Coverage Status

React form library with full customizable and extensible. This library help you create form with your own style.

This library doesn't add any style to your form component. It let style of form to your imagination.

Installation

npm install react-craftform --save

Usage

react-craftform created with HOC in mind. react-craftform provide function called withForm for providing existing form power!

import React from 'react';
import {withForm, validator, hasError} from 'react-craftform';

const MyForm = (props) => {
  return (
     <form>
          <section className="name_section">
              <label>Name</label>
              <input type="text" className="name" onChange={props.form.handleChange("name")} value={props.form.values['name']}/>
          </section>
          <p>Name Value: {props.form.values['name']} </p>
          <section className="code_section">
              <label>Last Name</label>
              <input type="text" className="code" onChange={props.form.handleChange("lastname")} value={props.form.values['lastname']} />
              
          </section>
          <p>LastName Value: {props.form.values['lastname']} </p>
          {hasError(props.form.errors) ? "There is an error" : "No Error, Huley"}
      </form>
  );
}

export default withForm(MyForm,{
  name:["",["required"]],
  lastname:["myinitalCode"]
});

Plunker example

Creating form with withForm

withForm take 2 parameters. First parameter it takes is your form component. Second parameter is description about your controls.

Let's take a look at withForm function.

 export default withForm(MyForm,{
    name:["",["required"]],
    lastname:["myinitilCode"]
 });

MyForm component will receive object called forn via props for managing control (eg. handling onChange event, geting value and errors).

Second parameter is object which property name is your control name. Each control name's value is array which for specifying initial value at index 0 and Validators at index 1.

In above code, we tell withForm that it has 2 controls which name are name and lastname. name control have no initial value and is required and lastname control have initial value which is "initialCode" and is opional.

Handling change on input

withForm doesn't update value for you so that you can customize as you want. Instead, it help you by providing form object via props with have method for update value.

form props has handleChange method for updating value. handleChange accept control name.

   <input type="text" className="name" onChange={props.form.handleChange("name")} value={props.form.values['name']}/>
   <input type="text" className="code" onChange={props.form.handleChange("lastname")} value={props.form.values['lastname']} />

Getting value of control

You can get value of control by call props.form.values[controlname].

As above exmaple, we pass props.form.values['name'] to input attribute value for name control and props.form.values['lastname'] for lastname control.

Validation

You can add validator easily in description object.

   import { validator } from 'react-craftform';
   
   ...
   
   export default withForm(MyForm,{
     name:["",["required",validator.minLength(3)]],
     lastname:["myinitilCode",validator.minLength(5)]
   });

You can use built-in validator provided by this library or create your own validator (I promise it's easy).

Plunker example

Error object

Error object will be generated according to specified validators. You can get error object in props.form.errors[controlname].

Error object has property name indicating what validator that control violate and it's value is detail about the error you can leverage (eg. showing more meaningful error message)

As above example, there'll be initial error for name and lastname controls.

 props.form.errors['name']; // { required: true, minLength: {length: 0, minRequired:3 }}
 props.form.errors['lastname']; // { maxLength:{length: 12, maxRequired:3 }}

You can use error object for showing in error message and/or prevent submit if there's an error. We will explain in Error message section and preventing user submit form section

Built-in validator

Function String example of error object
validator.required "required" { required:true }
validator.minLength(minRequired) - { minLength:{ length:3, minRequired:4 } length is current length of control
validator.maxLength(maxRequired) - { maxLength:{ length:6, maxRequired:4 } length is current length of control

Custom validator

Validator is just a function. You can provide your own easily.

 ...
 const haveToBeCat = (val) => {
   if(val === "cat"){ 
       return false;
   }else{
       return {
           havetoBeCat:true
       }
   }
 };

 export default withForm(MyForm,{
   name:["",[haveToBeCat]],
   lastname:["myinitilCode",validator.maxLength(3)]
 });

Plunker example

In the above example, we create function called haveToBeCat. It will be called and provided current value of control to first parameter by library so you can use it to check current value against your specified rule.

When create your own custom validator, just remember 2 things

  1. if value is valid, just return false.
  2. if value is invalid, return object which propery name is meaningful name. You will be use that name for showing error message.

haveToBeCat works great but you can refactor haveToBeCat to be more reuseable.

   const haveToBeSomething = (word) => {
       return (val) => {
           if(val === word){
               return false;
           }else{
               return {
                    havetoBeSomething:{
                      expectedValue:word,
                      value:val
                     }
               }
           }
       }

   };

   const haveToBeCat = haveToBeSomething("cat");
   const haveToBeDog = haveToBeSomething("dog");
  
   export default withForm(MyForm,{
     name:["",[haveToBeCat]],
     lastname:["myinitilCode",haveToBeDog]
   });

Plunker example

May functional programing be with you.

Showing error message

You can provide error message by calling errorMessage function. It accept one parameter and return another function which accept error object and return component we provide. It use currying function style.

If you're confused, don't worry just let code exlain itself.

  ...
  import {withForm, validator, hasError, errorMessage} from 'react-craftform';

  const MyForm = (props) => {
    return (
     <form>
          <section className="name_section">
              <label>Name</label>
              <input type="text" className="name" onChange={props.form.handleChange("name")} value={props.form.values['name']}/>
          </section>
          <p>Name Value: {props.form.values['name']} </p>
          {
            errorMessage({
                minLength:()=>{
                    return (<p>Please fill control with 5 length</p>);
                },
                required:()=>{
                    return (<p>Please fill input</p>);
                },        
            })(props.form.errors['name'])
          }
          <section className="code_section">
              <label>Last Name</label>
              <input type="text" className="code" onChange={props.form.handleChange("lastname")} value={props.form.values['lastname']} />
              
          </section>
          <p>LastName Value: {props.form.values['lastname']} </p>
          {
            errorMessage({
                minLength:()=>{
                    return (<p>Please fill control with 5 length</p>);
                },
                required:()=>{
                    return (<p>Please fill input</p>);
                },        
            })(props.form.errors['lastname'])
          }
      </form>
  );
}

export default withForm(MyForm,{
  name:["",["required"]],
  lastname:["",[validator.minLength(5)]]
});

Plunker example

Get rid of duplicated code

It works great but there's duplicated code for name and lastname controls. You can get rid of it by creating function and return invoking errorMessage function.

const errorSpec =  errorMessage({
    minLength:()=>{
        return (<p>Please fill control with 5 length</p>);
    },
    required:()=>{
        return (<p>Please fill input</p>);
    }
});

const MyForm = (props) => {
  return (
     <form>
          <section className="name_section">
              <label>Name</label>
              <input type="text" className="name" onChange={props.form.handleChange("name")} value={props.form.values['name']}/>
          </section>
          <p>Name Value: {props.form.values['name']} </p>
          {
            errorSpec(props.form.errors['name'])
          }
          <section className="code_section">
              <label>Last Name</label>
              <input type="text" className="code" onChange={props.form.handleChange("lastname")} value={props.form.values['lastname']} />
              
          </section>
          <p>LastName Value: {props.form.values['lastname']} </p>
          {
            errorSpec(props.form.errors['lastname'])
          }
      </form>
  );
}

Plunker example

Just assign variable to errorMessage with how to render error message.

Showing error with information

You can provide error information easily for showing error message

const errorSpec = (name) => {
    return errorMessage({
        minLength:()=>{
            return (<p>Please fill control {name} with 5 length</p>);
        },
        required:()=>{
            return (<p>Please fill control {name}</p>);
        }
    });
};

...
const MyForm = (props) => {
         return (
          ...
          {
            errorSpec('name')(props.form.errors['name'])
          }
          ..
          )
}

Plunker example

Showing error message with custom validator

It's the same as built-in validator. Let me show you

   const haveToBeSomething = (word) => {
       return (val) => {
           if(val === word){
               return false;
           }else{
               return {
                   havetoBeSomething:{
                     expectedValue:word,
                     value:val
                   }
               }
           }
       }

   };

   const haveToBeCat = haveToBeSomething("cat");
   const haveToBeDog  haveToBeSomething("dog");
  
   const errorSpec = () => {
       return errorMessage({
           ...
           havetoBeSomething:() => {
               return (<p>Please fill cat!</p>)
           }
       });
   };

Plunker example

It's easy but there's one problem, error message for havetoBeSomething assume to be "cat". How about dog, pig, elephant or whatever?

Luckily we can handle this issue in easy way.

Using error object for more meaningful error message

     const errorSpec = () => {
        return errorMessage({
            ...
            havetoBeSomething:(def) => {
                return (<p>Please fill {def.expectedValue}! Now you provide {def.value}</p>)
            }
        });
    };

Plunker example

errorMessage will be provided corresponding error object in parameter of function. So you can use it for providing more meaningful error message.

validator.minLength and validator.maxLength are also provide error object. you can look at here

How to handle form submit

withForm doesn't handle submit form. We want you to have full control.

Provided props to withForm component will go to your form component as well. So you can add callback for onSubmit event via props.

const MyForm = (props) => {
  const onSubmit = (evt) => {
    evt.preventDefault();
    props.onSubmit(props.form.values);
  };
  return (
     <form onSubmit={onSubmit}>
          <section className="name_section" >
              <label>Name</label>
              <input type="text" className="name" onChange={props.form.handleChange("name")} value={props.form.values['name']}/>
          </section>
          <p>Name Value: {props.form.values['name']} </p>
          {
            errorSpec()(props.form.errors['name'])
          }
          <section className="code_section">
              <label>Last Name</label>
              <input type="text" className="code" onChange={props.form.handleChange("lastname")} value={props.form.values['lastname']} />
              
          </section>
          <p>LastName Value: {props.form.values['lastname']} </p>
          {
            errorSpec()(props.form.errors['lastname'])
          }
          <button type="submit">Submit</button>
      </form>
  );
}

Plunker example

There's no difference for handling form in normal way, except you can get value from props.form.values which it's object whose properies is control name and it's value is corresponding control value.

Preventing form submit if there's an error

You can check there's an error easily with hasError function.

 ...
 const MyForm = (props) => {
  const onSubmit = (evt) => {
    evt.preventDefault();
    if(hasError(props.form.errors)){
        window.alert("There's an error");
    }else{
        props.onSubmit(props.form.values);
    }
    
  };
  return (
     <form onSubmit={onSubmit}>
     ...
  );
}

Plunker example

About

React form with full customizable and extensible. This library help you create form with your own style.

Resources

License

Stars

Watchers

Forks

Packages

No packages published