表單批量驗證,是一個常見應用場景,剛開始寫代碼那會兒,就做過這個驗證,摳了很長時間,才把驗證做完。這種驗證,從做開發開始,到今天,怎么樣也做過數次了,確切地說,每家公司都做過。
可是,每隔一段時間,就會忘個差不多,不記得當初是怎么做的了。最近,又遇到了批量驗證。前臺驗證插件還是那幾款插件,然而很久沒寫了,也想不起當初是怎么做的了,又得從來再來一遍。
重新搜索資料也是需要花費時間的,想避免這個浪費,就得整理總結,便于日后翻看。下面,進入正題。
環境說明:
springboot2.1.4 搭建的簡易后臺
thymeleaf模板
bootstrap前端框架
jquery、jquery.validate前端驗證插件
首先,使用springboot搭建一個簡易后臺,pom中引入文件如下:
<!-- web開發架包引用 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- thymeleaf架包引用 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
第二步,構建一個測試的Controller后臺,來接收批量驗證的數據。
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import com.example.entity.Pager;
import com.example.entity.Test;
/**
* 表單批量驗證測試demo
* @author 程就人生
* @date 2019年9月11日
*/
@RestController
@RequestMapping("/index")
public class TestController {
/**
* 頁面初始化
* @return
*
*/
@GetMapping
public ModelAndView index(){
return new ModelAndView("/index");
}
/**
* 提交操作
* @param test 接收提交的數據
* @return
*
*/
@PostMapping
public Object save(@RequestBody Test... tests){
Map<String,String> returnMap = new HashMap<String,String>();
for(Test test : tests){
System.out.println(test.getUserName() + ":" + test.getUserMobile() + ":" + test.getEmail() + ":" + test.getStatus());
}
returnMap.put("message", "共提交數據:" +tests.length + "條");
return returnMap;
}
/**
* 列表查詢,異步獲取數據
* @param pager
* @return
*
*/
@GetMapping("/searchByCriteria")
public Object searchByCriteria(Pager pager){
List<Test> list = new ArrayList<Test>();
//模擬一條已經存在的數據,也可以模擬多條
Test test = new Test();
test.setUserUid("123456");
test.setUserName("aa");
test.setUserMobile("15994587889");
//0,未刪除;1:已刪除;
test.setIsDelete((byte)0);
list.add(test);
Map<String,Object> map = new HashMap<String,Object>();
map.put("total", 0);
map.put("code", 200);
map.put("rows", list);
return map;
}
}
第三步,構建前端頁面來進行js驗證。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="renderer" content="webkit">
<meta http-equiv="Cache-Control" content="no-siteapp" />
<title>表單批量驗證demo</title>
<meta name="keywords" content="">
<meta name="description" content="">
<!--[if lt IE 9]>
<meta http-equiv="refresh" content="0;ie.html" />
<![endif]-->
<link rel="stylesheet" th:href="@{/css/bootstrap.min14ed.css?v=3.3.6}">
<link rel="stylesheet" th:href="@{/css/bootstrap-table.min.css}">
</head>
<body class="gray-bg">
<div class="wrapper wrapper-content animated fadeIn">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-content">
<form id="testForm" >
<button id="addButton" class="btn btn-info" type="button"><i class="fa fa-plus"></i> 新增</button>
<button id="saveButton" class="btn btn-info" type="submit"><i class="fa fa-check"></i> 提交保存</button>
<div class="table-responsive">
<!-- bootstrap Table 使用 -->
<table id="table"></table>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script th:src="@{/js/jquery.min.js?v=2.1.4}"></script>
<script th:src="@{/js/bootstrap.min.js?v=3.3.6}"></script>
<!-- jquery驗證start -->
<script th:src="@{/js/validate/jquery.validate.min.js}"></script>
<script th:src="@{/js/validate/jquery.validate-extend.js}"></script>
<script th:src="@{/js/validate/messages_zh.min.js}"></script>
<!-- jquery驗證end -->
<!-- bootstrap-table插件start -->
<script th:src="@{/js/bootstrap-table.min.js}"></script>
<script th:src="@{/js/bootstrap-table-zh-CN.min.js}"></script>
<!-- bootstrap-table插件end -->
<script th:inline="javascript">
$('#table').bootstrapTable({
url: "/index/searchByCriteria", //請求后臺的URL(*)
method: 'GET', //請求方式(*)
toolbar: '.searchArea',
clickEdit: true,
striped: true, //是否顯示行間隔色
cache: false, //是否使用緩存,默認為true,所以一般情況下需要設置一下這個屬性(*)
pagination: false, //是否顯示分頁(*)
sortable: false, //是否啟用排序
sortOrder: "asc", //排序方式
sidePagination: "server", //分頁方式:client客戶端分頁,server服務端分頁(*)
queryParamsType:"limit",
contentType:"application/x-www-form-urlencoded; charset=UTF-8",
pageList: [10, 25, 50, 100], //可供選擇的每頁的行數(*)
queryParams:queryParams,
search: false, //是否顯示表格搜索
strictSearch: false,
showColumns: false, //是否顯示所有的列(選擇顯示的列)
showRefresh: false, //是否顯示刷新按鈕
minimumCountColumns: 2, //最少允許的列數
clickToSelect: true, //是否啟用點擊選中行
//height: 500, //行高,如果沒有設置height屬性,表格自動根據記錄條數覺得表格高度
uniqueId: "ID", //每一行的唯一標識,一般為主鍵列
showToggle: false, //是否顯示詳細視圖和列表視圖的切換按鈕
cardView: false, //是否顯示詳細視圖
detailView: false, //是否顯示父子表
columns: [
{
field: 'userName',
title: '姓名',
halign: 'center',//表頭居中
formatter: function (value, row, index) {
return '<input type="hidden" name="userUid'+index+'" id="userUid'+index+'" value="'+(row.userUid==undefined?'':row.userUid)+'" /><input type="text" maxlength=20 required="true" name="userName'+index+'" id="userName'+index+'" value="'+value+'" />';
}
}, {
field: 'userMobile',
title: '手機號碼',
halign: 'center',
formatter: function (value, row, index) { //isMobile是jquery.validate-extend.js中的方法,可以通過class='isMobile'來綁定驗證
return '<input type="hidden" name="isDelete'+index+'" id="isDelete'+index+'" value="'+(row.isDelete==undefined?0:row.isDelete)+'" /><input class="isMobile" type="text" maxlength=30 required="true" name="userMobile'+index+'" id="userMobile'+index+'" value="'+value+'" />';
}
},{
field: 'email',
title: '電子郵箱',
halign: 'center',
formatter: function (value, row, index) { //email是validate自帶的驗證方法,可以直接使用type='email'來綁定驗證
return '<input name="email'+index+'" id="email'+index+'" type="email" value="'+(row.email==null?'':row.email)+'" />';
}
}, {
field: 'status',
title: '狀態',
halign: 'center',
align: "center",
formatter: function (value, row, index) {
return '<input type="radio" name="status'+index+'" value="1" />啟用<input type="radio" name="status'+index+'" checked value="0" />禁用';
}
}, {
field: 'deleteLabel',
title: '操作',
halign: 'center',
align: 'center',
formatter: function (value, row, index) {
return '<button class="btn btn-warning btn-circle" type="button" onclick="hiddenRow('+index+')" >刪除<i class="fa fa-times"></i></button>';
}
}],
onLoadSuccess: function () {
//TODO
},
formatLoadingMessage: function(){
return "請稍等,正在加載中。。。";
},
onLoadError: function () {
//showTips("數據加載失敗!");
}
});
function searchByCriteria() {
$('#table').bootstrapTable('refresh');
};
// 查詢全部信息的參數
function queryParams(params) {
var temp = {};
return temp;
};
//新增一行表格
$("#addButton").click(function(){
//獲取table中有沒有具體的數據行數,沒有時,會返回0
var length = $('#table').bootstrapTable('getData').length;
//獲取table中的行數,無數據時,會有一個提示無數據的tr行
var trLength = $('#table>tbody').children("tr").length;
if(length == 0 && trLength == 1){
$('#table').bootstrapTable('append',{
index:length,
userName:'',
userMobile:'',
email:'',
status:0
})
//增加手機號碼的驗證,isMobile是jquery.validate-extend.js的驗證方法,如果有新的電話號碼格式,可以在里面追加規則
$("#userMobile"+length).attr("class","isMobile");
}else{
var appendHtml = '<tr data-index="'+trLength+'"><td><input name="userUid'+trLength+'" id="userUid'+trLength+'" type="hidden" value=""><input name="userName'+trLength+'" id="userName'+trLength+'" required="true" type="text" maxlength="30" ></td>';
appendHtml += '<td><input name="isDelete'+trLength+'" id="isDelete'+trLength+'" type="hidden" value="0"><input class="isMobile" name="userMobile'+trLength+'" id="userMobile'+trLength+'" required="true" type="text" maxlength="20" /></td>';
appendHtml +='<td><input name="email'+trLength+'" id="email'+trLength+'" type="email" /></td>'
appendHtml += '<td style="text-align: center;" ><input type="radio" name="status'+trLength+'" value="1" />啟用<input type="radio" name="status'+trLength+'" checked value="0" />禁用</td><td style="text-align: center; "><button class="btn btn-warning btn-circle" onclick="hiddenRow('+trLength+')" type="button">刪除<i class="fa fa-times"></i></button></td></tr>';
$('#table>tbody').append(appendHtml);
}
});
//對刪除的數據進行隱藏
function hiddenRow(index){
$("#isDelete"+index).val(1);
//刪除后,去掉必填驗證
$("#userName"+index).removeAttr("required");
$("#userMobile"+index).removeAttr("required");
$('#table').children("tbody").children("tr").eq(index).hide();
}
//獲取表單,對表單參數的封裝
$.fn.serializeList = function(){
var testList = new Array();
var o = {};
$('#table').children("tbody").children("tr").each(function(i){
if($("#userUid"+i).val()!=''||$("#isDelete"+i).val()!=1){
o = {};
o.userUid = $("#userUid"+i).val();
o.isDelete = $("#isDelete"+i).val();
o.userName = $("#userName"+i).val();
o.userMobile = $("#userMobile"+i).val();
o.email = $("#email"+i).val();
//單選框值的獲取
o.status = $("input[name='status"+i+"']:checked").val();
testList[testList.length] = o;
}
});
return testList;
};
//form表單驗證
var returnvalidate = $("#testForm").validate({
submitHandler: function(form) { //通過之后回調
$.ajax({
type: "POST",
url: "/index",
data: JSON.stringify($(form).serializeList()),
dataType: "json",
contentType: "application/json",
success : function(data) {
alert(data.message);
/* if (data.code == 200) {
//刷新父頁面
searchByCriteria();
} else{
alert(data.message);
} */
}
});
},
invalidHandler: function(form, validator) { //不通過回調
return false;
}
});
</script>
</body>
</html>
最后,測試。
對于每一行,都測試一下,驗證不通過時,是否進行信息提示,運行結果圖如下:
輸入驗證全部通過后,后臺打印結果:
a:13555555555:2@qq.com:1
b:13555555555:2@qq.com:1
c:13555555555:2@qq.com:0
jquery.validate是一款很好用也很常用的前端js驗證插件,它驗證的一個核心是通過表單控件name進行唯一判斷和驗證。如果是一個數組,多個控件都是一個name,那就需要注意了。
在jquery.validate驗證里,如果是一個數組,只要數組里的有一個驗證通過,其他驗證不通過也不進行提示。這主要因為在插件里,有這么一段代碼被注釋掉了。如果把這段代碼放開,可能又會引起其它的問題。
//if ( this.name in rulesCache || !validator.objectLength($(this).rules()) ) { // return false; //}
既然如此,對于表單的批量驗證,還是要遵循驗證它的原則,通過name指定唯一標識來進行驗證,繞開不能通過數組來進行驗證的問題。以前是這么解決的,有些技術難題解決不了,就繞過去,但是這次又圍著這個技術難題搜了很多資料,現在想來,完全沒有必要。
表單批量驗證,一般都是針對表單數據量不是很大,但是又需要批量新增、修改、刪除的操作。這是很常用的業務場景,數據量多的時候,一條一條處理太費時。
在使用jquery.validate驗證時,通過這次整理,對jquery.validate-extend.js中擴展的方法有了一個了解,以前只知道引用這個文件,但是怎么用不知道。因為沒有去看jquery.validate-extend.js里面的代碼。
這次終于知道了,像手機驗證,在這個js文件中就已經包含了,無需再重新寫一個,直接使用就可以了。除了常見的將isMobile加入到rules里這一種方式外,在批量驗證時,還可以以class的方式綁定驗證,這是以前所不知道的。
jquery.validate-extend.js中的isMobile方法,對于新加的手機號14開頭的,在這里進行擴展。
通過這個demo,對jquey的validate的認識又加深了一步,如要了解更多,還是要查看官方文檔或者其他比較詳盡的文檔。
// 手機號碼驗證
jQuery.validator.addMethod("isMobile", function(value, element) {
var length = value.length;
return this.optional(element) || (length == 11 && /^(((13[0-9]{1})|(14[0-9]{1})|(15[0-9]{1})|(18[0-9]{1}))+\d{8})$/.test(value));
}, "請正確填寫您的手機號碼。");
參考資料:
https://www.runoob.com/jquery/jquery-plugin-validate.html
https://www.jquery123.com/