八 表单验证
1 Bootstrap-validator
Bootstrap-validator是一个基于 Bootstrap 的表单验证插件,它可以帮助开发者更方便地进行表单验证,提高用户体验。
① 引入插件
<!--引入验证插件的样式文件-->
<link rel="stylesheet" href="/static/js/plugins/bootstrap-validator/css/bootstrapValidator.min.css"/>
<link rel="stylesheet" href="/static/js/plugins/bootstrap-treeview/bootstrap-treeview.min.css"/>
<!--引入验证插件的js文件-->
<script type="text/javascript" src="/static/js/plugins/bootstrap-validator/js/bootstrapValidator.min.js"></script>
<!--中文语言库-->
<script type="text/javascript" src="/static/js/plugins/bootstrap-validator/js/language/zh_CN.js"></script>
2 更新前端页面的表单验证规则 – /employee/input
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>员工管理</title>
</head>
<body class="hold-transition skin-black sidebar-mini">
<div th:replace="common/fragment :: link"></div>
<div class="wrapper">
<div th:replace="common/fragment :: navbar"></div>
<div th:replace="common/fragment :: menu"></div>
<div class="content-wrapper">
<section class="content-header">
<h1>员工编辑</h1>
</section>
<section class="content">
<div class="box">
<form class="form-horizontal" action="/employee/saveOrUpdate" method="post" id="editForm" >
<input type="hidden" name="id" th:value="${employee?.id}">
<div class="form-group" style="margin-top: 10px;">
<label class="col-sm-2 control-label">用户名:</label>
<div class="col-sm-6">
<input type="text" class="form-control" th:disabled="${employee != null}" th:value="${employee?.username}" name="username" placeholder="请输入用户名">
</div>
</div>
<div class="form-group" style="margin-top: 10px;">
<label class="col-sm-2 control-label" >真实姓名:</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="name" th:value="${employee?.name}" placeholder="请输入真实姓名">
</div>
</div>
<div class="form-group" th:if="${employee == null}">
<label for="password" class="col-sm-2 control-label">密码:</label>
<div class="col-sm-6">
<input type="password" class="form-control" id="password" name="password" placeholder="请输入密码">
</div>
</div>
<div class="form-group" th:if="${employee == null}">
<label for="repassword" class="col-sm-2 control-label">验证密码:</label>
<div class="col-sm-6">
<input type="password" class="form-control" id="repassword" name="repassword" placeholder="再输入一遍密码">
</div>
</div>
<div class="form-group">
<label for="email" class="col-sm-2 control-label">电子邮箱:</label>
<div class="col-sm-6">
<input type="text" th:value="${employee?.email}" class="form-control" id="email" name="email" placeholder="请输入邮箱">
</div>
</div>
<div class="form-group">
<label for="age" class="col-sm-2 control-label">年龄:</label>
<div class="col-sm-6">
<input type="text" class="form-control" th:value="${employee?.age}" id="age" name="age" placeholder="请输入年龄">
</div>
</div>
<div class="form-group">
<label for="dept" class="col-sm-2 control-label">部门:</label>
<div class="col-sm-6">
<select class="form-control" id="dept" name="dept.id">
<option value="">请选择</option>
<option th:each="department:${departments}"
th:text="${department.name}"
th:value="${department.id}"
th:selected="${department.id == employee?.dept?.id}"></option>
</select>
</div>
</div>
<div class="form-group" id="adminDiv">
<label for="admin" class="col-sm-2 control-label">超级管理员:</label>
<div class="col-sm-6" style="margin-left: 15px;">
<input type="checkbox" id="admin" th:checked="${employee?.admin}" name="admin" class="checkbox" >
</div>
</div>
<div class="form-group" id="roleDiv">
<label for="role" class="col-sm-2 control-label">分配角色:</label><br/>
<div class="row" style="margin-top: 10px">
<div class="col-sm-2 col-sm-offset-2">
<select multiple class="form-control allRoles" id="role" style="height: 350px;" size="15">
<option th:each="role:${roles}"
th:text="${role.name}"
th:value="${role.id}"></option>
</select>
</div>
<div class="col-sm-1" style="margin-top: 60px;" align="center">
<div>
<a type="button" class="btn btn-primary " style="margin-top: 10px" title="右移动"
onclick="moveSelected('allRoles', 'selfRoles')">
<span class="glyphicon glyphicon-menu-right"></span>
</a>
</div>
<div>
<a type="button" class="btn btn-primary " style="margin-top: 10px" title="左移动"
onclick="moveSelected('selfRoles', 'allRoles')">
<span class="glyphicon glyphicon-menu-left"></span>
</a>
</div>
<div>
<a type="button" class="btn btn-primary " style="margin-top: 10px" title="全右移动"
onclick="moveAll('allRoles', 'selfRoles')">
<span class="glyphicon glyphicon-forward"></span>
</a>
</div>
<div>
<a type="button" class="btn btn-primary " style="margin-top: 10px" title="全左移动"
onclick="moveAll('selfRoles', 'allRoles')">
<span class="glyphicon glyphicon-backward"></span>
</a>
</div>
</div>
<div class="col-sm-2">
<select multiple class="form-control selfRoles" style="height: 350px;" size="15" name="roleIds">
<option th:each="role:${selfRoles}"
th:text="${role.name}"
th:value="${role.id}"></option>
</select>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-6">
<button type="submit" class="btn btn-primary btn-submit">保存</button>
<a href="javascript:window.history.back()" class="btn btn-danger">取消</a>
</div>
</div>
</form>
</div>
</section>
</div>
<div th:replace="common/fragment :: footer"></div>
</div>
<script>
// 全部左右移动
function moveAll(src, target) {
$('.' + target).append($('.' + src + ' > option'));
}
// 选中左右移动
function moveSelected(src, target) {
$('.' + target).append($('.' + src + ' > option:selected'));
}
// 点击保存按钮
/* $('.btn-submit').click(function () {
$('.selfRoles > option').prop('selected',true);
$('#editForm').submit();
})*/
$(function () {
// 下拉列表去重
var arr = $('.selfRoles > option').map(function (index, domEle) {
return $(domEle).val();
})
$('.allRoles > option').each(function (i, e) {
// 判断每次遍历的元素是否在上面的数组中出现过?
if($.inArray($(e).val(),arr) >= 0){
// 若存在则返回索引,说明重复了。我们要将该元素删除
$(e).remove();
}
})
// 回显超级管理员则需要隐藏下面的 div
if($('#admin').prop('checked')){
// 是选中状态,说明是超级管理员,需要隐藏下面角色部分
$('#roleDiv').addClass('hidden');
} else {
// 不是选中状态,说明不是超级管理员,需要显示下面角色部分
$('#roleDiv').removeClass('hidden')
}
})
// 点击超级管理员按钮隐藏角色部分
$('#admin').click(function () {
// 判断勾选状态
if($(this).prop('checked')){
// 是选中状态,说明是超级管理员,需要隐藏下面角色部分
$('#roleDiv').addClass('hidden');
} else {
// 不是选中状态,说明不是超级管理员,需要显示下面角色部分
$('#roleDiv').removeClass('hidden')
}
})
</script>
<script>
$("#editForm").bootstrapValidator({/*找到表单 调用插件*/
feedbackIcons: { // 图标
valid: 'glyphicon glyphicon-ok',/*对号图标*/
invalid: 'glyphicon glyphicon-remove',/*错号图标*/
validating: 'glyphicon glyphicon-refresh'/*转圈图标*/
},
fields:{ // 配置要验证的字段 开始验证
username:{
validators:{ // 验证的规则
//message:"用户名必填",
notEmpty:{ // 不能为空
message:"用户名必填" // 错误时的提示信息 若这一层没有message则向上找validators层没有则再次向上找message
},
stringLength: { // 字符串的长度范围
min: 1,
max: 10,
message:"用户名长度为1~5位"
},
regexp: {//正则表达式
regexp: /^[a-zA-Z0-9_\.]+$/,
message: '禁止使用非法字符'
},
remote:{//远程校验 验证用户是否存在 可以添加延迟减少访问数据库次数
type:'post',
url:'/employee/checkUsername',
message:'用户名已存在'
}
}
},
name:{
validators:{ // 验证的规则
notEmpty:{ // 不能为空
message:"姓名必填" // 错误时的提示信息
},
stringLength: { // 字符串的长度范围
min: 1,
max: 5,
message:"昵称长度为1~5位"
}
}
},
password:{
validators:{
notEmpty:{ // 不能为空
message:"密码必填" // 错误时的提示信息
},
different: {
field: 'username',
message: '用户名与密码不能相同'
}
}
},
repassword:{
validators:{
notEmpty:{ // 不能为空
message:"密码必填" // 错误时的提示信息
},
identical: {// 两个字段的值必须相同
field: 'password',
message: '两次输入的密码必须相同'
},
}
},
email: {
validators: {
emailAddress: {}, // 邮箱格式
message:"请输入正确邮箱"
}
},
age:{
validators: {
between: { // 数字的范围
min: 18,
max: 60,
message:"非法年龄范围"
}
}
}
}
}).on('success.form.bv', function(e) {
// 阻止表单提交 原先button的selected操作挪到了这里 依然选中后再提交 监听表单 点击submit才会进入到这里
e.preventDefault();
$('.selfRoles > option').prop('selected', 'true');
// TODO 这里可以改成用异步的方式提交表单
$.post(
"/employee/saveOrUpdate",
$('#editForm').serialize(),
/*通过serialize()获取表单所有数据将其转换为一条String语句*/
function (data) {/*只有状态码为200才能走进function 当200缺插入失败才叫插入失败 这里是成功之后的回调函数*/
if(data.success){
// 插入成功 展示页面
location.href='/employee/list'
} else {/*插入失败跳模式框*/
Swal.fire({
text: data.msg,
icon:'error',
confirmButtonText: '确定!',
});
}
}
)
});
</script>
</body>
</html>
3 验证添加的员工是否存在 – 用户名不能重复
① 前端代码
$("#editForm").bootstrapValidator({/*找到表单 调用插件*/
feedbackIcons: { // 图标
valid: 'glyphicon glyphicon-ok',/*对号图标*/
invalid: 'glyphicon glyphicon-remove',/*错号图标*/
validating: 'glyphicon glyphicon-refresh'/*转圈图标*/
},
fields:{ // 配置要验证的字段 开始验证
username:{
validators:{ // 验证的规则
//message:"用户名必填",
notEmpty:{ // 不能为空
message:"用户名必填"
},
stringLength: { // 字符串的长度范围
min: 1,
max: 10,
message:"用户名长度为1~5位"
},
regexp: {//正则表达式
regexp: /^[a-zA-Z0-9_\.]+$/,
message: '禁止使用非法字符'
},
remote:{//远程校验 验证用户是否存在 可以添加延迟减少访问数据库次数
type:'post',
url:'/employee/checkUsername',
message:'用户名已存在'
delay: 1000, // 输入内容延迟 1 秒后发请求
}
}
},
// 略
② 后端代码,插件要求返回结果需要为键值对形式 key 为 valid,value 为 boolean 类型(使用 Map 封装即可)
valid: true 代表验证通过(该用户名不存在)
valid: false 代表验证不通过(用户名已经存在)
// 检查用户名是否存在 (Controller)
@RequestMapping("/checkUsername")
@ResponseBody
public Map<String,Boolean> checkUsername(String username){
Map<String,Boolean> map = new HashMap<>();
Boolean flag = employeeService.checkUsername(username);
map.put("valid",flag);
return map;
}
// service
Boolean checkUsername(String username);
// impl
public Boolean checkUsername(String username) {
Integer count = employeeMapper.checkUsername(username);
return count==0;
}
③ Mapper,可以通过count()方法查询数据库中有无该用户名,有返回1,无返回0
// 接口
Integer checkUsername(String username);
// xml
<select id="checkUsername" resultType="java.lang.Integer">
select count(*) from employee where username=#{username}
</select>