Bootstrap4定制验证(CustomValidation)的简单实现⽅法如果你只是想知道如何boostrap form validation, 请直接点和
0. 关于Flask技术栈渲染⽹页上的⼀些碎碎念 a.k.a React定向安利
通常遇到⼀些⼩的页⾯需求,我们会⽤bootstrap搞定。不过,作为最基础的表单,⽤户输⼊的合法验证也是需要的。
过去,我通常的做法是⽤flask-wtf + flask-bootstrap,配合 quick_form宏在服务器端⽣成表单。到⽬前为⽌这依然是⼀个可⾏的选项,⽽且依旧很⽅便,主要问题在于flask-bootstrap并不⽀持 bootstrap4, 同时wtf的⽂档太丑, ⼀旦需要进⼀步customize, 你就要开始看源码, 然后去试jinja宏⾥提供的各种参数, 最后 fight against it。。真是惨痛回忆,想起来就头疼。。
下图是我看flask-bootstrap 时看到的@_@
于此同时,当下python技术栈的们的javascript⽔平也逐渐开始6了起来,那么,直接撸前端是在正常不过的事情了。我因⼯作需求搓了⼀个⼩React Native APP之后对React的技术栈就越发的喜爱起来,先不管前端各个框架的布道师们是如何定义React, Angular, Vue的, 就我这种玩票型选⼿⽽⾔React的JSX就是⼀个简洁灵活的界⾯描述语⾔,所有Jinja能⼲的事情都能⼲,⽽且能⼲的漂亮得多。平常,我每次写html+css都是很蛋疼的,有了JSX我就可以All in JavaScript.
最重要的,python的灵活性和react的灵活性相近, 也让我有⼗⾜的亲近感。
1. Bootstrap Validation 的原理
⾸先, 表单的验证按官⽹分为"client side"和"rver side",
官⽹对"client side"的理解为通过游览器的Validation API去验证字段的合法性, 然后在form标签上加上"was-validated"来展⽰validation内容。
对于这种⽅法,⼀个简单的例⼦如下:
<form><!-- 请尝试更换为 <form class="was-validated"> -->
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" required />
<div class="invalid-feedback">这是⼀个invalid-feedback</div>
</div>
</form>
本例⼦中给input元素加上了required属性, 因此游览器会主动地验证这个地⽅有没有填值。
当form标签没加was-validated,那么就不会去触发:invalid和:valid这两个css伪元素,
input外边框不会变成红⾊或者绿⾊, .invalid-feedback⾥的内容是隐藏的,如下:
然后如果游览器加上was-validated,因为<input required>,没填游览器就会主动触发:invalid, ,如下:
填了就是:valid, 如下:
如果你需要⾃⼰定义规则来触发:invalid和:valid元素, 那就需要很多了, 不过,与其在html5⾥的框架⾥跳舞,我还是想概念越少越好;),有兴趣的同学请点击上述链接扩展学习。
2. 本⽂的推荐实现⽅法
所以, 终于进⼊了本⽂的主要内容: rver-rendering。
长话短说, 直接在<input>标签上挂.is-invalid和.is-valid, 不⽤在form上toggle .was-validated class属性,就能显⽰该input的验证状态, 并且控制其相邻.invalid-feedback元素的显⽰开关。
初始代码:
<form>
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" value="whatever"/>
<div class="invalid-feedback">Server render invalid message</div>
</div>
</form>
<input type="text" class="form-control" value="whatever" />
不显⽰验证状态, 如下:
<input type="text" class="form-control is-invalid" value="whatever" />
显⽰invalid状态, 展⽰.invalid-feedback内容, 如下:
<input type="text" class="form-control is-valid" value="whatever" />
显⽰valid状态, 不展⽰.invalid-feedback内容, 如下:
OK, Get了这个知识点,那么恭喜诸位Flask同学已经知道如何rver-rendering了。
接下来介绍⼀下我⽤react实现的⼀个简单实现的Demo:
本例采⽤的是controlled components, 不了解的这个概念的请看。同时,由于bootstrap⾥的标准form做法是把input包在.form-group⾥, 这部分可以抽象成⼀个Dumb组件:
import classNames from 'classnames';
function FormGroupText({label, name, type = 'text', onChange, placeholder, value, validation = {}}) {
let id = `form-id-${name}`; // ⽤于label.for和input.id
return (
<div className="form-group">
<label htmlFor={id}>{label}</label>
<input
id={id}
type={type}
className={classNames('form-control', {'is-invalid': validation.status === fal}, {'is-valid': validation.status === true})}
name={name}
onChange={onChange}
placeholder={placeholder}
value={value}
/>
{
validation.msg &&
<div className="invalid-feedback">
{validation.msg}
</div>
}
</div>
);
}
上述代码中: 对函数参数语法不了解的同学可以看 jsx中我推荐⽤classnames库来⽣成className代替⼿动拼接,
form表单的组件代码如下:
import React, {Component} from 'react';
class App extends Component {
state = {
email: "", urname: "", password: "",
validation: {}
};
onInputChange = (e) => {
const {name, value} = e.target;
this.tState({[name]: value});
};
checkValidation = () => {
let validation = {};
if (ail === "" || !dsWith('')) {
return [fal, validation];
} el {
}
if (this.state.urname === "" || this.state.urname.length < 5) {
validation.urname = {status: fal, msg: '⽤户名字符必须⼤于5位'};
return [fal, validation];
} el {
validation.urname = {status: true};
}
if (this.state.password === "" || this.state.password.length < 6) {
validation.password = {status: fal, msg: '密码必须⼤于6位 '};
return [fal, validation];
} el {
validation.password = {status: true};
}
return [true, validation]
};
onSubmit = (e) => {
e.preventDefault(); // 阻⽌默认的提交的页⾯跳转⾏为
e.stopPropagation();
const [isValid, validation] = this.checkValidation();
this.tState({validation});
if (isValid) {
// Do ajax jobs
}
};
render() {
const {validation} = this.state;
return (
<div className="container">
<h2>Form Validation Demo</h2>
{/* form 加上 noValidate 来阻⽌默认的浏览器的验证tooltips*/}
<form
method='post'
onSubmit={Submit}
noValidate
>
<FormGroupText
label="Email"
type="email"
name="email"
value={ail}
placeholder=""
onChange={InputChange}
validation={ail}
/>
<FormGroupText
label="Ur Name"
type="text"
name="urname"
value={this.state.urname}
placeholder="aweffr"
onChange={InputChange}
validation={validation.urname}
/>
<FormGroupText
label="Password"
type="password"
name="password"
value={this.state.password}
placeholder="Plea Input Password"
onChange={InputChange}
validation={validation.password}
/
>
<button type="submit" className="btn btn-block btn-primary">
Submit
</button>
</form>
</div>
);
}
}
上述demo的运⾏效果如下:
上述代码中:
checkValidation⽅法为具体的验证字段的逻辑
在实现上,我在每次验证⼀个字段后,如果invalid就直接return,是因为觉得这样对⽤户⽐较友好,这样⽤户不会⼀提交就满屏幕的红⾊:)
每个FormGroupText组件中的status来⾃于表单的state.validation, 如email字段的属性位于this.ail.status status: undefined => 未验证, className='form-control'
status: true => 合法, className='form-control is-valid'
status: fal => 不合法, className='form-control is-invalid'
onInputChange中{[name]: value}这个语法请参考
Ctrl + F5 Computed property names (ES2015)
3. More
当然, 这个例⼦⾥的validation实现⽅法还是很粗糙的。更进⼀步,严肃的项⽬上我们会去验证某个字段是不是必须是数字,是不是只能含有中⽂,是不是不能包含特殊字符,以及输⼊长度验证和密码强
度验证。这就要求把validation rules配置化。这⽅⾯我⽤过的,体验⾮常好的是⼤哥级组件库antd。有兴趣的同学可以去
和它的具体实现做进⼀步学习,
他们把验证的部分解耦到了上,⽤起来挺顺⼿的, 尤其他们开发时划分表单problem的⽅法和思路尤其值得学习。
不过,⾄于如何上⼿antd,以及其官⽅⼤佬所实现的脚⼿架,乃⾄react状态管理摊开来讲就⼜是⼀篇博客了。To Be Continued.