七、添加 Vue 表单

在本章中,您将使用v-modelv-bind创建表单。您将学习在将表单数据发送到服务器之前在客户端验证表单。您将使用基本元素创建表单,绑定动态值并使用修饰符修改输入元素的行为。您还将学习使用vee-validate插件验证表单并将其应用于 Nuxt 应用。在本章中,学习和理解如何将v-modelv-bind与 Vue 表单一起使用非常重要,因为我们将在接下来的章节中使用表单,例如第 10 章添加 Vuex 商店第 12 章创建用户登录和 API 验证中。

我们将在本章中介绍的主题如下:

  • 理解v-model
  • 使用基本数据绑定验证表单
  • 创建动态值绑定
  • 使用vee-validate验证表格
  • 在 Nuxt 中应用 Vue 表单

理解 v 模型

v-model是一个 Vue 指令(自定义内置的 Vue HTML 属性),允许我们在表单的inputtextareaselect元素上创建双向绑定。可以将表单输入与 Vue 数据绑定,以便在用户与输入字段交互时更新数据。v-model将始终跳过您在表单元素上设置的初始值,但将 Vue 数据视为真相的来源。因此,您应该在 Vue 端的data选项或函数中声明初始值。

v-model将根据输入类型选择适当的方式更新元素,这意味着如果您在带有type="text"的表单输入中使用它,它将使用value作为属性,input作为事件为您执行双向绑定。在接下来的章节中,让我们看一下该指令下的内容。

在 text 和 textarea 元素中使用 v-model

还记得我们在第 5 章添加 Vue 组件中通过v-model实现的创建自定义输入组件的双向绑定吗?在该章的创建自定义输入组件一节中,我们了解到输入的v-model语法—<input v-model="username">是以下内容的简写:

<input
  v-bind:value="username"
  v-on:input="username = $event.target.value"
>

这个文本input元素在幕后绑定了value属性,该属性从处理程序username获取值,该处理程序从input事件获取其值。因此,自定义文本输入组件也必须始终在model属性中使用value属性和input事件,如下所示:

Vue.component('custom-input', {
  props: {
    value: String
  },
  model: {
    prop: 'value',
    event: 'input'
  },
  template: `<input v-on:input="$emit('input', $event.target.value)">`,
})

这只是因为v-model输入的性质是由v-bind:valuev-on:input组成的。在textarea元素中使用v-model指令时也是如此,如下例所示:

<textarea v-model="message"></textarea>

v-model``textarea元素是以下内容的简写:

<textarea
  v-bind:value="message"
  v-on:input="message = $event.target.value"
></textarea>

这个textarea输入元素在幕后绑定了value属性,该属性从处理程序message获取值,该处理程序从input事件获取其值。因此,自定义textarea组件也必须始终符合v-model``textarea元素的性质,使用value属性中的value属性和input事件,如下所示:

Vue.component('custom-textarea', {
  props: {
    value: null
  },
  model: {
    prop: 'value',
    event: 'input'
  }
})

简言之,v-modeltextinput元素和v-model``textarea输入元素总是将value属性与处理程序绑定,以获取输入事件上的新值,通过采用相同的属性和事件,自定义输入组件也必须如此。那么复选框和单选按钮元素中的v-model呢?让我们在下一节深入探讨它们。

在复选框和单选元素中使用 v 型

另一方面,v-model``checkboxradio按钮输入元素总是将checked属性与在change事件中更新的布尔值绑定,如下例所示:

<input type="checkbox" v-model="subscribe" value="yes" name="subscribe">

前面代码片段中的v-model``checkbox输入元素实际上是以下内容的简写:

<input
  type="checkbox"
  name="subscribe"
  value="yes"
  v-bind:checked="false"
  v-on:change="subscribe = $event.target.checked"
>

因此,自定义复选框输入元素也必须始终符合v-model复选框输入元素的性质(如前面的代码块所示),方法是采用model属性中的checked属性和change事件,如下所示:

Vue.component('custom-checkbox', {
  props: {
    checked: Boolean,
  },
  model: {
    prop: 'checked',
    event: 'change'
  }
})

同样适用于v-model单选按钮输入元素,如下所示:

<input type="radio" v-model="answer" value="yes" name="answer">

前面的v-model元素是以下内容的另一个缩写:

<input
  type="radio"
  name="answer"
  value="yes"
  v-bind:checked="answer == 'yes'"
  v-on:change="answer = $event.target.value"
>

因此,自定义单选按钮输入元素也必须始终符合v-model元素的性质,如下所示:

Vue.component('custom-radio', {
  props: {
    checked: String,
    value: String
  },
  model: {
    prop: 'checked',
    event: 'change'
  }
})

简而言之,由于v-model``checkboxradio按钮输入元素总是绑定value属性,并在change事件中更新,因此必须采用相同属性和事件的自定义输入组件。现在,让我们来看看 Apple T5 如何在下一节中的元素 T6 中工作。

在“选择图元”中使用 v 模型

毫不奇怪,v-modelselect输入元素总是将value属性与处理程序绑定,以获取其在change事件上的选定值,如下例所示:

<select
  v-model="favourite"
  name="favourite"
>
  //...
</select>

前面的v-model``checkbox输入元素只是以下内容的另一个缩写:

<select
  v-bind:value="favourite"
  v-on:change="favourite = $event.target.value"
  name="favourite"
>
  //...
</select>

因此,自定义的checkbox输入元素也必须始终符合v-model元素的性质,使用value属性和model属性中的change事件,如下所示:

Vue.component('custom-select', {
  props: {
    value: String
  },
  model: {
    prop: 'value',
    event: 'change'
  }
})

如您所见,v-modelv-bind之上的语法糖,它将值绑定到标记,v-on更新用户输入事件的数据,可以是changeinput事件。简而言之,v-modelv-bindv-on结合在一起,但作为 Vue/Nuxt 应用开发人员,理解语法背后的含义很重要。

You can find the examples we've covered in this section in /chapter-7/vue/html/ in our GitHub repository.

既然您已经发现了v-model指令在表单的输入元素中是如何工作的,那么让我们在表单上使用这些v-model元素,并在下一节中验证它们。

使用基本数据绑定验证表单

表格是收集信息的文件。HTML<form>元素是一种可以从 web 用户处收集数据或信息的表单。此元素需要其中的<input>元素来指定我们要收集的数据。但在接受数据之前,我们通常希望验证和过滤数据,以便从用户那里获得真实和正确的数据。

Vue 允许我们轻松地验证来自v-model输入元素的数据,因此让我们从单文件组件(SFC)Vue 应用和网页包开始,您在第 5 章添加 Vue 组件以及使用网页包编译单文件组件部分了解了这一点。首先,我们将创建一个非常简单的表单,其中包含一个submit按钮和用于在<template>块中显示错误消息的标记,如下所示:

// src/components/basic.vue
<form v-on:submit.prevent="checkForm" action="/" method="post">
  <p v-if="errors.length">
    <b>Please correct the following error(s):</b>
    <ul>
      <li v-for="error in errors">{{ error }}</li>
    </ul>
  </p>
  <p>
    <input type="submit" value="Submit">
  </p>
</form>

稍后我们将在<form>中添加其余的输入元素。现在,让我们建立基本结构并了解我们需要什么。默认情况下,我们使用v-on:submit.prevent阻止浏览器发送表单数据,因为我们将在<script>块的 Vue 实例中使用checkForm方法处理提交:

// src/components/basic.vue
export default {
  data () {
    return {
      errors: [],
      form: {...}
    }
  },
  methods:{
    checkForm (e) {
      this.errors = []
      if (!this.errors.length) {
        this.processForm(e)
      }
    },
    processForm (e) {...}
  }
}

在 JavaScript 方面,我们定义了一个数组来保存验证过程中可能出现的错误。checkForm逻辑验证我们将在本节后面添加的必需字段。如果必填字段未通过验证,则我们将错误消息推送到errors。当表单填写正确并且/或者没有发现错误时,它将被传递到processForm逻辑,在那里我们可以在将表单数据发送到服务器之前进一步处理表单数据。

验证文本元素

让我们首先为单行文本添加一个<input>元素:

// src/components/basic.vue
<label for="name">Name</label>
<input v-model="form.name" type="text">

export default {
  data () {
    return {
      form: { name: null }
    }
  },
  methods:{
    checkForm (e) {
      this.errors = []
      if (!this.form.name) {
        this.errors.push('Name required')
      }
    }
  }
}

<script>块中,我们在data函数中定义了一个name属性,该属性保存初始null值,并将根据<input>元素的input事件进行更新。当您点击submit按钮时,我们验证if条件块中的name数据;如果没有提供数据,则我们push将错误消息发送到errors

验证 textarea 元素

我们要添加的下一个元素是多行文本的<textarea>,其工作方式与<input>相同:

// src/components/basic.vue
<label for="message">Message</label>
<textarea v-model="form.message"></textarea>

export default {
  data () {
    return {
      form: { message: null }
    }
  },
  methods:{
    checkForm (e) {
      this.errors = []
      if (!this.form.message) {
        this.errors.push('Message required')
      }
    }
  }
}

<script>块中,我们在data函数中定义了一个message属性,该属性保存初始null值,并将根据<textarea>元素的input事件进行更新。当您点击submit按钮时,我们验证if条件块中的message数据;如果没有提供数据,则我们push将错误消息发送到errors

验证复选框元素

下一个元素是一个复选框<input>元素,它将保存默认的布尔值:

// src/components/basic.vue
<label class="label">Subscribe</label>
<input type="checkbox" v-model="form.subscribe">

export default {
  data () {
    return {
      form: { subscribe: false }
    }
  },
  methods:{
    checkForm (e) {
      this.errors = []
      if (!this.form.subscribe) {
        this.errors.push('Subscription required')
      }
    }
  }
}

我们还将添加以下绑定到同一数组的多个复选框<input>元素,即books: []

// src/components/basic.vue
<input type="checkbox" v-model="form.books" value="On the Origin of Species">
<label for="On the Origin of Species">On the Origin of Species</label>

<input type="checkbox" v-model="form.books" value="A Brief History of Time">
<label for="A Brief History of Time">A Brief History of Time</label>

<input type="checkbox" v-model="form.books" value="The Selfish Gene">
<label for="The Selfish Gene">The Selfish Gene</label>

export default {
  data () {
    return {
      form: { books: [] }
    }
  },
  methods:{
    checkForm (e) {
      this.errors = []
      if (this.form.books.length === 0) {
        this.errors.push('Books required')
      }
    }
  }
}

<script>块中,我们在data函数中定义了一个subscribe属性,该属性保存初始布尔值false,并将在复选框<input>元素的change事件中更新。当您点击submit按钮时,我们验证if条件块中的subscribe数据;如果未提供数据或为false,则我们push将错误消息发送至errors

对于多个 checkbox<input>元素,我们也会定义一个books属性,该属性保存初始空数组,并将在 checkbox<input>元素的change事件中更新。验证if条件块中的books数据;如果其长度为0,则我们push将错误消息发送至errors

验证无线电元件

下一个元素是绑定到相同属性名的多个单选按钮<input>元素,即gender

// src/components/basic.vue
<label for="male">Male</label>
<input type="radio" v-model="form.gender" value="male">

<label for="female">Female</label>
<input type="radio" v-model="form.gender" value="female">

<label for="other">Other</label>
<input type="radio" v-model="form.gender" value="other">

export default {
  data () {
    return {
      form: { gender: null }
    }
  },
  methods:{
    checkForm (e) {
      this.errors = []
      if (!this.form.gender) {
        this.errors.push('Gender required')
      }
    }
  }
}

<script>块中,我们在data函数中定义了一个gender属性,该属性保存初始null值,并将在所选<input>单选按钮元素的change事件中更新。当您点击submit按钮时,我们验证if条件块中的gender数据。如果没有提供数据,则我们push将错误消息发送到errors

验证所选元素

下一个元素是单个<select>元素和多个<option>元素,如下所示:

// src/components/basic.vue
<select v-model="form.favourite">
  <option disabled value="">Please select one</option>
  <option value="On the Origin of Species">On the Origin of 
   Species</option>
  <option value="A Brief History of Time">A Brief History of Time</option>
  <option value="The Selfish Gene">The Selfish Gene</option>
</select>

export default {
  data () {
    return {
      form: { favourite: null }
    }
  },
  methods:{
    checkForm (e) {
      this.errors = []
      if (!this.form.favourite) {
        this.errors.push('Favourite required')
      }
    }
  }
}

最后一个是多个<select>元素,其中多个<option>元素绑定到同一个Array,即favourites: []

// src/components/basic.vue
<select v-model="form.favourites" multiple >
  <option value="On the Origin of Species">On the Origin of 
   Species</option>
  <option value="A Brief History of Time">A Brief History of Time</option>
  <option value="The Selfish Gene">The Selfish Gene</option>
</select>

export default {
  data () {
    return {
      form: { favourites: [] }
    }
  },
  methods:{
    checkForm (e) {
      this.errors = []
      if (this.form.favourites.length === 0) {
        this.errors.push('Favourites required')
      }
    }
  }
}

<script>块中,我们在data函数中定义了一个favourites属性,该属性保存初始null值,并将根据<select>元素的change事件进行更新。当您点击submit按钮时,我们验证if条件块中的favourites数据。如果没有提供数据,则我们push将错误消息发送到errors。对于多个<select>元素,我们也可以通过定义一个favourites属性来执行相同的操作,该属性保存初始空数组,并将在<select>元素的change事件中进行更新。验证if条件块中的favourites数据;如果其长度为0,则我们将错误消息推送到errors

然后我们将使用processForm逻辑完成此表单,只有在checkForm逻辑中没有发现错误时才会调用此表单。我们使用 Node.js 包qsthis.form对象进行字符串化,以便我们可以将数据以以下格式发送到服务器:

name=John&message=Hello%20World&subscribe=true&gender=other

让我们用 npm 安装qs

$ npm i qs

然后我们可以按如下方式使用它:

import axios from 'axios'
import qs from 'qs'

processForm (e) {
  var data = qs.stringify(this.form)
  axios.post('../server.php', data)
  .then((response) => {
    // success callback
  }, (response) => {
    // error callback
  })
}

我们使用axios发送数据并从服务器获取响应(通常为 JSON 格式),然后您可以对响应数据进行处理,例如在服务器端显示“成功”或“失败”消息。

For more information about qs, visit https://www.npmjs.com/package/qs, and for axios, please visit https://github.com/axios/axios.

You can find the preceding example app in /chapter-7/vue/webpack/ in our GitHub repository.

但是,我们还没有完全完成,因为我们有时可能希望将动态值绑定到表单输入,而不是从v-model获取默认值。例如,在我们的示例应用中,我们仅通过单个复选框<input>元素获取subscribe属性的布尔值,但我们希望使用带有yesno的字符串值。在下一节中,我们将探讨如何更改默认值。

创建动态值绑定

在上一节的示例应用中,我们单独使用v-model获取radiocheckboxselect选项的字符串或布尔值。我们可以使用true-valuefalse-valuev-bind更改此默认值。让我们潜水吧。

替换布尔-复选框元素

我们可以使用true-valuefalse-value将自定义值绑定到单个checkbox元素。例如,我们可以绑定yes值,用true-value替换默认true布尔值,用false-value替换默认false布尔值:

// src/components/dynamic-values.vue
<input
  type="checkbox"
  v-model="form.subscribe"
  true-value="yes"
  false-value="no"
>

export default {
  data () {
    return {
      form: { subscribe: 'no' }
    }
  },
  methods:{
    checkForm (e) {
      this.errors = []
      if (this.form.subscribe !== 'yes') {
        this.errors.push('Subscription required')
      }
    }
  }
}

现在,当您将subscribe输入的值发送到服务器时,您会得到yesno的响应。在<script>块中,我们现在将no声明为subscribe属性的初始值,并在if条件块中验证它,以确保当您点击submit按钮时它始终是yes,否则我们将错误消息推到errors

用动态属性替换字符串–单选元素

对于单选按钮<input>元素,我们可以使用v-bind将其值绑定到 Vue 实例中的动态属性:

// src/components/dynamic-values.vue
<input type="radio" v-model="form.gender" v-bind:value="gender.male">

export default {
  data () {
    return {
      gender: {
        male: 'm',
        female: 'f',
        other: 'o',
      },
      form: { gender: null }
    }
  }
}

现在,当选中此单选按钮且验证与之前相同时,您将获得m

用对象替换字符串–选择选项元素

我们也可以使用v-bind非字符串值,例如Object来进行表单输入。请参见以下示例:

// src/components/dynamic-values.vue
<select v-model="form.favourite">
  <option v-bind:value="{ title: 'On the Origin of Species' }">On 
   the Origin of Species</option>
</select>

export default {
  data () {
    return {
      form: {
        favourite: null
      }
    }
  }
}

现在选择此选项后,typeof this.favourite得到objectthis.favourite.title得到On the Origin of Species。验证逻辑没有变化。

我们还可以使用动态值和v-for动态呈现<option>元素:

// src/components/dynamic-values.vue
<select v-model="form.favourites" name="favourites_array[]" multiple >
  <option v-for="book in options.books" v-bind:value="book.value">
    {{ book.text }}
  </option>
</select>

data () {
  return {
    form: { favourites: [] },
    options: {
      books: [
        { value: { title: 'On the Origin of Species' }, text: 'On the 
         Origin of Species'},
        { value: { title: 'A Brief History of Time' }, text: 'A Brief 
         History of Time'},
        { value: { title: 'The Selfish Gene' }, text: 'The Selfish Gene'}
      ]
    }
  }
}

现在我们不必再硬编码<option>元素了。我们可以从其他地方提取books数据,比如 API。

除了绑定动态值以形成输入外,我们还可以修改输入元素上的v-model的默认行为。例如,我们可以在数据上使用change事件,而不是将输入与数据同步。让我们在下一个主题中了解如何做到这一点。

使用修饰语

Vue 提供了这三个修饰符,.lazy.number.trim,我们可以使用它们与v-model一起更改默认事件或向表单输入添加额外功能。让我们潜水吧。

添加。懒惰

我们可以使用.lazyv-modelinput事件更改为<input><textarea>元素上的change事件。请参见以下示例:

// src/components/modifiers.vue
<input v-model.lazy="form.name" type="text">

现在与数据的输入在change之后同步,而不是默认的input事件。

添加.编号

我们可以使用.numberv-model<input>元素上用type="number"string的默认类型转换更改为number。请参见以下示例:

// src/components/modifiers.vue
<input v-model.number="form.age" type="number">

现在,您可以在没有.number的情况下,为typeof this.form.age获得number而不是string

添加。修剪

我们可以使用.trimv-model来删除用户输入中的空白。请参见以下示例:

// src/components/modifiers.vue
<textarea v-model.lazy.trim="form.message"></textarea>

现在,来自用户的文本将自动修剪。文本开头和结尾的任何额外空白都将被删除。

虽然编写自定义验证逻辑是可能的,但是已经有一个很好的插件可以帮助轻松验证输入并显示相应的错误。该插件称为 VeeValidate,是一个基于模板的 Vue 验证框架。让我们在下一节中了解如何利用此插件。

使用 VeeValidate 验证表单

使用 VeeValidate,我们将使用 VeeValidate 的组件来验证 HTML 表单和 Vue 的作用域插槽以公开错误消息。例如,这是一个我们已经熟悉的v-model输入元素:

<input v-model="username" type="text" />

如果要使用 VeeValidate 验证,只需使用<ValidationProvider>组件包装输入:

<ValidationProvider name="message" rules="required" v-slot="{ errors }">
  <input v-model="username" name="username" type="text" />
  <span>{{ errors[0] }}</span>
</ValidationProvider>

通常,我们使用<ValidationProvider>组件来验证<input>元素。我们可以使用rules属性将验证规则附加到此组件,并使用v-slot指令显示错误。让我们通过以下步骤了解如何利用此插件加快验证过程:

  1. 使用 npm 安装 VeeValidate:
$ npm i vee-validate
  1. /src/目录中创建一个.js文件,并使用 VeeValidate 中的extend功能添加规则:
// src/vee-validate.js
import { extend } from 'vee-validate'
import { required } from 'vee-validate/dist/rules'

extend('required', {
  ...required,
  message: 'This field is required'
})

VeeValidate 在单独的捆绑包中提供了许多内置的验证规则,如requiredemailminregex等,因此我们可以导入我们的应用只需要的特定规则。因此在前面的代码中,我们导入了required规则并通过extend函数进行安装,然后在message属性中添加自定义消息。

  1. /src/vee-validate.js导入启动 Vue 实例的主入口文件:
// src/main.js
import Vue from 'vue'
import './vee-validate'
  1. ValidationProvider组件本地导入页面,并开始验证该页面上的输入字段:
// src/components/vee-validation.vue
<ValidationProvider name="name" rules="required|min:3" v-slot="{ errors }">
  <input v-model.lazy="name" type="text" name="name">
  <span>{{ errors[0] }}</span>
</ValidationProvider>

import { ValidationProvider } from 'vee-validate'

export default {
  components: {
    ValidationProvider
  }
}

我们也可以在/src/main.js/src/plugins/vee-validate.js中全局注册ValidationProvider

import Vue from 'vue'
import { ValidationProvider, extend } from 'vee-validate'

Vue.component('ValidationProvider', ValidationProvider)

但是,如果你不需要在应用的每个页面上都使用这个组件,那么这可能不是一个好主意。因此,如果您只需要在页面上使用它,请在本地导入它。

  1. 本地导入ValidationObserver组件,并将passes对象添加到v-slot指令中。因此,让我们从步骤 4重构JavaScript 代码,如下所示:
// src/components/vee-validation.vue
<ValidationObserver v-slot="{ passes }">
  <form v-on:submit.prevent="passes(processForm)" novalidate="true">
    //...
    <input type="submit" value="Submit">
  </form>
</ValidationObserver>

import {
  ValidationObserver,
  ValidationProvider
} from 'vee-validate'

export default {
  components: {
    ValidationObserver,
    ValidationProvider
  },
  methods:{
    processForm () {
      console.log('Posting to the server...')
    }
  }
}

在提交之前,我们使用<ValidationObserver>组件包装<form>元素来判断它是否有效。我们还在<ValidationObserver>组件的作用域插槽的对象中使用passes属性,用于防止表单在无效时提交。然后我们将我们的processForm方法传递给表单元素上v-on:submit事件中的passes函数。如果表单无效,则不会调用我们的processForm方法。

就这样。我们完了。您可以看到,methods属性中的v-on:submit事件不再需要checkForm方法,因为 VeeValidate 已经为我们完成了验证元素的繁重工作,现在我们的 JavaScript 代码缩短了。我们只需要用<ValidationProvider><ValidationObserver>组件包装输入字段。

If you want to find out more about the Vue slots and VeeValidate, please visit the following links:

You can find the example of our preceding Vue app in /chapter-7/vue/cli/ in our GitHub repository.

接下来,我们将在下一节中了解如何在 Nuxt 应用中应用 VeeValidate。

将自定义验证应用于 Nuxt 应用

让我们将自定义验证应用于我们已经拥有的示例网站中的联系人页面。您可能已经注意到,现有的联系人窗体已经安装了来自基础的验证(ZURB)。使用 Foundation 的表单验证是另一种增强 HTML 表单验证的好方法。

If you are interested in knowing more about Foundation, you can find out more from their official guide at https://foundation.zurb.com/sites/docs/abide.html.

但是,如果我们想使用 VeeValidate 进行自定义验证(我们刚刚学习了在 Vue 应用中使用 VeeValidate),那么让我们通过以下步骤安装并设置 Nuxt 所需的内容:

  1. 通过 npm 安装 VeeValidate:
$ npm i vee-validate
  1. /plugins/目录中创建一个插件文件,并添加我们需要的规则,如下所示:
// plugins/vee-validate.js
import { extend } from 'vee-validate'
import {
  required,
  email
} from 'vee-validate/dist/rules'

extend('required', {
  ...required,
  message: 'This field is required'
})

extend('email', {
  ...email,
  message: 'This field must be a valid email'
})

此文件中的所有内容都与我们在 Vue 应用中创建的文件相同。

  1. 在 Nuxt 配置文件的plugins选项中包括插件路径:
// nuxt.config.js
plugins: [
  '~/plugins/vee-validate'
]
  1. 在 Nuxt 配置文件的build选项中为/vee-validate/dist/rules.js文件添加异常:
// nuxt.config.js
build: {
  transpile: [
    "vee-validate/dist/rules"
  ],
  extend(config, ctx) {}
}

在 Nuxt 中,/node_modules/文件夹默认被排除在传输之外,使用vee-validate时会出现读取Unexpected token export的错误,所以在运行 Nuxt app 之前必须添加/vee-validate/dist/rules.js进行传输。

  1. 导入ValidationObserverValidationProvider组件,就像我们为 Vue 应用所做的那样:
// pages/contact.vue
import {
  ValidationObserver,
  ValidationProvider
} from 'vee-validate'

export default {
  components: {
    ValidationObserver,
    ValidationProvider
  }
}
  1. <form>元素中删除 Foundation 的data-abide属性,但用<ValidationObserver>组件将其包装,并用passesprocessForm方法将submit事件绑定到<form>元素,如下所示:
// pages/contact.vue
<ValidationObserver v-slot="{ passes }" ref="observer">
  <form v-on:submit.prevent="passes(processForm)" novalidate>
  //...
  </form>
</option>

这一步也与我们使用 Vue 应用所做的相同,但我们在本例中添加了ref="observer",因为我们将在步骤 8中需要它。

  1. 开始用<ValidationProvider>组件重构<form>元素中的所有<input>元素,如下所示:
// pages/contact.vue
<ValidationProvider name="name" rules="required|min:3" v-slot="{ errors, invalid, validated }">
  <label v-bind:class="[invalid && validated ? {'is-invalid-label': 
   '{_field_}'} : '']">Name
    <input
      type="text"
      name="name"
      v-model.trim="name"
      v-bind:class="[invalid && validated ? {'is-invalid-input': 
       '{_field_}'} : '']"
    >
    <span class="form-error">{{ errors[0] }}</span>
  </label>
</ValidationProvider>

这一步也与我们对 Vue 应用所做的相同,但在本例中,我们在v-slot指令中添加了两个作用域插槽数据属性invalidvalidated,用于有条件地将类绑定到<label><input>元素。因此,如果我们对invalidvalidated都得到true,那么我们将分别将is-invalid-labelis-invalid-input类绑定到元素。

For more information about the Validation Provider's scoped slot data properties, please visit https://vee-validate.logaretm.com/v2/guide/components/validation-provider.html#scoped-slot-data.

  1. 通过添加以下数据属性与v-model输入元素同步,重构<script>块中的data函数。我们还将在methods选项中添加两种方法,如下所示:
// pages/contact.vue
export default {
  data () {
    return {
      name: null,
      email: null,
      subject: null,
      message: null
    }
  },
  methods:{
    clear () {
      this.name = null
      this.email = null
      this.subject = null
      this.message = null
    },
    processForm (event) {
      alert('Processing!')
      console.log('Posting to the server...')
      this.clear()
      this.$refs.observer.reset()
    }
  }
}

这一步也与我们在 Vue 应用中所做的相同,但在本例中,我们在processForm中为methods选项添加了clear方法和reset方法。<ValidationObserver>组件在提交表单后不会重置表单的状态,因此我们必须手动执行,在步骤 6中将观察者作为参考传递,然后我们可以通过this.$refs从 Vue 实例访问它。

  1. 将这三个作用域插槽数据属性dirtyinvalidvalidated添加到<ValidationObserver>组件中,用于切换警报和成功消息,然后按照如下方式重构此组件:
// pages/contact.vue
<ValidationObserver v-slot="{ passes, dirty, invalid, validated }" ref="observer">
  <div class="alert callout" v-if="invalid && validated">
    <p><i class="fi-alert"></i> There are some errors in your 
     form.</p>
  </div>
  //...
  <div class="success callout" v-if="submitted && !dirty">
    <p><i class="fi-like"></i>&nbsp; Thank you for contacting
      me.</p>
  </div>
</ValidationObserver>

export default {
  data () {
    return {
      submitted: false
      //...
    }
  },
  methods:{
    processForm (event) {
      console.log('Posting to the server...')
      this.submitted = true
      //...
    }
  }
}

在最后一步中,我们添加了一个默认为false布尔值的submitted数据属性,当表单以processForm方法提交时,该属性将被设置为true。另一方面,当作用域插槽的invalidvalidated均为true时,警报消息块可见;当submitted属性均为truedirty作用域插槽数据属性均为false时,成功消息块可见。如果其中一个输入字段是“脏的”——换句话说,当输入字段中存在字母时,我们从dirty属性得到一个true布尔值。

您可以看到,我们的 Nuxt 应用中的重构代码与我们在 Vue 标准应用中所做的非常相似。但在 Nuxt 应用中,我们为表单添加了更复杂的逻辑,例如切换警报和成功消息,有条件地将类绑定到<label><input>元素,以及在提交表单时重置<ValidationObserver>组件。对于其余的输入元素,重构过程是相同的,您可以在本书的 GitHub 存储库/chapter-7/nuxt-universal/sample-website/中找到。

总结

在本章中,我们介绍了在各种表单输入上使用v-model进行 Vue 表单验证。您已经学习了基本和动态值绑定,以及如何使用修饰符更改默认输入事件和类型转换。您还学会了使用vee-validate插件来简化验证。最后,我们设法将这些应用于 Nuxt 应用。

在下一章中,我们将探讨如何在 Nuxt 应用中添加服务器端框架。您将学会使用 Koa 创建一个简单的 API 并将其与 Nuxt 集成,并使用 HTTP 客户机 Axios 来使用 API T0T 请求 API 数据。此外,还将向您介绍一个基于 webpack 的简约构建系统,称为 Backpack,它将简化我们用于单文件组件 Vue 应用的自定义 webpack 配置。您还将学习如何在 Nuxt 应用中使用此构建系统。所以,请继续关注!**