• 表单校验
    • 基本的示例
    • 使用自定义校验
    • 另一个自定义校验的例子
    • 服务端校验
    • 其它替代模式

    表单校验

    基本的示例

    Watch a free lesson on Vue School

    表单校验是浏览器原生支持的,但是有的时候用不同的浏览器处理起来需要一些小技巧。即使当表单校验已经被完美支持,你也还是有很多时候需要进行自定义的校验。这时一个更加手动的基于 Vue 的解决方案可能会更适合。我们来看一个简单的示例。

    给定一个表单,包含三个字段,其中两个是必填项。我们先来看看 HTML:

    1. <form
    2. id="app"
    3. @submit="checkForm"
    4. action="https://vuejs.org/"
    5. method="post"
    6. >
    7. <p v-if="errors.length">
    8. <b>Please correct the following error(s):</b>
    9. <ul>
    10. <li v-for="error in errors">{{ error }}</li>
    11. </ul>
    12. </p>
    13. <p>
    14. <label for="name">Name</label>
    15. <input
    16. id="name"
    17. v-model="name"
    18. type="text"
    19. name="name"
    20. >
    21. </p>
    22. <p>
    23. <label for="age">Age</label>
    24. <input
    25. id="age"
    26. v-model="age"
    27. type="number"
    28. name="age"
    29. min="0"
    30. >
    31. </p>
    32. <p>
    33. <label for="movie">Favorite Movie</label>
    34. <select
    35. id="movie"
    36. v-model="movie"
    37. name="movie"
    38. >
    39. <option>Star Wars</option>
    40. <option>Vanilla Sky</option>
    41. <option>Atomic Blonde</option>
    42. </select>
    43. </p>
    44. <p>
    45. <input type="submit" value="Submit">
    46. </p>
    47. </form>

    我们从头到尾看一遍,这个 <form> 标记上有一个我们将会用在 Vue 组件上的 ID。这里有一个你稍后会看到的 submit 处理函数,而这里的 action 是一个可能指向了某个真实服务器的临时 URL (当然你在服务端也是要有校验的)。

    下面有一段内容,会根据错误状态进行显示或隐藏。它将会在表单的最顶端渲染一个简单的错误列表。同时要注意我们会在提交的时候进行校验,而不是每个字段被修改的时候。

    最后值得注意的是这三个字段都有一个对应的 v-model 来连接它们的值,我们将会在 JavaScript 中使用它。现在我们就来看一下。

    1. const app = new Vue({
    2. el: '#app',
    3. data: {
    4. errors: [],
    5. name: null,
    6. age: null,
    7. movie: null
    8. },
    9. methods:{
    10. checkForm: function (e) {
    11. if (this.name && this.age) {
    12. return true;
    13. }
    14. this.errors = [];
    15. if (!this.name) {
    16. this.errors.push('Name required.');
    17. }
    18. if (!this.age) {
    19. this.errors.push('Age required.');
    20. }
    21. e.preventDefault();
    22. }
    23. }
    24. })

    非常短小精悍。我们定义了一个数组来放置错误,并将这三个表单字段的默认值设为 nullcheckForm 的逻辑 (在表单提交时运行) 只会检查姓名和年龄,因为电影是选填的。如果它们是空的,那么我们会检查每一个字段并设置相应的错误,差不多就是这样。你可以在下面运行这个 demo。不要忘记提交成功时它会 POST 到一个临时的 URL。

    在 CodePen 查看 Raymond Camden (@cfjedimaster) 的 表单校验 1。

    使用自定义校验

    对于第二个示例来说,第二个文本字段 (年龄) 变换成了电子邮件地址,它将会通过一些自定义的逻辑来校验。这部分代码来自 StackOverflow 的问题:如何在 JavaScript 中校验电子邮件地址。这是一个很好的问题,因为它会让 Facebook 上最激烈的政治、宗教争论看上去都只是“哪家的啤酒最好喝”这样的小分歧了。讲真的这很疯狂。我们来看 HTML,尽管它和第一个例子很接近。

    1. <form
    2. id="app"
    3. @submit="checkForm"
    4. action="https://vuejs.org/"
    5. method="post"
    6. novalidate="true"
    7. >
    8. <p v-if="errors.length">
    9. <b>Please correct the following error(s):</b>
    10. <ul>
    11. <li v-for="error in errors">{{ error }}</li>
    12. </ul>
    13. </p>
    14. <p>
    15. <label for="name">Name</label>
    16. <input
    17. id="name"
    18. v-model="name"
    19. type="text"
    20. name="name"
    21. >
    22. </p>
    23. <p>
    24. <label for="email">Email</label>
    25. <input
    26. id="email"
    27. v-model="email"
    28. type="email"
    29. name="email"
    30. >
    31. </p>
    32. <p>
    33. <label for="movie">Favorite Movie</label>
    34. <select
    35. id="movie"
    36. v-model="movie"
    37. name="movie"
    38. >
    39. <option>Star Wars</option>
    40. <option>Vanilla Sky</option>
    41. <option>Atomic Blonde</option>
    42. </select>
    43. </p>
    44. <p>
    45. <input
    46. type="submit"
    47. value="Submit"
    48. >
    49. </p>
    50. </form>

    尽管这里的不同点很小,注意顶端的 novalidate="true"。但是这很重要,因为浏览器会尝试在 type="email" 的字段校验邮件地址。坦白说在这个案例中浏览器的校验规则是值得信任的,不过我们想要创建一个自定义校验的例子,所以把它禁用了。以下是更新后的 JavaScript。

    1. const app = new Vue({
    2. el: '#app',
    3. data: {
    4. errors: [],
    5. name: null,
    6. email: null,
    7. movie: null
    8. },
    9. methods: {
    10. checkForm: function (e) {
    11. this.errors = [];
    12. if (!this.name) {
    13. this.errors.push("Name required.");
    14. }
    15. if (!this.email) {
    16. this.errors.push('Email required.');
    17. } else if (!this.validEmail(this.email)) {
    18. this.errors.push('Valid email required.');
    19. }
    20. if (!this.errors.length) {
    21. return true;
    22. }
    23. e.preventDefault();
    24. },
    25. validEmail: function (email) {
    26. var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    27. return re.test(email);
    28. }
    29. }
    30. })

    如你所见,我们添加了一个新方法 validEmail,它将会在 checkForm 中被调用了。我们现在可以这样运行示例:

    在 CodePen 查看 Raymond Camden (@cfjedimaster) 的 表单校验 2。

    另一个自定义校验的例子

    在第三个示例中,我们已经构建了一些你可能在一些调研类应用中见过的东西。用户需要花掉“预算”来为歼星舰模型装配一套部件。总价必须等于 100。先看 HTML。

    1. <form
    2. id="app"
    3. @submit="checkForm"
    4. action="https://vuejs.org/"
    5. method="post"
    6. novalidate="true"
    7. >
    8. <p v-if="errors.length">
    9. <b>Please correct the following error(s):</b>
    10. <ul>
    11. <li v-for="error in errors">{{ error }}</li>
    12. </ul>
    13. </p>
    14. <p>
    15. Given a budget of 100 dollars, indicate how much
    16. you would spend on the following features for the
    17. next generation Star Destroyer. Your total must sum up to 100.
    18. </p>
    19. <p>
    20. <input
    21. v-model.number="weapons"
    22. type="number"
    23. name="weapons"
    24. > Weapons <br/>
    25. <input
    26. v-model.number="shields"
    27. type="number"
    28. name="shields"
    29. > Shields <br/>
    30. <input
    31. v-model.number="coffee"
    32. type="number"
    33. name="coffee"
    34. > Coffee <br/>
    35. <input
    36. v-model.number="ac"
    37. type="number"
    38. name="ac"
    39. > Air Conditioning <br/>
    40. <input
    41. v-model.number="mousedroids"
    42. type="number"
    43. name="mousedroids"
    44. > Mouse Droids <br/>
    45. </p>
    46. <p>
    47. Current Total: {{total}}
    48. </p>
    49. <p>
    50. <input
    51. type="submit"
    52. value="Submit"
    53. >
    54. </p>
    55. </form>

    这组输入框覆盖了五个不同的部件。注意这里为 v-model 特性添加了 .number。它会告诉 Vue 将其值作为数字来使用。不过这里有一个小小的 bug,那就是当其值为空的时候,它会回到字符串格式,稍后你将会看到变通的办法。为了让用户使用起来更方便,我们添加展示了一个当前的总和,这样我们就能够实时的看到它们一共花掉了多少钱。现在我们来看看 JavaScript。

    1. const app = new Vue({
    2. el: '#app',
    3. data:{
    4. errors: [],
    5. weapons: 0,
    6. shields: 0,
    7. coffee: 0,
    8. ac: 0,
    9. mousedroids: 0
    10. },
    11. computed: {
    12. total: function () {
    13. // 必须解析,因为 Vue 会将空值转换为字符串
    14. return Number(this.weapons) +
    15. Number(this.shields) +
    16. Number(this.coffee) +
    17. Number(this.ac+this.mousedroids);
    18. }
    19. },
    20. methods:{
    21. checkForm: function (e) {
    22. this.errors = [];
    23. if (this.total != 100) {
    24. this.errors.push('Total must be 100!');
    25. }
    26. if (!this.errors.length) {
    27. return true;
    28. }
    29. e.preventDefault();
    30. }
    31. }
    32. })

    我们将总和设置为了一个计算属性,从那个我们解决掉的 bug 外面看上去,这已经足够了。我的 checkForm 方法现在只需要关注总和是不是 100 了。你可以在这里试用:

    在 CodePen 查看 Raymond Camden (@cfjedimaster) 的 表单校验3。

    服务端校验

    在我们最终的示例中,我们构建了一些用到 Ajax 的服务端校验的东西。这个表单将会问你为一个新产品起名字,并且将会确保这个名字是唯一的。我们快速写了一个 OpenWhisk 的 serverless action 来进行这个校验。虽然这不是非常重要,但其逻辑如下:

    1. function main(args) {
    2. return new Promise((resolve, reject) => {
    3. // 不好的产品名:vista, empire, mbp
    4. const badNames = ['vista', 'empire', 'mbp'];
    5. if (badNames.includes(args.name)) {
    6. reject({error: 'Existing product'});
    7. }
    8. resolve({status: 'ok'});
    9. });
    10. }

    基本上除了“vista”、“empire”和“mbp”的名字都是可以接受的。好,让我们来看看表单。

    1. <form
    2. id="app"
    3. @submit="checkForm"
    4. method="post"
    5. >
    6. <p v-if="errors.length">
    7. <b>Please correct the following error(s):</b>
    8. <ul>
    9. <li v-for="error in errors">{{ error }}</li>
    10. </ul>
    11. </p>
    12. <p>
    13. <label for="name">New Product Name: </label>
    14. <input
    15. id="name"
    16. v-model="name"
    17. type="text"
    18. name="name"
    19. >
    20. </p>
    21. <p>
    22. <input
    23. type="submit"
    24. value="Submit"
    25. >
    26. </p>
    27. </form>

    这里没有任何特殊的东西。接下来我们再看看 JavaScript。

    1. const apiUrl = 'https://openwhisk.ng.bluemix.net/api/v1/web/rcamden%40us.ibm.com_My%20Space/safeToDelete/productName.json?name=';
    2. const app = new Vue({
    3. el: '#app',
    4. data: {
    5. errors: [],
    6. name: ''
    7. },
    8. methods:{
    9. checkForm: function (e) {
    10. e.preventDefault();
    11. this.errors = [];
    12. if (this.name === '') {
    13. this.errors.push('Product name is required.');
    14. } else {
    15. fetch(apiUrl + encodeURIComponent(this.name))
    16. .then(res => res.json())
    17. .then(res => {
    18. if (res.error) {
    19. this.errors.push(res.error);
    20. } else {
    21. // 在成功的时候重定向到一个新的 URL 或做一些别的事情
    22. alert('ok!');
    23. }
    24. });
    25. }
    26. }
    27. }
    28. })

    我们从一个运行在 OpenWhisk 的 API 的 URL 变量开始。现在注意 checkForm。在这个版本中,我们始终阻止了表单的提交 (当然,它也可以通过 Vue 在 HTML 中完成)。你可以看到一个基本的校验,即 this.name 是否为空,然后我们请求这个 API。如果名字是无效的,我们就添加一个错误。如果是有效的,我们就不做任何事 (只是一个 alert),但是你可以引导用户去一个新页面,在 URL 中带上产品的名字,或者其它行为。接下来你可以体验这个 demo:

    在 CodePen 查看 Raymond Camden (@cfjedimaster) 的 表单校验4。

    其它替代模式

    这份秘笈专注在“手动”校验表单,当然一些非常棒的 Vue 的库会为你搞定这些事情。使用一些预打包的库可能会影响你的应用最终的体积,但是好处是非常多的。这里有经过充分测试且保持日常更新的代码。其中包括以下 Vue 的表单校验库:

    • vuelidate
    • VeeValidate