ReactJs - Viết một Validator class đơn giản dùng để validate form
Bài đăng này đã không được cập nhật trong 4 năm
Trước kia khi validate form bên phía client side mình thưởng sử dụng plugin jquery-validation, tuyên nhiên khi làm việc với ReactJs, dự liệu form thược được chứa trong state, vì vậy sử dụng jquery-validation không còn hợp lý nữa, việc sử dụng jQuery trong code ReactJs cũng không phải là một cách làm hay. Trong bài viết này mình sẽ hướng dẫn các bạn viết một class dùng để validate form trong ReactJs có thể sử dụng lại cho nhiều trường hợp.
Cài đặt
Chúng ta sẽ sử dụng create-react-app
để tạo ứng dụng ReactJs.
Cài đặt package sử dụng npm
:
npm install -g create-react-app
Tạo ứng dụng:
create-react-app react-form-validation-sample
Chạy ứng dụng:
cd react-form-validation-sample/
yarn start
Truy cập vào http://localhost:3000/ trên trình duyệt bạn sẽ thấy ứng dụng đã có thể chạy được:
Chuẩn bị
Mình kiếm được một form snippet khá đẹp trên codepen, nên mình sẽ fork về và sử dụng nó trong ứng dụng này:
Style
Sửa file src/App.css
:
body {
margin: auto;
background: #eaeaea;
font-family: 'Open Sans', sans-serif;
}
.info p {
text-align:center;
color: #999;
text-transform:none;
font-weight:600;
font-size:15px;
margin-top:2px
}
.info i {
color:#F6AA93;
}
form h1 {
font-size: 18px;
background: #F6AA93 none repeat scroll 0% 0%;
color: rgb(255, 255, 255);
padding: 22px 25px;
border-radius: 5px 5px 0px 0px;
margin: auto;
text-shadow: none;
text-align:left
}
form {
border-radius: 5px;
max-width:700px;
width:100%;
margin: 5% auto;
background-color: #FFFFFF;
overflow: hidden;
}
p span {
color: #F00;
}
p {
margin: 0px;
font-weight: 500;
line-height: 2;
color:#333;
}
h1 {
text-align:center;
color: #666;
text-shadow: 1px 1px 0px #FFF;
margin:50px 0px 0px 0px
}
input {
border-radius: 0px 5px 5px 0px;
border: 1px solid #eee;
margin-bottom: 15px;
width: 75%;
height: 40px;
float: left;
padding: 0px 15px;
}
a {
text-decoration:inherit
}
textarea {
border-radius: 0px 5px 5px 0px;
border: 1px solid #EEE;
margin: 0;
width: 75%;
height: 130px;
float: left;
padding: 0px 15px;
}
.form-group {
overflow: hidden;
clear: both;
}
.icon-case {
width: 35px;
float: left;
border-radius: 5px 0px 0px 5px;
background:#eeeeee;
height:42px;
position: relative;
text-align: center;
line-height:40px;
}
i {
color:#555;
}
.contentform {
padding: 40px 30px;
}
.bouton-contact{
background-color: #81BDA4;
color: #FFF;
text-align: center;
width: 100%;
border:0;
padding: 17px 25px;
border-radius: 0px 0px 5px 5px;
cursor: pointer;
margin-top: 40px;
font-size: 18px;
}
.leftcontact {
width:49.5%;
float:left;
border-right: 1px dotted #CCC;
box-sizing: border-box;
padding: 0px 15px 0px 0px;
}
.rightcontact {
width:49.5%;
float:right;
box-sizing: border-box;
padding: 0px 0px 0px 15px;
}
.validation {
display:none;
margin: 0 0 10px;
font-weight:400;
font-size:13px;
color: #DE5959;
}
#sendmessage {
border:1px solid #fff;
display:none;
text-align:center;
margin:10px 0;
font-weight:600;
margin-bottom:30px;
background-color: #EBF6E0;
color: #5F9025;
border: 1px solid #B3DC82;
padding: 13px 40px 13px 18px;
border-radius: 3px;
box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.03);
}
#sendmessage.show,.show {
display:block;
}
Tạo form
File src/App.js
gồm những field đơn giản như name, email, address, subject và message, handle event change lưu data vào state.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor (props) {
super(props);
this.state = {
name: '',
email: '',
address: '',
subject: '',
message: '',
};
}
handleInput = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
};
handleSubmit = (e) => {
// Handle validation here
};
render() {
return (
<div className="App">
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet" />
<h1>Elegant Contact Form.</h1>
<form>
<h1>Should you have any questions, please do not hesitate to contact me :</h1>
<div className="contentform">
<div id="sendmessage"> Your message has been sent successfully. Thank you. </div>
<div className="leftcontact">
<div className="form-group">
<p>Name <span>*</span></p>
<span className="icon-case"><i className="fa fa-user"></i></span>
<input type="text" name="name" value={this.state.name} onChange={this.handleInput}/>
<div className="validation"></div>
</div>
<div className="form-group">
<p>E-mail <span>*</span></p>
<span className="icon-case"><i className="fa fa-envelope-o"></i></span>
<input type="email" name="email" value={this.state.email} onChange={this.handleInput}/>
<div className="validation"></div>
</div>
<div className="form-group">
<p>Address <span>*</span></p>
<span className="icon-case"><i className="fa fa-location-arrow"></i></span>
<input type="text" name="address" value={this.state.address} onChange={this.handleInput}/>
<div className="validation"></div>
</div>
</div>
<div className="rightcontact">
<div className="form-group">
<p>Subject <span>*</span></p>
<span className="icon-case"><i className="fa fa-comment-o"></i></span>
<input type="text" name="subject" value={this.state.subject} onChange={this.handleInput}/>
<div className="validation"></div>
</div>
<div className="form-group">
<p>Message <span>*</span></p>
<span className="icon-case"><i className="fa fa-comments-o"></i></span>
<textarea name="message" onChange={this.handleInput}>{this.state.message}</textarea>
<div className="validation"></div>
</div>
</div>
</div>
<button type="button" className="bouton-contact">Send</button>
</form>
</div>
);
}
}
export default App;
Đây là kết quả sau khi đã custom form trên ReactJs:
Validator class
Cài đặt package validator
:
npm install --save validator
Validator class
Nội dung file src/utils/validator.js
:
import methods from 'validator';
class Validator {
constructor(rules) {
this.rules = rules;
this.initiate();
}
initiate() {
this.isValid = true;
this.errors = {};
}
validate(state) {
this.initiate();
this.rules.forEach((rule) => {
if (this.errors[rule.field]) return;
const fieldValue = state[rule.field] || '';
const args = rule.args || [];
const validationMethod = typeof rule.method === 'string'
? methods[rule.method]
: rule.method;
if (validationMethod(fieldValue, ...args, state) !== rule.validWhen) {
this.errors[rule.field] = rule.message;
this.isValid = false;
}
});
return this.errors;
}
}
export default Validator;
Constructor truyền vào mảng rules
const requiredWith = (value, field, state) => state[field] && value;
const rules = [
{
field: 'name',
method: 'isEmpty',
validWhen: false,
message: 'The name field is required.',
},
{
field: 'name',
method: 'isLength',
args: [{min: 5}],
validWhen: true,
message: 'The name must be at least 5 characters.',
},
{
field: 'message',
method: requiredWith,
args: ['subject'],
validWhen: true,
message: 'The message field is required when subject is present.',
},
];
Mỗi rule sẽ bao gồm:
- field : key của field trong state
- method: method dùng để validate, nếu method là string sẽ lấy các methods trong package validator. Ngoài ra có thể tự viết method như ở trên mình viết có viết
requiredMethod
có các tham số truyền vào là value, các tham số khác, sau đó là state.requiredMethod
sẽ check xem nếu field truyền vào và value có giá trị sẽ trả về true. - args: các tham số sẽ truyền vào.
- validWhen:
true
hayfalse
thì sẽ valid - message: message khi có lỗi
Initiate function
Khởi tạo các giá trị của validator
Validate function
Duyệt lần lượt các rules và chạy method của nó, nếu có lỗi sẽ add vào errors object một property với key là key của field, value là message báo lỗi. VD:
{
name: 'The name field is required.',
email: 'The email must be a valid email address.'
}
App.js
Import validator
import Validator from './utils/validator';
Thêm state errors:
this.state = {
errors: {},
};
Khởi tạo validator trong constructor:
const requiredWith = (value, field, state) => (!state[field] && !value) || !!value;
const rules = [
{
field: 'name',
method: 'isEmpty',
validWhen: false,
message: 'The name field is required.',
},
{
field: 'name',
method: 'isLength',
args: [{min: 5}],
validWhen: true,
message: 'The name must be at least 5 characters.',
},
{
field: 'email',
method: 'isEmpty',
validWhen: false,
message: 'The email field is required.',
},
{
field: 'email',
method: 'isEmail',
validWhen: true,
message: 'The email must be a valid email address.',
},
{
field: 'address',
method: 'isEmpty',
validWhen: false,
message: 'The address field is required.',
},
{
field: 'message',
method: requiredWith,
args: ['subject'],
validWhen: true,
message: 'The message field is required when subject is present.',
},
];
this.validator = new Validator(rules);
Thêm code validate trong method handleSubmit
:
handleSubmit = (e) => {
this.setState({
errors: this.validator.validate(this.state),
});
};
Hiển thị lỗi trong method render
:
{errors.name && <div className="validation" style={{display: 'block'}}>{errors.name}</div>}
File App.js
đầy đủ sẽ như sau:
import React, { Component } from 'react';
import Validator from './utils/validator';
import './App.css';
class App extends Component {
constructor (props) {
super(props);
this.state = {
name: '',
email: '',
address: '',
subject: '',
message: '',
errors: {},
};
const requiredWith = (value, field, state) => (!state[field] && !value) || !!value;
const rules = [
{
field: 'name',
method: 'isEmpty',
validWhen: false,
message: 'The name field is required.',
},
{
field: 'name',
method: 'isLength',
args: [{min: 5}],
validWhen: true,
message: 'The name must be at least 5 characters.',
},
{
field: 'email',
method: 'isEmpty',
validWhen: false,
message: 'The email field is required.',
},
{
field: 'email',
method: 'isEmail',
validWhen: true,
message: 'The email must be a valid email address.',
},
{
field: 'address',
method: 'isEmpty',
validWhen: false,
message: 'The address field is required.',
},
{
field: 'message',
method: requiredWith,
args: ['subject'],
validWhen: true,
message: 'The message field is required when subject is present.',
},
];
this.validator = new Validator(rules);
}
handleInput = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
};
handleSubmit = (e) => {
this.setState({
errors: this.validator.validate(this.state),
});
console.log(this.state);
};
render() {
const {errors} = this.state;
return (
<div className="App">
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet" />
<h1>Elegant Contact Form.</h1>
<form>
<h1>Should you have any questions, please do not hesitate to contact me :</h1>
<div className="contentform">
<div id="sendmessage"> Your message has been sent successfully. Thank you. </div>
<div className="leftcontact">
<div className="form-group">
<p>Name <span>*</span></p>
<span className="icon-case"><i className="fa fa-user"></i></span>
<input type="text" name="name" value={this.state.name} onChange={this.handleInput}/>
{errors.name && <div className="validation" style={{display: 'block'}}>{errors.name}</div>}
</div>
<div className="form-group">
<p>E-mail <span>*</span></p>
<span className="icon-case"><i className="fa fa-envelope-o"></i></span>
<input type="email" name="email" value={this.state.email} onChange={this.handleInput}/>
{errors.email && <div className="validation" style={{display: 'block'}}>{errors.email}</div>}
</div>
<div className="form-group">
<p>Address <span>*</span></p>
<span className="icon-case"><i className="fa fa-location-arrow"></i></span>
<input type="text" name="address" value={this.state.address} onChange={this.handleInput}/>
{errors.address && <div className="validation" style={{display: 'block'}}>{errors.address}</div>}
</div>
</div>
<div className="rightcontact">
<div className="form-group">
<p>Subject</p>
<span className="icon-case"><i className="fa fa-comment-o"></i></span>
<input type="text" name="subject" value={this.state.subject} onChange={this.handleInput}/>
{errors.subject && <div className="validation" style={{display: 'block'}}>{errors.subject}</div>}
</div>
<div className="form-group">
<p>Message</p>
<span className="icon-case"><i className="fa fa-comments-o"></i></span>
<textarea name="message" value={this.state.message} onChange={this.handleInput}/>
{errors.message && <div className="validation" style={{display: 'block'}}>{errors.message}</div>}
</div>
</div>
</div>
<button type="button" className="bouton-contact" onClick={this.handleSubmit}>Send</button>
</form>
</div>
);
}
}
export default App;
Kết quả
Source code
All rights reserved