react-craftform
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"]
});
withForm
Creating form with 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).
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)]
});
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
- if value is valid, just return false.
- 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]
});
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)]]
});
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>
);
}
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'])
}
..
)
}
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>)
}
});
};
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>)
}
});
};
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>
);
}
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}>
...
);
}