@quatrecentquatre/form-me
v5.0.0
Published
FormMe =======
Downloads
66
Readme
FormMe
Helps you handle recurrent form manipulation on submit
Installation
First of all, you must allow your project to download the package.
To do so, make sure you have a .npmrc file (at the same level as the package.json file) containing the following code:
//registry.npmjs.org/:_authToken=${NPM_TOKEN}Replace ${NPM_TOKEN} by the token you will find in Zoho behind NPM - Clé installation package.
Then, add the package to your project:
$ yarn add @quatrecentquatre/form-meUsage
First, import the library in your main JS file:
import "@quatrecentquatre/form-me";Here you go ! You're now ready to use FormMe.
To create a form and to add validation to it, here's the things you need to do to make it work.
- Create your HTML form
- Create your Javascript view.
HTML Form
You'll need to create an HTML form with a special attribute data-me-form with the javascript class name you'll create as value.
Example:
<form method="post" action="#" ajax="true" data-me-form="BasicForm">
<div>
<label for="name">Name</label>
<input type="text" name="name" id="name" placeholder="name" />
<p class="hide error" aria-hidden="true" role="alert" aria-atomic="true">
Field error message
</p>
</div>
<div>
<label for="email">Email</label>
<input type="text" name="email" id="email" placeholder="email" />
<p class="hide error" aria-hidden="true" role="alert" aria-atomic="true">
Field error message
</p>
</div>
<input type="submit" />
</form>Here's are the attribute to set on your form element. Not required all required but might be for the best to set them.
- method : (Mandatory) POST or GET
- ajax : If added, need to be set to "true". If ajax not needed, just remove the attribute from the DOM
- action : URL for the request
There's additional attributes that can be set on the form
- recaptcha : (Boolean) True | False to determine if you want recaptcha active
- recaptcha-action : (String) If you want to specify the action passed to recaptcha.
- data-me-form-data : (Object) If you want to pass data from the html to js
Javascript view
You'll need to create a Javascript file with a class that extends the basic Form class.
In the constructor, you'll have to add fields that need validation. For all possible option for a single field, please refer to the field section.
Example:
// We use yup for validation so we've set the import in the example too but needs to be modify to fit your needs.
import { string } from "yup";
import { FormBase } from "@quatrecentquatre/form-me";
export class BasicForm extends FormBase {
constructor(options) {
super(options);
this.addFields([
{
name: "name",
validationSchema: string().required(),
$error: document
.querySelector('[name="name"]')
.parentElement.parentElement.querySelector(".error"),
},
{
name: "email",
validationSchema: string().required(),
$error: document
.querySelector('[name="email"]')
.parentElement.parentElement.querySelector(".error"),
},
]);
}
}
Me.forms["BasicForm"] = BasicForm;View params
You can have default params set for your form view. The way to do it is to declare the defaults function and return the object of defaults value you want. These params can be useful if you want to add another layer to fit your particular need with a re-usable base class. These params will merge with the data-me-form-data that you can set on the HTML side.
Example:
import { string } from "yup";
import { FormBase } from "@quatrecentquatre/form-me";
class BasicForm extends FormBase {
defaults() {
return {
name: "username",
};
}
constructor(options) {
super(options);
this.addFields([
{
name: "name",
validationSchema: string().required(),
$error: document
.querySelector('[name="name"]')
.parentElement.parentElement.querySelector(".error"),
},
{
name: "email",
validationSchema: string().required(),
$error: document
.querySelector('[name="email"]')
.parentElement.parentElement.querySelector(".error"),
},
]);
}
}
Me.forms["BasicForm"] = BasicForm;The defaults params can be changed / extended in your DOM declaration too.
Example:
<form
method="post"
action="#"
ajax="true"
data-me-form="BasicForm"
data-me-form-data='{"extend": "extend"}'
>
<div>
<label for="name">Name</label>
<input type="text" name="name" id="name" placeholder="name" />
<p class="hide error" aria-hidden="true" role="alert" aria-atomic="true">
Error
</p>
</div>
<div>
<label for="email">Email</label>
<input type="text" name="email" id="email" placeholder="email" />
<p class="hide error" aria-hidden="true" role="alert" aria-atomic="true">
Error
</p>
</div>
<input type="submit" />
</form>Field params
- name: (String) [Required] Name attribute of the field
- validationSchema: (Yup Schema)[Required] // See https://github.com/jquense/yup
- $error: (HtmlElement) Single element that will automatically show on error
- maskOptions: (Object IMask Options) // See https://imask.js.org/
- format: (Function) Function to format the field value before sending data through ajax call
Customization
Validation
If you are looking to add a custom validation on one of the field, you can use the .test function. Follow the Yup documentation at https://github.com/jquense/yup#schematestname-string-message-string--function--any-test-function-schema
Field example:
{
name: 'file',
validationSchema: mixed()
.test('is-valid-type', 'Not a valid file type', (value) => value && value.name.toLowerCase().split('.').at(-1) === 'jpg')
.test('is-valid-size', 'Max allowed size is 1MB', (value) => value && value.size <= 1024000),
$error: document.querySelector('[name="file"]').parentElement.parentElement.querySelector('.error'),
}Format
If you have to format a field value before sending it through the ajax call you can use a custom function to format the value. The function will automatically receive the field value.
import { string } from "yup";
import { FormBase } from "@quatrecentquatre/form-me";
class BasicForm extends FormBase {
constructor(options) {
super(options);
this.addFields([
{
name: "name",
validationSchema: string().required(),
$error: document
.querySelector('[name="name"]')
.parentElement.parentElement.querySelector(".error"),
format: this.toUppercase.bind(this),
},
{
name: "email",
validationSchema: string().required(),
$error: document
.querySelector('[name="email"]')
.parentElement.parentElement.querySelector(".error"),
},
]);
}
toUppercase(value) {
return value.toUpperCase();
}
}
Me.forms["BasicForm"] = BasicForm;Empty values
By default, if a field is not required and empty, FormMe will exclude it from the values sent. If you want to allow empty values you can use this.acceptSendingEmptyValues = true
Functions
Here's a list of some functions that can be modified or to overwrite. You can see in the lib all functions that can be changed if you want to.
submitHandler(e)
Will be triggered by clickSubmitHandler and if the user submit by hitting enter. Will look if ajax is defined or not to trigger the right submit.
handleValidationSuccess()
If no error found, it will go through this function. Will add a class if you want to apply new styles, it will check if recaptcha is present and if need to be validated and then send a formatted version of the form data to the function that will use the fetch api to send the data.
handleValidationError()
If error found during validation, it will pass here to show fields with error to the user.
setFieldState(field, error)
Set error state of the field. We base the logic on the error type to display the right error message already present in the HTML. Overwrite the function to handle what needs to be set if you want to do custom manipulation on error handling.
resetFieldState(field)
As the name says, restore field value to an empty one. Based on the data-me-form-error to add the hide class. Overwrite the function to handle what needs to be reset if you want to do custom manipulation.
ajaxSuccess(response)
As the function name says it, when the call return a success, it goes in and restore the form, add a class to let the dev display a message with css only. You can overwrite this function to have a custom handle of the success provided by the backend.
ajaxError(error)
As the function name says it, when the call return an error, it goes in and add a class to let the dev display an error message only with css. You can overwrite this function to have a custom handle of the error provided by the backend and handle different error that can be returned.
ajaxComplete()
This function exist to handle all code that should be run if a success or an error is triggered. If you need to do anything no matter what the response is, you can overwrite this function.
reset()
This will reset fields to original empty value
Modify functions
To extend or completely modify a function simple declare it in your new class like the following example. Use super if you want the original code to run before the code you will add.
Example:
import { string } from "yup";
import { FormBase } from "@quatrecentquatre/form-me";
class BasicForm extends FormBase {
constructor(options) {
super(options);
this.addFields([
{
name: "name",
validationSchema: string().required(),
$error: document
.querySelector('[name="name"]')
.parentElement.parentElement.querySelector(".error"),
format: this.toUppercase.bind(this),
},
{
name: "email",
validationSchema: string().required(),
$error: document
.querySelector('[name="email"]')
.parentElement.parentElement.querySelector(".error"),
},
]);
}
resetFieldState(field) {
super.resetFieldState(field);
// Custom code to run after field pass validation success
}
}
Me.forms["BasicForm"] = BasicForm;Errors
Looking to handle error message for your fields? There is a simple way to handle them. Simply add the error attribute to the field object you want an error to show with the element you want to see when the error occur.
Add the element you want to show on error in your HTML
<form method="post" action="#" ajax="true" data-me-form="BasicForm">
<div>
<label for="name">Name</label>
<input type="text" name="name" id="name" placeholder="name" />
<p class="hide error" aria-hidden="true" role="alert" aria-atomic="true">
Field error message
</p>
</div>
<input type="submit" />
</form>this.addFields([
{
name: "name",
$error: document
.querySelector('[name="name"]')
.parentElement.parentElement.querySelector(".error"),
},
]);Multiple error message?
If you're looking to handle multiple case for one field you need to do it by yourself. Since we use YUP for validation. FormMe will detect if there's any data-me-form-error attribute with the right value based on the error type. Ex: Required -> data-me-form-error="required" You can find an example with the email field in the demo.
Submit Button
You can define a custom submit button by adding data-me-form-submit to the desired element. Otherwise, the submit type will be assigned as submit button
Recaptcha v3
If you are loading Recaptcha v3 into your project, FormMe has an automatic detection to ensure recaptcha validation before ajax call.
How does it work?
If you have set ajax="true" on your form and if grecaptcha is defined, FormMe will perform a recaptcha validation before the ajax call.
To perform that validation, FormMe will expect :
- a
window.SETTINGS.RECAPTCHA_SITE_KEYcontaining the recaptcha site key to use - a
recaptcha-actionattribute on your<form>containing the action to use - a
<input type="hidden" name="g-recaptcha-response">into the<form>to store the validation token
In case you have set ajax="true" on your form and grecaptcha is defined but you want to prevent FormMe to perform a recaptcha validation, you can add recaptcha="false" on your <form>
Some case present in the demo
Matching values
If you need to validate fields that needs to have the same value, you can take a look at the schema of the email/email-match
Regex or Mask
If you need to set a mask to set a pattern like for phone, zip code, etc ... you can add a mask. We use Imask. You can add a maskOptions key with a Object as value containing IMask options. You can check the Phone / Tel field that contains Regex and mask.
Regex example
phone: /^(?:\+?1)?[-. ]?\(?[2-9][0-8][0-9]\)?[-. ]?[2-9][0-9]{2}[-. ]?[0-9]{4}$/i;
zipcode: /^[ABCEGHJKLMNPRSTVWXYZ][0-9][ABCEGHJKLMNPRSTVWXYZ]?[ ]?[0-9][ABCEGHJKLMNPRSTVWXYZ][0-9]$/i;Files
If you need to have a file field, you can check the demo to see how size should be handle and type of files uploaded.
