VueJS-表单构建指南-全-

VueJS 表单构建指南(全)

原文:zh.annas-archive.org/md5/89D4502ECBF31F487E1AF228404A6AC0

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

Vue.js 是世界领先和增长最快的前端开发框架之一。其平缓的学习曲线和充满活力和乐于助人的社区使其成为许多新开发人员寻求利用前端框架的力量的不二选择。此外,其灵活性和强大性使其成为高级开发人员和公司选择将其作为强大、动态和精简应用程序和网站的主要工具。

使用 Vue.js 构建表单中,我们将探索前端开发的一个特定部分——表单。我们将一起旅行,从创建最基本的表单,一直到理解完全动态、基于模式的表单是如何工作的。

这本书是为谁准备的

使用 Vue.js 构建表单旨在面向具有 Vue.js 框架基本理解的前端开发人员,他们想要了解如何更好地创建强大且可重用的表单。

本书涵盖的内容

第一章,“设置演示项目”,将指导您设置项目的基础,我们将在整本书中构建该项目。建议您按照章节的顺序学习本书,因为它们是基于前面章节学习的概念构建的。但是,如果您想要跳过,每章的完成代码将在每章的开头提供。

第二章,“最简单的形式中的表单”,展示了构建基本网络表单的基础知识,以及将输入连接到应用程序状态的过程。您还将了解提交表单的基础知识,并使用 Axios 库进行异步调用后端。

第三章,“创建可重用的表单组件”,将教您如何将表单分解为可以在整个应用程序中重复使用的组件。您将了解v-model指令的工作原理,以及主应用程序和表单如何利用这些组件。

第四章,“使用 v-mask 进行输入掩码”,涉及使用v-mask库来实现输入掩码以改善用户体验。您将学习如何实现第三方插件,以及如何将它们合并到自定义组件中。

第五章,使用 Vuelidate 进行输入验证,将带你了解如何向项目中添加 Vuelidate——一个强大的表单验证库,创建验证规则并将其应用到你的表单,以及如何将其整合到你的自定义组件中。

第六章,使用 Vuex 转移到全局状态,通过利用 Vuex 的强大功能,将当前应用程序的本地状态转移到全局状态。我们将把 Vuelidate 和我们的自定义组件整合在一起。

第七章,创建基于模式的表单,将所有先前的概念整合在一起,带你了解并创建一个渲染器组件,使你的应用程序完全基于模式驱动。它将对模拟 API 提供的 API 更改做出反应,并解释如何生成一个完全构建的表单,包括向模拟后端提交数据的方法。

为了充分利用这本书

为了让你更容易地跟随这本书,我必须对你已有的一些知识做一些假设。以下是你需要的基本要求清单,以便充分利用这本书:

  • 你之前使用过 HTML、CSS 和 JavaScript,并且能够轻松创建基本的 Web 应用程序。

  • 你熟悉console.log语句和在浏览器中调试 Web 应用程序的一般方法,比如 Chrome。

  • 基本的终端命令知识。你至少应该知道如何使用cd命令导航文件夹。

  • 你了解 Vue 的基本概念,比如状态、响应性、插值、计算属性和方法。确保查看官方指南的基本部分以供参考:vuejs.org/v2/guide/

  • 你有一台电脑和互联网连接,可以下载和安装所需的库和项目文件。

在本书的第一章中,我们将介绍如何按照简单的步骤列表设置你的项目。

下载示例代码文件

您可以从www.packt.com的帐户中下载本书的示例代码文件。如果您在其他地方购买了本书,可以访问www.packtpub.com/support注册,直接将文件发送到您的邮箱。

您可以按照以下步骤下载代码文件:

  1. www.packt.com上登录或注册。

  2. 选择“支持”标签页。

  3. 点击“代码下载”。

  4. 在搜索框中输入书名,并按照屏幕上的说明操作。

下载文件后,请确保使用最新版本的以下软件解压或提取文件夹:

  • WinRAR/7-Zip for Windows

  • Zipeg/iZip/UnRarX for Mac

  • 7-Zip/PeaZip for Linux

本书的代码包也托管在 GitHub 上,网址为github.com/PacktPublishing/Building-Forms-with-Vue.js。如果代码有更新,将在现有的 GitHub 存储库上进行更新。

我们还有来自丰富书籍和视频目录的其他代码包,可在github.com/PacktPublishing/上找到。快去看看吧!

下载彩色图像

我们还提供了一个 PDF 文件,其中包含本书中使用的屏幕截图/图表的彩色图像。您可以在这里下载:static.packt-cdn.com/downloads/9781839213335_ColorImages.pdf

代码演示

访问以下链接查看代码运行的视频:

bit.ly/2puBGN1

使用的约定

本书中使用了许多文本约定。

CodeInText:表示文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄。这是一个例子:“让我们开始安装v-mask库。”

代码块设置如下:

<input 
 type="text"
 v-model="form.telephone"
 v-mask="'(###)###-####'"
>

任何命令行输入或输出都将按照以下方式书写:

> npm install v-mask

粗体:表示新术语、重要单词或屏幕上看到的单词。例如,菜单或对话框中的单词会以这种方式出现在文本中。这是一个例子:“返回到第一个标签页,响应和正文。”

警告或重要提示会以这种方式出现。提示和技巧会以这种方式出现。

第一章:设置演示项目

每一个伟大的应用程序部署都始于一行代码,而在我们面前还有很长的路要走,我们应该从一开始就开始。

在本书中,我们将使用 Vue CLI 3 来设置我们的项目结构。Vue CLI 是一个令人惊叹的工具,可以快速搭建 Vue 应用程序。Vue CLI 诞生于成为搭建应用程序的首选工具。尽管还有其他令人惊叹的解决方案,比如 Nuxt,但了解 Vue CLI 的基础知识将让您在大多数项目中上手。如果您以前没有使用过它,不用担心:我们将一起逐步深入设置。

在本章中,我们将涵盖以下主题:

  • 将 Vue CLI 安装到我们的计算机上

  • 创建我们的新项目

  • 快速查看项目结构

技术要求

本章的要求如下:

  • 您需要一台可以访问终端程序的计算机,例如苹果的终端或 Windows 的命令提示符。

  • Node 版本 8.9 或更高版本和Node 包管理器npm):本章将提供安装说明。

  • 您将需要一个您喜欢的集成开发环境IDE)。一个很棒的免费 IDE 可以在code.visualstudio.com/找到

本章的代码文件可以在以下 GitHub 存储库中找到:

github.com/PacktPublishing/Building-Forms-with-Vue.js/tree/master/Chapter01

观看以下视频,查看代码运行情况:

bit.ly/2OXLxpg

将 Vue CLI 安装到我们的计算机上

在撰写本文时,Vue CLI 要求使用 Node 版本 8.9 或更高版本(建议使用 8.11.0+),因此我们需要确保您首先在开发计算机上设置了这个。

要检查是否已安装,请执行以下步骤:

  1. 打开一个终端(也称为命令行!)

  2. 执行node -v命令

如果您得到一个带有版本标签的输出,那么您已经安装了它,可以跳过。

如果您还没有安装 Node,请在浏览器中转到以下链接:nodejs.org

您应该看到一个主屏幕和两个大绿色的下载按钮。我们将使用标有“Current”的按钮,如下截图所示:

因此,请继续点击按钮并按照您自己操作系统的安装说明进行安装。

安装完成后,请验证一切是否正常工作:

  1. 打开您的终端

  2. 执行node -v命令

您应该得到类似 v12.2.0 的输出,验证了 node 已经正确安装到您的系统中。

然而,要将 Vue CLI 实际安装到我们的系统中,我们仍然需要使用包管理器。

现在,当您安装 Node 时,实际上免费在系统上安装了npm的副本。您可以通过在终端中输入npm -v来验证这一点,和以前一样,您将得到一个版本号作为输出。

请注意,Vue CLI 要求 Node 版本在 8.9 或以上(推荐 8.11.0+),但请确保您在阅读本书时检查以下链接以获取确切的版本号:vuejs.org/guide/installation.html

最后,现在是真正开始运行事情的时候了。再次打开您的终端,并运行以下命令:

> npm install --global @vue/cli

终端将继续下载所有所需的文件到您的计算机,并在全局可访问的路径中设置它们,以便您可以在计算机的任何地方使用此 CLI 工具。很棒,对吧?

请注意此命令上的--global标志。这意味着您在计算机上全局安装此软件包。简而言之,这意味着您可以在文件系统的任何位置使用命令,而无需导航到特定文件夹。

供以后参考,您还可以使用--global的简写,即简单的-g

再次,让我们通过在终端上运行vue --version来检查一切是否安装正确。您应该会得到 Vue CLI 的版本号。

现在我们已经设置好了 CLI,我们可以开始创建我们的新项目。让我们在下一节深入了解如何做到这一点。

创建我们的新项目

进入您选择的一个文件夹,该文件夹将保存您的项目文件。不用担心,我们不需要设置服务器、虚拟主机或任何类似的东西。Vue CLI 实际上会在我们每次运行项目脚本时为我们设置一个开发服务器,因此您可以在任何您喜欢的地方创建它。

您现在要运行的命令是vue create <name>,其中<name>是您的项目名称,也是将要创建的文件夹。

我们将通过运行以下命令来创建我们的新项目:

> vue create vuetiful-forms

vuetiful-forms 部分的命令将命名项目文件夹。当然,你可以根据自己的需要自由命名。

运行此命令后,Vue CLI 将显示一个向导,让你配置项目的设置方式:

我们将选择手动选择功能,因为我们想要尝试并查看可以切换开关的选项。请注意,我们在这里做出的决定并不是最终的。任何东西都可以随后添加或移除,所以不用担心!

第一个屏幕向我们展示了可以选择的不同功能和包:

  1. 选择 Babel 和 Lint/Formatter,这是默认的两个选项。在本书的后面,我们将手动向项目添加 Vuex。

  2. 按下空格键选择任何选项,按下 Enter 键继续到下一个屏幕。

  3. 在 linter/formatter 配置屏幕上,使用仅包含错误预防的 ESLint 配置。

  4. 在下一个屏幕上,我们将选择在保存时进行代码检查。(如果你不喜欢自动代码检查,可以选择其他选项。)

  5. 对于我们的配置,选择将其存储在专用配置文件中,以尽可能保持我们的 package.json 文件整洁。

  6. 最后,如果你愿意,可以将此保存为未来项目的预设。

另外,请注意,根据你的选择,你可能会看到不同于我在这里解释的配置。

终端将再次开始工作,在幕后为你的新项目创建项目结构:

通过这个易于遵循的向导,你可以轻松地搭建所有项目,但是如果在此阶段没有选择特定选项,不用担心;Vue CLI 使得随后添加和移除插件变得非常容易!现在让我们快速看一下我们的项目。

项目结构的快速概览

打开你的新的 vuetiful-forms 文件夹在你喜欢的代码编辑器中。如果你还没有用于开发的集成开发环境,你可以从 code.visualstudio.com 免费获取一个非常好的。

你的项目结构将如下截图所示:

以下是你可以在结构中找到的快速概述:

  • node_modules: 这里保存着你的依赖项——你可以使用 npm 安装或移除的代码包。

  • public:这个文件夹将保存index.html,当您导航到应用程序的 URL 时,您的 Web 服务器将加载它。Vue 会自动注入它所需的所有文件,因此您不需要担心这里发生的事情。

  • src:这是您将放置所有代码、组件、资产等的地方。

在您的项目根目录中,您将看到一个名为.eslintrc.js的配置文件,用于您的 ESLint 配置,.gitignore用于 Git,package.jsonpackage-lock.jsonyarn.lock文件用于包管理,以及根据您之前的选择而定的其他文件。

这些文件用于更改每个服务的偏好设置,如果您没有调整它们的经验,可以安全地忽略它们。

总结

到目前为止,您已经了解了使用 Vue CLI 构建应用程序的所有基础知识,并且已经初步了解了项目结构。

在下一章中,我们将启动并运行我们的项目,并开始处理实际的表单!

第二章:最简单的表单

好的!让我们毫不犹豫地开始(在美化之前稍作一些绕路)。我们将创建一个非常简单的带有表单的页面。这个表单将向用户询问一些基本的个人数据,表单的第二部分将用于更具体的问题。

在本章结束时,您将对如何在 Vue 中构建基本表单有扎实的理解,而且您还将快速复习基本的 Vue 概念,如v-model、事件和属性。

在本章中,我们将涵盖以下主题:

  • 使用 Bootstrap 入门

  • 实际编写一些代码

  • 将输入绑定到本地状态

  • 提交表单数据

  • 引入 Axios

技术要求

本章的代码可以在以下 GitHub 存储库中找到:

github.com/PacktPublishing/Building-Forms-with-Vue.js/tree/master/Chapter02

查看以下视频以查看代码的实际操作:

bit.ly/35F6340

使用 Bootstrap 入门

让我们首先将 Bootstrap 4 作为项目的依赖项添加到我们的项目中,这样我们就不必考虑设计,可以专注于我们表单的功能。

Bootstrap 是一个流行的开源工具包,它为我们提供了一些预定义的类和样式,这样我们就可以让我们的应用程序看起来漂亮,而不必太担心样式。

要安装 Bootstrap 并为我们的项目设置,请按照以下步骤进行:

  1. 打开项目文件夹的终端,并使用以下命令安装依赖项:
> npm install bootstrap
  1. 太棒了!这将把包添加到我们的node_modules文件夹和package.json中。现在,继续并将必要的样式导入到src/main.js中。使用以下命令来执行:
import 'bootstrap/dist/css/bootstrap.min.css';

我们不会使用 Bootstrap 的脚本,所以我们只需要最小化的 CSS 就可以了。

让我们对我们的App.vue文件进行一些清理,因为现在我们只有一些样板代码,但我们想要重新开始!所以,让我们开始清理:

  1. 用以下代码替换App.vue中的所有内容:
<template>
  <div id="app">
  </div>
</template>

<script>
export default {
  name: 'app'
}
</script>
  1. 通过在终端上运行以下命令来启动开发服务器:
> npm run serve
  1. 打开终端显示的链接(显示为本地)并在浏览器中应该看到一个空白屏幕。

瞧,这是伟大和重要的第一步的空白画布!耶!

让我们继续并开始着手实际的表单工作。现在是写一些代码的时候了。

实际编写一些代码

好了,够了设置——让我们写一些代码!我们将从一个非常简单的表单开始,这样我们的用户可以填写他们的个人信息。没什么疯狂的,只是小步走。

我们将向我们的表单添加三个字段。一个firstName输入,一个lastName输入和一个email输入。最后,我们将添加一个Submit按钮。

还记得我们安装 Bootstrap 吗?这就是它发挥作用的地方。我们要添加到标记中的所有类都将由 Bootstrap 自动样式化。

对您的App.vue文件进行以下更改:

<template>
  <div id="app" class="container py-4">
    <div class="row">
      <div class="col-12">
        <form>
          <div class="form-group">
            <label>First Name:</label>
            <input type="text" class="form-control">
          </div>

          <div class="form-group">
            <label>Last Name:</label>
            <input type="text" class="form-control">
          </div>

          <div class="form-group">
            <label>Email:</label>
            <input type="email" class="form-control">
          </div>

          <div class="form-group">
            <button type="submit" class="btn btn-primary">Submit</button>
          </div>
        </form>
      </div>
    </div>
  </div>
</template>

在上一个代码示例中,我们设置了一个带有row的容器。在这个row中,我们用三个不同的输入填充了它,两个text类型(一个用于名字,一个用于姓氏),以及一个email类型的输入。最后,我们添加了<button>,它将作为提交表单的主要方式。

保存您的文件并检查您的浏览器。如果服务器仍在运行,您应该会自动看到更改。好吧,我同意这有点令人失望,但我确实说过我们从一个简单的例子开始,这就是最简单的例子!

表单是完全功能的,甚至可以单击提交按钮使其提交给自身并实现绝对没有任何作用。很棒!但让我们用一些 Vue 来调味一下。

将输入绑定到本地状态

Web 应用程序中表单的目的是捕获一些用户输入并能够对其进行操作。在我们的示例中,我们仍然没有任何方法使用 JavaScript 访问用户的输入以进行我们美好的 Vuetiful 计划,所以让我们从那里开始。

请注意,您不一定要将表单数据包装在次要对象中,但我发现这样更清晰——特别是当您开始向组件添加其他数据属性时,这些属性可能与您的表单无关。

在您的App.vue文件的实例上创建一个新的data属性。在其中,我们将声明一个form对象,它将依次包含每个输入的属性:

<script>
export default {
  name: 'app',
  data() {
    return {
      form: {
        firstName: '',
        lastName: '',
        email: ''
      }
    }
  }
}
</script>

为了将我们的输入值绑定到内部状态,我们需要使用 Vue 的v-model属性。因此,让我们为每个输入添加v-model。这样,每当用户输入或删除信息时,输入元素的值将绑定到我们的data属性。

请记住,v-model不是一个神奇的属性。它是两件事的简写:

  • 它绑定了我们输入框的input事件:
v-on:input="form.name = $event.target.value"
  • 它将value属性绑定到我们的data属性:
 v-bind:value="form.firstName"

继续在所有输入中添加v-model

...
<div class="form-group">
  <label>First Name:</label>
  <input 
    v-model="form.firstName" 
    type="text" 
    class="form-control"
  >
</div>
<div class="form-group">
  <label>Last Name:</label>
  <input 
    v-model="form.lastName" 
    type="text" 
    class="form-control"
  >
</div>
<div class="form-group">
  <label>Email:</label>
  <input
    v-model="form.email"
    type="email"
    class="form-control"
  >
</div>

下面的屏幕截图显示了 Vue 开发者工具显示我们的表单与数据内部状态之间的双向数据绑定:

干得好!现在,这并不是非常令人印象深刻,但我们正在为未来打下基础。

在接下来的部分中,我们将看看如何处理表单提交并发送到 API 端点。

提交表单数据

目前,当单击提交按钮时,表单将提交到相同的 URL。这不是 Vue 的魔法 - 这只是默认的<form>行为,特别是因为我们没有在标签上指定action属性。

在大多数实际场景中,您希望在提交表单之前执行一些操作。最常见的操作可能是验证一些输入,或者甚至使用诸如 Axios 之类的库进行异步调用来覆盖默认的提交行为。

首先,我们需要确保当用户单击提交按钮时,我们阻止表单自行提交。我们还希望绑定一个新的方法来处理点击。

让我们首先绑定表单的submit事件。请记住,我们希望为事件添加.prevent修饰符,以便在提交表单时,不会触发默认行为,而我们的函数将按预期运行:

<form @submit.prevent="onSubmit">
  ...
</form>

太棒了!现在我们需要在App.vue文件的配置中创建这个新的onSubmit方法。在进一步详细说明之前,让我们在click方法处理程序内使用console.log来验证它是否有效。

将此代码作为export default声明内的属性添加:

methods: {
  onSubmit() {
    console.log('click');
  }
}

只是为了验证一切是否正常工作,继续打开浏览器,点击几次提交按钮。检查控制台;日志应该显示点击。到目前为止,一切都很好 - 我们已经成功控制了表单的行为。

让我们创建一个非常基本的验证方法作为示例。我们将验证三个字段的输入长度是否> 0(不为空)。在后面的章节中,我们将介绍 Vuelidate,它将为我们的表单提供更深入和强大的验证。

让我们创建一个名为formIsValid的新计算属性,它将检查我们刚刚讨论的条件。将以下内容添加到App.vue中:

computed: {
  formIsValid() {
    return (
      this.form.firstName.length > 0 && 
      this.form.lastName.length > 0 && 
      this.form.email.length > 0
    );
 }
}

现在我们有一个计算属性来检查我们表单的状态,让我们在onSubmit方法中实际使用它。我们将验证this.formIsValid是否为true,如果不是,我们将简单地返回并阻止表单提交。现在,我们将仅使用console.log进行确认。

onSubmit方法调整为以下内容:

onSubmit() {
  if (!this.formIsValid) return;
  console.log('Send my form!');
}

在浏览器上进行测试。如果您缺少任何字段,您将无法得到console.log,因为验证将失败。如果您填写它们并点击提交按钮,您将在控制台中收到消息。

在下一个块中,我们将引入第三方库 Axios,以帮助我们发送数据。

引入 Axios

我们表单的下一步是实际上让表单将用户的数据发送到我们的服务器。举例来说,数据实际上不会被存储在任何地方,但我们将看一下创建 POST 调用的步骤,大多数表单将用于将数据传输到 API 或服务器端点。

Axios 是一个非常棒和流行的库,用于向服务器发送和接收数据。我个人推荐它作为您的 Vue 应用程序中进行任何 HTTP 调用时的首选。您可以在这里找到官方的 GitHub 页面:github.com/axios/axios

按照以下步骤准备好您的项目中的 Axios:

  1. 打开您的终端并运行以下命令:
> npm install axios
  1. 我们需要一个 API 端点来进行调用。由于我们手头没有任何服务器,为了保持简单,我们将使用一个名为 Mockoon 的应用程序。前往mockoon.com/#download下载适用于您操作系统的应用程序。安装完成后,启动它。

  2. 在第二列中,您将看到两个示例路由;我们感兴趣的是 POST 路由到/dolphins(坦白说,我更喜欢海獭,但我会接受这个)。继续点击顶部的绿色播放三角形;这将在localhost:3000上启动一个服务器,默认情况下,但如果默认设置不适用于您,您可以更改端口。

  3. 现在,Axios 已经作为项目的依赖项添加,我们可以将其导入到App.vue中,以利用其不同的方法。

  4. App.vue文件的顶部添加导入语句,就在开头的<script>标签之后,在export default {行之前:

import axios from 'axios';

通过这个导入,我们现在可以在这个组件的任何地方使用 Axios。请记住,如果以后我们想在另一个组件或文件中使用它,我们将不得不再次导入它。

  1. 让我们再次更新onSubmit按钮。这一次,我们将摆脱console.log,然后用 Axios 进行async调用:
onSubmit() {
  if (!this.formIsValid) return;
  axios
    .post('http://localhost:3000/dolphins', { params: this.form })
    .then(response =>   {
      console.log('Form has been posted', response);
    }).catch(err => {
      console.log('An error occurred', err);
    });
}

每个 Axios 方法都返回一个 promise,这是一个原始的 JavaScript 对象。当这个 promise 解析时,也就是说,当实际的 HTTP 请求完成时,then块就会被调用!关于 promises 的更多信息,MDN 在developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise上有一个很好的资源。

如果你现在去浏览器尝试一下,你会发现,当点击提交按钮时,我们的onSubmit方法被触发,console.log成功执行。在这一点上,我们可以说我们有一个非常基本的(但可悲的无用)表单!

让我们再走一步,实际上在表单有效之前禁用输入按钮。(请记住,我们的验证现在非常薄弱,但我们以后会加强它。)

回到你的模板,让我们将按钮的:disabled属性与我们的计算属性formIsValid关联起来:

<button 
  :disabled="!formIsValid" 
  @click.prevent="onSubmit" 
  type="submit" 
  class="btn btn-primary"
> 
  Submit
</button>

再次在浏览器中测试一下,你会发现在表单实际填写之前,输入按钮是灰色的。很整洁!

总结

在本章中,我们已经迈出了创建一个简单数据收集表单的第一步。我们使用 Bootstrap 对其进行了样式化,并钩入了<form>事件。最后,我们使用 Axios 和 Mockoon 将数据发送到一个虚拟后端进行测试。

在下一章中,我们将探讨利用 Vue 的强大功能来构建可重用的表单组件。

第三章:创建可重用的表单组件

Vue 最强大的部分之一是它能够创建组件。

组件是可重用的代码片段,通常包括模板、脚本和样式。组件的惊人之处在于,你可以将特定元素或一组元素的所有逻辑封装到一个单元中。

开始以组件的方式思考的一个好方法是将日常物品分解为简单的、更小的部分。(请在你的脑海中!)

举个例子,你正在使用的计算机。作为一个整体,整个系统可以称为计算机。现在再进一步分解——它有一个显示器、一个键盘和一些电缆。现在拿键盘来分解。现在你有一个容器,这个容器有键。每个键都是一个单一的组件,它重复出现,具有一些在它们之间变化的属性。键上的标签会改变,有时也会改变大小。

那么这个关键组件呢?你能进一步分解吗?也许可以!但是值得吗?键盘键是一个很好的单一组件。它有清晰的属性来定义它,我们可以清晰地定义它的内部功能。当它被按下时,我们需要告诉包含它的人,一个键被按下,以及该键的值。

这种将某物进行分解的过程也可以应用到任何 Vue 应用程序中。从整个应用程序作为一个整体单元开始,然后将其分解。

现在,我们当前的表单是一个大块在App.vue中,这不是理想的。让我们创建一些组件!

在本章中,我们将涵盖以下主题:

  • 将应用程序分解为可重用组件

  • 理解自定义组件中的v-model

  • 实现自定义输入和选择组件

技术要求

本章的代码可以在以下 GitHub 存储库中找到:

github.com/PacktPublishing/Building-Forms-with-Vue.js/tree/master/Chapter03

查看以下视频以查看代码的实际操作:

bit.ly/2qgj7wx

将表单分解为组件

看看App.vue,让我们从我们可以创建的最小可能组件开始。如果你仔细看,你会发现代码中有一个重复的模式——这通常是一个好迹象,表明某些东西可能成为一个很好的组件!

在我们的<form>元素中,我们有三个不同的文本输入。其中两个是text类型,一个是email类型。看起来我们需要一种方法将这些值分配给type属性。属性对象可能是一个简单的解决方案!

作为一个快速提醒,这是表单的当前代码:

<div class="form-group">
  <label>First Name:</label>
  <input v-model="form.firstName" type="text" class="form-control">
</div>
<div class="form-group">
  <label>Last Name:</label>
  <input v-model="form.lastName" type="text" class="form-control">
</div>
<div class="form-group">
  <label>Email:</label>
  <input v-model="form.email" type="email" class="form-control">
</div>

继续在src/components文件夹中创建一个新文件,命名为BaseInput.vue。就我个人而言,我喜欢以Base开头命名我的基本输入组件;这样,我就知道它是我在应用程序中能找到的最简单的输入形式。

如果我需要制作一个扩展或以某种方式使用Base的组件,那么我只需导入BaseInput组件,并进行一些调整!但是,您可以随意使用任何您喜欢的命名约定。如果您想要一些关于命名组件的实际样式指南和最佳实践,请参考官方指南:vuejs.org/v2/style-guide/

让我们将App.vue中的第一个输入复制到我们的新组件中的<template>标签中,这样我们就有了一个基础来工作:

<template>
  <div class="form-group">
    <label>Name:</label>
    <input v-model="form.firstName" type="text" class="form-control">
  </div>
</template>
<script>
export default {

}
</script>

我们需要做的第一件事是弄清楚如何摆脱硬编码的值;毕竟,将代码提取到组件中的目的是使其具有动态性和可重用性。

让我们创建一个属性对象来保存label的值(名称为string):

<script>
export default {
  props: {
    label: {
      type: String,
      required: true
    }
  }
}
</script>

我们将使用对象表示法来声明属性,这样我们可以确保任何使用我们组件的人至少会在浏览器控制台中被警告,如果他们忘记定义标签。

现在,让我们回到模板,实际上用新创建的属性对象替换这个值:

<template>
  <div class="form-group">
    <label>{{ label }}</label>
    <input v-model="form.firstName" type="text" class="form-control">
  </div>
</template>

还有一个,类型呢?我们可能会想要在电子邮件和最终密码字段中使用这个(我们会的)。

让我们为此创建一个新的属性对象,并像以前一样绑定它:

props: {
  label: {
    type: String,
    required: true
  },
  type: {
    type: String,
    default: 'text',
    validator(value) {
      return ['text', 'email', 'password'].includes(value);
    }
  }
}

我们的新属性类型有一个默认值,在组件实现时,如果该属性缺失,将使用默认值。

validator是一个函数,它验证!它接受一个参数,即传递到属性的值,并且必须返回一个布尔值来验证该值是否可接受用于属性(validator验证!)。

在这种特殊情况下,我们只是检查它是否是我们允许的三个选择之一:textemailpassword

既然我们已经准备好了,让我们更新<input>

<input v-model="form.firstName" :type="type" class="form-control">

到目前为止,一切都很好!除了还有一件事情还缺少,我们需要重构。你能发现吗?

到目前为止,我们已经看到了如何将表单分解为组件。现在让我们更深入地了解v-model,以及在创建动态组件时的重要性。

在自定义组件中理解 v-model

正如你所知,v-modelv-on:inputv-bind:value="value"的简写形式。它允许我们双向绑定特定元素的值,以及它发出的事件到我们内部状态属性之一。

然而,在谈论组件组合时,我们需要考虑额外的事情。

为了让自定义组件能够实现v-model协议,我们必须确保发生两件事。没错!我们需要确保组件有一个value属性,并且它$emits一个输入事件。

有一种方法可以通过使用model属性来更改这种默认行为,但这超出了本书的范围。如果你想告诉你的组件使用不同的属性或不同的事件来使用v-model,请查看vuejs.org/v2/api/#model

让我们将这个理论付诸实践。我们将修改我们的BaseInput组件,以便能够使用v-model绑定。首先,让我们添加一个value属性,并将其挂钩到<input>上:

props: {
  label: {
    type: String,
    required: true
  },
  type: {
    type: String,
    default: 'text',
    validator(value) {
      return ['text', 'email', 'password'].includes(value);
    }
  },

  // Add this new prop
  value: {
    type: String,
    required: true
  }
}

现在我们有了新的value属性,我们需要将其绑定到<input>的值上。不过,一定要将旧的v-model从中移除!看一下下面的例子:

<input :value="value" type="text" class="form-control">

差不多了;现在我们需要确保<input>在更新时触发输入事件。因此,我们需要添加一个事件处理程序来$emit这个信息。

重要!在我们继续之前,让我告诉你一个非常常见的陷阱,当使用v-model和表单时。并非所有的输入都是一样的!<input>文本元素(textemailpassword)和<textarea>很容易。它们触发我们可以监听到用于v-model绑定的输入事件。但是,selectcheckboxesradio呢?

Vue 文档非常清晰,所以我要引用一下:

v-model在内部使用不同的属性并为不同的输入元素发出不同的事件:

  • texttextarea元素使用value属性和input事件;

  • checkboxesradiobuttons使用checked属性和change事件;

  • select字段使用value作为属性和change作为事件。

现在我们已经搞清楚了这个理论,让我们实际监听我们的事件:

<input 
  :value="value" 
  :type="type" 
  class="form-control"
  @input="$emit('input', $event.target.value)"
> 

恭喜!我们的BaseInput组件已经准备好使用了。

现在我们对v-model和自定义组件有了清晰的理解,我们将在表单中使用我们的组件。这将使它更易读、动态和易于维护。

实现自定义输入组件

创建可重用的自定义组件是 Vue 的核心部分,但是为了使组件真正有用,我们必须实际上使用它们!

打开您的App.vue文件,让我们用我们的自定义组件替换三个<div class="form-group">元素。

首先要做的是将组件导入到我们的文件中。让我们先搞定这个。在<script>元素的顶部添加以下导入,如下所示:

import BaseInput from '@/components/BaseInput';

仅仅导入文件是不够的;我们实际上必须将组件添加到文件的组件属性中,这样我们才能在模板中使用它。我们目前在 Vue 实例中没有这样的属性,所以让我们在namedata()之间创建一个:

...
components: { BaseInput },
...

现在我们已经注册了我们的组件,并在App.vue文件中导入了它,我们可以进入模板,用我们的新组件替换旧的输入:

<BaseInput 
  label="First Name:" 
  v-model="form.firstName" 
/>
<BaseInput 
  label="Last Name:" 
  v-model="form.lastName" 
/>
<BaseInput 
  label="Email:" 
  v-model="form.email" 
  type="email" 
/>

回到您的浏览器,玩一下这个应用程序。您会发现,即使实际上没有发生任何变化,表单现在是由可重用的输入组件驱动的——例如,如果我们需要更新输入的 CSS,我们只需在那个文件中更改一次,整个应用程序就会更新以反映这些变化。

再次打开 Vue DevTools,并确保选择了第一个图标(组件结构的图标)。深入结构,您将看到三个BaseInput组件在那里表示。

您甚至可以点击每一个,属性面板将清楚地显示每一个的独特之处——属性!

在下面的截图中,您可以看到,当我在“名称:”字段中输入我的名字时,组件会在其值属性中反映出来:

还有一件事!在表单中输入一些值,然后查看 props 框,它将实时更新您的值属性中的双向绑定。现在,点击 DevTools 中的第三个图标,看起来像一堆点——这是事件视图。

再次在其中一个输入框中输入一些值,您会看到事件框将填满条目。点击其中一个,您会注意到我们的输入事件在每次按键时都被触发。

这是两种不同的操作——值得到更新和输入事件被触发构成了v-model在工作中所做的工作,正如我们之前讨论的那样!

让我们看一下以下的屏幕截图:

在前面的屏幕截图中,您可以看到<BaseInput>组件是如何发出输入事件的——payload是用户在表单中输入的内容。

再来一次——带有下拉选项!

在我们结束本章之前,让我们构建一个自定义组件,它包装了一个下拉输入,以便回顾我们迄今为止学到的知识。

首先创建组件文件——我们将其命名为BaseSelect.vue,并将其放在components文件夹中。

就像我们对BaseInput所做的那样,首先我们将定义我们的 HTML 模板。我们现在会留一些属性为空,因为我们稍后会绑定它们。我们还将设置一些虚拟数据以便轻松测试。在组件创建中,您会发现小步骤是前进的方式!

将以下代码添加为BaseSelect的模板:

<template>
  <div class="form-group">
 <label>Label here</label>
 <select class="form-control">
 <option value="">Test!</option>
 <option value="">Me!</option>
 <option value="">:D</option>
 </select>
  </div>
</template>

看起来不错!让我们将这个新组件导入到App.vue中,并在我们的模板中,以便我们可以在浏览器中测试它的功能。按照给定的步骤来做:

  1. script元素的顶部导入组件,紧挨着BaseInput导入语句:
import BaseSelect from '@/components/BaseSelect';
  1. BaseSelect添加到您的components声明中:
components: { BaseInput, BaseSelect },
  1. <template>元素内创建BaseSelect的实例,就在最后一个BaseInput组件下面,也在包含输入按钮的div之前:
...
<BaseSelect />
... 

检查您的浏览器,您会看到我们新选择的组件正在发挥作用。她不是很漂亮吗?

让我们再进一步,我们迫切需要一些props。让我们从添加label开始;我们可以从模板中看到它需要被动态化。

在新的script元素内创建您的props对象,并将其添加到列表中:

<script>
export default {
  props: {
    label: {
      type: String,
      required: true
    }
  }
}
</script>

现在,前往模板并动态绑定它们。我们需要使用一些插值使<label>的内容动态化:

<template>
  <div class="form-group">
    <label>{{ label }}</label>
    <select class="form-control">
      <option value="">Test!</option>
      <option value="">Me!</option>
      <option value="">:D</option>
    </select>
  </div>
</template>

到目前为止,一切都很好!回到App.vue,并将这些新的props添加到我们的示例组件中:

<BaseSelect 
  label="What do you love most about Vue?" 
/>

在浏览器中测试一下,确保没有出现问题。到目前为止,组件运行得相当顺利,但它显示的选项仍然是硬编码的。让我们实现一个options属性,这次它将是一个对象数组,我们将用它来填充select选项。

回到BaseSelect.vue,并创建新的属性:

options: {
  type: Array,
  required: true,
  validator(opts) {
    return !opts.find(opt => typeof opt !== 'object');
  }
}

对于validator对象,我们将使用 JavaScript 数组,以找到一种方法来查看数组中是否存在一个不是对象的元素。如果找到了某些东西,find方法将返回它,!将对其进行评估为false,这将引发控制台错误。如果找不到任何东西(并且所有元素都是对象),那么find将返回undefined!将转换为true,并且验证将通过。

有关find方法的更多信息,请查看以下链接:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find

让我们在<select>元素内部实现一个v-for循环:

<select class="form-control">
  <option 
    v-for="opt in options"
    :key="opt.value"
    :value="opt.value"
    >
    {{ opt.label || 'No label' }}  
  </option>
</select>

v-for循环将抓取 options 中的每个元素,并在<select>内创建一个新的<option>元素;不要忘记设置:key属性!

如果您想了解更多关于:key的信息,即何时使用它以及原因,请查看我在以下链接中的文章:www.telerik.com/blogs/in-vue-when-do-i-actually-need-the-key-attribute-and-why

我们需要确保 options 中的每个对象都有labelvalue属性,但如果label缺失,我们将提供一个默认值。

回到App.vue,我们将在data()内部创建一个名为loveOptions的新内部state属性,它将保存特定<Select>的选项:

return {
  form: ...,
  loveOptions: [
    { label: 'Fun to use', value: 'fun' },
    { label: 'Friendly learning curve', value: 'curve' },
    { label: 'Amazing documentation', value: 'docs' },
    { label: 'Fantastic community', value: 'community' }
  ]
}

现在我们已经设置好了,去模板并将其绑定到我们的BaseSelect组件的options prop 上:

<BaseSelect 
  label="What do you love most about Vue?" 
  :options="loveOptions"
/>

保存后返回浏览器并检查选项。它活了!

还有一件事情缺失,我们需要将其添加到这个组件中,即v-model功能。我们需要创建一个value prop,使所选的option属性使用它,并确保我们从组件内部触发输入事件。

“记住,记住,v-model 的规则,属性绑定和发射。我不知道任何理由,v-model 系统,应该被遗忘。” - Vue Fawkes

在这种情况下,由于我们将使用 v-modelselect,请记住我们需要监听变化,甚至是内部的变化!另一个需要注意的是,您可能会想要在 select 标签上放置一个 :value 绑定,这不是与选择一起工作的正确方式!

HTML 中的 <select> 元素没有 value 属性;它所做的是将 selected 属性应用到其中的 option 元素,该元素保存当前值。

  1. 添加 value 属性:
      value: {
        type: String,
        required: true
      }
  1. 您将使用 value 属性来检查此选项的值是否等于它。确保在 select 元素触发 change 事件时发出 input
      <select 
        class="form-control"
        @change="$emit('input', $event.target.value)"
       >
        <option
          v-for="opt in options"
          :key="opt.value"
          :value="opt.value"
          :selected="value === opt.value"
          >
          {{ opt.label || 'No label' }}  
        </option>
      </select>
  1. 返回到 App.vue 并将 v-model 绑定到这个新元素。您需要在 data() 中的 form 属性中创建一个名为 love 的新属性,并将 v-model 属性添加到 BaseSelect 元素中:
      form: {
        firstName: '',
        lastName: '',
        email: '',
        love: 'fun'
      },

BaseSelect 元素现在将具有 v-model 绑定:

<BaseSelect 
        label="What do you love most about Vue?" 
        :options="loveOptions"
        v-model="form.love"
      />

最后,检查您的浏览器,看看一切是否正常。进入 DevTools 并检查您的 App 组件 - 当您切换选择的值时,它也会更新!

总结

在本章中,我们已经通过将单例应用程序或表单解构为可重用的动态组件的过程。我们已经涵盖了一些重要的核心 Vue 功能,比如 v-model,属性和事件。

在下一章中,我们将加快速度,实现一个与用户体验(UX)相关的功能,即输入掩码!

第四章:使用 v-mask 进行输入掩码

任何成功表单的关键方面之一是清晰度。如果用户发现表单易于使用和理解,他们更有可能填写并提交表单。在本章中,我们将研究输入掩码。您将学习如何快速轻松地将掩码应用于表单输入,并使用真实示例(如电话号码)根据您的需求进行配置。

究竟什么是输入掩码?它们是预定义的结构,用于显示输入的数据。例如,如果您要对电话输入进行掩码处理,您可能希望它显示为(123) 234-5555,而不仅仅是1232345555。您可以清楚地看到,第一个示例不仅更容易阅读,而且还传达了字段试图实现的含义。

输入掩码是一个很好的功能,可以将您的 UX 提升到另一个水平,而且非常容易实现,这要归功于开源库,如v-mask。GitHub 存储库页面可以在以下链接找到:github.com/probil/v-mask

在本章中,我们将快速了解如何在现有项目的基础上实现此库。

在本章中,我们将涵盖以下主题:

  • 安装v-mask

  • 探索v-mask指令

  • 增强我们的自定义输入

技术要求

本章的代码可以在以下 GitHub 存储库中找到:

github.com/PacktPublishing/Building-Forms-with-Vue.js/tree/master/Chapter04

查看以下视频以查看代码的实际操作:

bit.ly/31jFmyH

安装 v-mask 库

让我们开始安装v-mask库。为了让我们的项目使用它所提供的功能,我们首先需要将其添加到我们的项目依赖项中。按照以下步骤执行此操作:

  1. 打开您的终端并输入以下命令将库添加到我们的依赖项中:
> npm install v-mask
  1. 我们需要将其添加到 Vue 作为插件,因此转到main.js,让我们导入它并让 Vue 知道我们要将其注册为所有应用程序的插件。在import App行之后添加以下代码:
import VueMask from 'v-mask'
Vue.use(VueMask);

现在我们已经注册了我们的插件,我们可以访问一个新的指令:v-mask。我们可以直接将这个新指令添加到我们的<input>元素上,该库将通过读取用户的输入并调整字段的显示来处理幕后的掩码。

首先让我们在常规输入上尝试一下,然后我们将向我们项目的组件添加一些属性:

  1. 转到App.vue,在电子邮件输入后创建一个新的<input>元素:
<input type="text" />

如果我们在此字段中输入电话号码,我们将获得默认的输入行为。什么都可以。因此,让我们对其应用telephone号码掩码。我们的新v-mask库要求我们将其应用到的每个字段都需要进行 v 模型化,因此让我们首先完成这项工作。

  1. form对象的data()中添加一个新的telephone属性:
form: {
    ...
 telephone: ''
},
  1. 现在,返回到我们的新<input>元素并应用v-model。我们现在还将添加v-mask指令,如下所示:
<input 
 type="text"
 v-model="form.telephone"
 v-mask="'(###)###-####'"
>

返回浏览器,再次尝试输入。当您输入时,您将看到它被很好地格式化为我们期望的电话号码。

在五个简单的步骤中,我们已经为我们的一个表单字段添加了输入掩码。在下一节中,我们将更深入地了解v-mask指令为我们做了什么。

探索 v-mask 指令

当我们将v-mask库添加到我们的项目中,并在main.js中添加了插件时,该库为我们创建了一个新的指令v-mask。但是,指令到底是什么?我们知道它看起来像 HTML 属性,但还有什么?

指令可以定义如下:

“指令是带有v-前缀的特殊属性。指令属性值预期是单个 JavaScript 表达式(除了v-for […]之外)。指令的作用是在其表达式的值发生变化时,对 DOM 应用响应式的副作用。”- 官方 Vue 文档。

好的,看起来我们有一个特殊的属性可以修改元素。这听起来就像我们在应用到输入元素时看到的情况。但是,我们放入此指令的实际表达式或值是如何工作的呢?

我们从示例中知道我们正在传递一个字符串,并且您可以看到在组成v-mask=""属性的双引号内,我们设置了一对新的单引号(')。这意味着此属性内的表达式是 JavaScript,并且我们正在传递给它一个字符串值。

从查看v-mask库文档,我们知道我们有一些特殊的占位符字符,可以在我们的掩码中使用。这些特殊字符如下表所示:

# 数字(0-9)
A 任何大小写字母(a-z,A-Z)
N 数字或字母
X 任何符号
? 可选的(下一个字符)

以显示一天中的时间为例;您可以将其定义如下:

v-mask="'##:##'"

这意味着此输入将接受两个数字,从 0 到 9(##),后跟一个:字符,然后是另外两个数字(##)。

任何不符合此模式的内容都将被输入忽略。

v-mask是一个非常强大的库,它允许我们通过组合这些简单的规则来自定义我们希望输入如何显示。在下一节中,我们将修改我们的自定义输入,以便能够利用输入掩码的功能。

增强我们的自定义输入

我们已经付出了很多工作来创建我们的令人敬畏的自定义BaseInput,所以我们肯定希望继续使用它!

按照以下步骤修改BaseInput并允许输入掩码:

  1. 回到App.vue并将<input>元素切换为<BaseInput>组件:
<BaseInput 
label="Telephone"
 type="text"
 v-model="form.telephone"
/>

现在让我们进入BaseInput.vue并创建一个新的 prop;我们将称其为mask,并将其默认为一个空字符串。重要的是将其默认为一个空字符串,否则指令将尝试匹配它,如果没有声明掩码,我们将无法在字段中输入!

  1. 将其添加到您的props对象中:
...,
mask: {
type: String,
required: false
}
  1. 现在,回到App.vue并更新我们的电话BaseInput以使用mask属性:
<BaseInput 
label="Telephone"
type="text" 
v-model="form.telephone"
 :mask="'(###)###-####'"
/>

全部完成!返回到您的浏览器,并在字段中添加一些数字,您应该可以看到一个漂亮的电话掩码与您的自定义组件一起工作!

总结

在本章中,我们已经学会了如何利用v-mask库的强大功能来对我们的表单应用输入掩码。输入掩码是一种强大而简单的方式,可以为用户提供更好的体验,在构建甚至是最简单的表单时,不应忽视它!

在下一章中,我们将进一步学习并查看如何使用强大的库Vuelidate进行表单验证!

第五章:使用 Vuelidate 进行输入验证

在生产就绪的表单中,验证用户输入是必须的。即使在服务器端,应用程序也应该对传递给它们的所有数据进行双重检查,同时在前端对数据进行预验证应该是任何有经验的开发人员的强制性实践。

在这一章中,我们将看一下一个非常著名和强大的表单验证库 Vuelidate。您将学习如何在项目中使用这个库,并且能够成功地用它来验证用户输入。

值得庆幸的是,在 Vue 中,我们有一些不同的选项可以选择第三方库,比如 Vuelidate、VeeValidate,甚至 Vuetify 都有自己的验证方法。

在这一章中,我们将涵盖 Vuelidate。从安装到创建规则并将其应用于我们的表单输入,以及使用错误状态来通知用户存在问题。

本章将涵盖以下主题:

  • 安装依赖项

  • 创建验证规则

  • 将验证移入我们的自定义输入

  • 添加最后的修饰

技术要求

本章的代码可以在以下 GitHub 存储库中找到:

github.com/PacktPublishing/Building-Forms-with-Vue.js/tree/master/Chapter05

查看以下视频,看看代码是如何运行的:

bit.ly/2VJIL8E

安装依赖项

让我们首先将 Vuelidate 作为一个依赖项安装到我们的项目中,然后我们将用它进行验证。按照以下步骤进行:

  1. 打开终端并执行以下命令:
> npm install vuelidate

一旦库被安装,我们必须将其导入到main.js中并将其用作插件,以便它在全局范围内对所有组件都可用。

  1. main.js中添加以下代码,放在导入VueApp的代码之后:
import Vuelidate from 'vuelidate';
Vue.use(Vuelidate);

现在 Vuelidate 已经安装并成为我们项目依赖的一部分,我们准备让它承担一些繁重的工作。在下一节中,我们将创建我们的验证规则。

创建验证规则

当我们通过Vue.use将 Vuelidate 添加到我们的项目中时,该库会在我们的组件上添加一个新的保留属性:validations

这个属性被添加到组件的配置对象上,与data()computed等一起。它也将是一个对象,为我们想要验证的每个输入保留一个属性。

让我们创建这个属性并设置一个新的输入,没有自定义组件包装器进行测试。一旦我们理解了基础知识,我们就可以开始将所有这些内容翻译成我们的BaseInputBaseSelect组件。

按照以下步骤创建验证规则:

  1. App.vue中的BaseInputtelephone对象下面创建一个新的<input>表单:
<input type="text" v-model="form.website" />
  1. 记得将这个新属性website添加到data()form对象中:
form: {
  firstName: '',
  lastName: '',
  email: '',
  love: 'fun',
  telephone: '',
  website: ''
},

现在,让我们实际创建一个validations属性;现在,我们只会添加form.website的验证。

  1. 将其放置在component对象的顶层,与您的data()和计算属性处于同一级别:
validations: {
  form: {
    website: {
      // our validations will go here
    }
  }
}

对于这个特定的字段,我们希望确保验证用户提供的输入是一个有效的 URL。在 Vuelidate 中,我们有几个内置的验证器可以直接使用。完整列表可以在vuelidate.netlify.com/#sub-builtin-validators找到。

为了验证输入是否为有效的 URL,我们有 URL 验证器。但是,为了将其添加到我们网站的validators对象中,我们必须首先导入它。Vuelidate 允许我们只导入我们实际要使用的验证器;这样,我们可以确保我们部署的代码保持较小。

  1. App.vue中的其他导入附近添加以下导入语句:
import { url } from 'vuelidate/lib/validators';
  1. 现在我们已经导入了语句,我们最终可以将其添加到validations.website对象中:
validations: {
  form: {
    website: {
      url // Validate that the "website" input is a valid URL
    }
   }
 },

设置好规则就够了。记得我们之前创建的新的<input>表单来保存v-model="form.website"吗?我们需要对v-model的设置进行一些调整,以便 Vuelidate 负责验证。

除了我们之前用来设置规则的validations属性之外,Vuelidate 还为我们在组件实例内部提供了一个新属性:$v

$v是一个特殊的对象,它保存了我们验证结构的副本。除其他事项外,一个显著的特点是,它为我们添加到validations中的每个元素都有一个$model属性。Vuelidate 将成为我们的中介模型,并且反过来,它将自动绑定到我们data()中的form.website属性。

让我们来看看实际操作中是怎样的:

  1. 更新<input>网站元素,使用 Vuelidate 期望的新的v-model格式。此外,我们将在其下插入$v对象,以便您可以更清楚地看到幕后发生了什么,如下所示:
<input type="text" v-model="$v.form.website.$model" />
<pre>{{ $v }}</pre>
  1. 回到你的浏览器,看看$v对象的结构,在你在新的表单字段中输入任何内容之前。

要特别注意的第一件事是form.website对象。在此对象内部,Vuelidate 将保持此输入的验证状态。$model属性将保存用户的输入,就像我们告诉v-model要做的那样。$error属性实际上将切换布尔值,并且会告诉我们输入是否有错误。

尝试在字段中输入一些随机的胡言乱语,并观察更新的属性。$error属性将更新为true,表示存在错误。与 URL 规则直接关联的url属性将切换为false,表示未满足 URL 验证条件。

  1. 让我们在<input>上添加一些 CSS 绑定,以便在我们的输入上直观显示出未通过验证的情况:
<input 
  type="text"
  v-model="$v.form.website.$model" 
  class="form-control"
  :class="{ 
    'is-valid': !$v.form.website.$error && $v.form.website.$dirty,
    'is-invalid': $v.form.website.$error
  }"
/>

在我们进一步解释之前,请在浏览器中尝试一下。尝试输入一个有效的 URL,例如google.com,并注意输入如何反映您的更改。

:class绑定是一种在 Vue 中有条件地向任何 HTML 元素添加类的方法。在我们这里使用的语法类型中,它允许我们设置一个键值对,其中键定义了我们要切换的类,例如is-valid

该值是一个将被评估的 JavaScript 条件,以确定是否应该应用该类。这些条件是响应式的,并且每当条件的依赖项发生变化时,将被重新执行。

在我们的示例中,只要没有$error并且输入是$dirtyis-valid就会被切换为on。如果你想知道为什么我们还要检查$dirty,请尝试删除条件的这一部分,然后重新加载浏览器。您会立即注意到,即使元素中没有任何值,输入上也会出现绿色边框和复选标记。我们确定<input>是否在任何时候被用户修改的方式是通过$dirty属性;在这种情况下,从用户体验的角度来看,直到实际有一些输入时才显示有效的视觉提示是有意义的。

is-invalid的情况下,我们正在检查字段中是否存在任何$errors,并使用漂亮的红色边框和 x 图标设置字段。

现在我们已经有了一些基本规则,让我们继续下一节,在那里我们将学习如何将所有这些内容合并到我们的自定义组件中。

将验证移入我们的自定义输入中

拥有自定义组件的惊人之处在于您可以以任何您喜欢的方式来打造它们。在本章中,我们将为我们的组件添加对有效和无效状态的支持。主要的验证逻辑仍将由父组件App.vue持有,因为它是包含表单的组件。

按照以下步骤添加验证:

  1. 首先,让我们为每个输入添加新的规则。将以下内容添加到validations属性中:
validations: {
form: {
first_name: { alpha, required },
last_name: { alpha },
    email: { email, required },
  telephone: {
      validPhone: phone => phone.match(/((\(\d{3}\) ?)|(\d{3}-))? 
      \d{3}-\d{4}/) !== null
    },
    website: { url },
    love: { required }
  }
},
  1. 不要忘记更新您的导入语句,以引入我们现在使用的新验证器,如下所示:
import { url, alpha, email, required } from 'vuelidate/lib/validators';

让我们来看看新的验证器:

  • alpha:这将限制字段只能包含字母数字字符。

  • required:此字段使字段成为必填项;如果没有值,则无效。

  • email:此字段确保输入保持有效的电子邮件格式。

对于telephone字段,我们将进行一些自定义验证,因为该字段被掩码为特定格式(###)###-####,我们需要编写自己的验证函数。

在这种情况下,我们正在调用验证器validPhone,它是一个返回布尔值的函数。这个布尔值是通过将电话与正则表达式进行匹配并确保它不为空来计算的;也就是说,它确实有一个匹配项。

现在我们已经将所有的validations放在了适当的位置,我们需要更新我们的App.vue模板。我们的BaseInput组件和BaseSelect组件需要更新v-model,以便它指向 Vuelidate 模型而不是我们的本地状态。此外,我们需要将我们的网站输入更新为完整的BaseInput组件。

  1. 对您的代码进行以下更改;我们正在更新v-model和输入类型:
<form>
  <BaseInput 
    label="First Name:" 
    v-model="$v.form.firstName.$model" 
  />
  <BaseInput 
    label="Last Name:" 
    v-model="$v.form.lastName.$model" 
  />
  <BaseInput 
    label="Email:" 
    v-model="$v.form.email.$model" 
    type="email" 
  />
  <BaseInput 
    label="The URL of your favorite Vue-made website"
    v-model="$v.form.website.$model"
  />
  <BaseInput 
    label="Telephone"
    type="text" 
    v-model="$v.form.telephone.$model"
    :mask="'(###)###-####'"
  />
  <BaseSelect 
    label="What do you love most about Vue?" 
    :options="loveOptions"
    v-model="$v.form.love.$model"
  />
  <div class="form-group">
    <button 
      :disabled="!formIsValid" 
      @click.prevent="onSubmit" 
      type="submit" 
      class="btn btn-primary"
    >Submit</button>
  </div>
</form>

为了使我们的自定义组件显示正确的 CSS 类,我们将为它们添加一个名为validator的新属性,并将引用传递给与特定元素匹配的 Vuelidate 对象的 prop。

  1. 打开BaseInput.vue并创建validator属性,如下所示:
validator: {
type: Object,
  required: false,
  validator($v) {
    return $v.hasOwnProperty('$model');
  }
}

在属性的“验证器”方法中,我们将检查作为属性传递的“验证器”对象中是否有一个$model属性(即“验证器.$model”),这对于 Vuelidate 的所有字段属性都是 true。这样,我们可以确保我们可以访问我们需要的属性。

接下来,让我们将之前在元素上的:class 绑定带过来,但我们会做一些小的调整,以适应这是一个组件属性。

  1. 将以下内容添加到 BaseInput.vue 中的元素:
:class="{
  'is-valid': validator && !validator.$error && validator.$dirty,
  'is-invalid': validator && validator.$error
}"

由于验证器不是我们组件上的必需属性,我们必须再次检查实际设置的条件,然后再检查它的errordirty 属性。

  1. 最后,回到 App.vue 并为所有的 BasicInput 元素添加:validator 属性:
<BaseInput 
  label="First Name:" 
  v-model="$v.form.firstName.$model" 
  :validator="$v.form.firstName"
/>
<BaseInput 
  label="Last Name:" 
  v-model="$v.form.lastName.$model" 
  :validator="$v.form.lastName"
/>
<BaseInput 
  label="Email:" 
  v-model="$v.form.email.$model" 
  :validator="$v.form.email"
  type="email" 
/>
<BaseInput 
  label="The URL of your favorite Vue-made website"
  v-model="$v.form.website.$model"
  :validator="$v.form.website"
/>
<BaseInput 
  label="Telephone"
  type="text" 
  v-model="$v.form.telephone.$model"
  :validator="$v.form.telephone"
  :mask="'(###)###-####'"
/>

回到你的浏览器,玩弄一下输入框,现在它们都在后台由 Vuelidate 进行验证!

哇,这是相当多的信息 - 休息一下,吃点鳄梨土司;你值得拥有!在下一节中,我们将对我们的表单、BaseSelect 和 onSubmit 方法进行一些最终的更改,以便我们可以结束这一切。

添加最后的修饰

在我们结束本章之前,还有一些事情需要做。首先,让我们处理 BaseSelect;它仍然需要一个验证器属性和一些:class 绑定。

按照以下步骤找出我们如何做到这一点:

  1. 首先,在 BaseSelect.vue 中添加验证器属性:
validator: {
type: Object,
 required: false,
  validator($v) {
   return $v.hasOwnProperty('$model');
  }
}

现在,让我们添加:class 绑定;但这里,我们不会根据$dirty 进行检查,因为我们没有初始空值。

  1. 将以下代码添加到中应该有我们的三个选项。此时你会得到一些控制台错误,因为我们还没有为组件的双向值绑定工作,并且它们被设置为必需。但不用担心,我们很快就会解决这个问题! 动态绑定用户的数据 如果我们无法使用用户输入的数据,那么任何表单有什么用呢?尽管我们可以根据模式完全动态地生成这些表单,这很酷,但我们仍然需要能够以某种方式存储这些值,以便以后根据需要对它们进行处理。我们的表单能够创建双向绑定的第一步是告诉Renderer.vue如何处理来自动态组件的输入事件。 按照以下步骤进行: 让我们进入Renderer.vue,为添加:value绑定,以及一个@input监听器: 记住,为了v-model或双向绑定到自定义组件,通常我们需要传入一个值并监听输入事件。在这种情况下,我们将明确监听输入事件,因为我们的自定义组件都会触发这种类型的事件以进行双向绑定。 继续为我们的Renderer组件添加新的value属性: props: { element: { type: Object, required: true }, value: { required: true } } 最后,我们需要实现handleComponentInput方法。请记住,我选择将其作为一个方法而不是直接在template中触发emit的原因有两个。第一个是我发现这样做更容易编写单元测试,第二,如果我们需要为具有特定要求的特定组件编写if语句或条件,它可以提供更大的灵活性。 添加新的handleComponentInput方法: methods: { handleComponentInput (value) { this.$emit('input', value); } } 现在渲染器已经准备好与v-model进行双向绑定了,让我们回到App.vue,在那里我们正在实现它并添加实际的绑定。我们将在<Renderer>中添加v-model属性,这里的诀窍是我们将它绑定到form[name]。记住,我们的模式具有一个结构,其中属性的名称是该字段的唯一标识符。打开schema.json来查看它。 例如,在第一个字段中,firstName是保存模式对象中第一个空格的属性的名称。这个属性是我们将要绑定的,这样我们以后就可以知道它在我们的数据中代表哪个字段。 让我们在App.vue中的<Renderer>中添加我们的v-model: <Renderer v-for="(element, name) in schema" :key="name" :element="element" v-model="form[name]" /> 打开浏览器,查看你的表单;如果你开始填写字段并查看你的Vue开发工具,你会看到绑定都正常工作。渲染器通过动态的v-model所做的是将每个属性都绑定到本地数据表单的一个属性上。 如果您想以更快的方式看到这个示例,而不必使用dev工具,可以将以下代码添加到<Renderer>组件中的App.vue中: <pre>{{ form }}</pre> 我们只是简单地将表单放到屏幕上,并使用 HTML 的<pre>标签进行简单格式化。尝试进入schema.json并添加一些新字段。您将立即在浏览器上看到结果,因为渲染器将会注意到模式的更改,并且页面将自动重新加载(热重载)。有了新的模式,您将看到所有新字段都已就位。 我们正在快速取得进展!在接下来的部分,我们将再次准备演示 API。在实际应用中,您不会从文件中提供模式,而是从服务器中提供。启动 Mockoon,让我们开始吧! 创建模拟 API 我们演示的下一步是创建一个实际的模拟 API,然后将其转换为我们的渲染器理解的结构。为什么我们要这样解决这个问题呢?在实际工作场景中,后端通常不会完全匹配前端的需求。也许 API 是首先构建的,或者是根据早期版本的前端设计的,工作方式完全不同;有很多可能性,在这种情况下,我们将调整不兼容的 API,以学习如何防范这种情况。 这种方法还确保我们有一个中间人,将翻译并理解我们应用的 API。如果由于任何原因 API 发生变化,我们可以安全地只需更改这个中间人来调整变化,并且在大多数情况下甚至不必触及应用的任何内部组件。 按照这些步骤: 启动 Mockoon,这是我们在整本书中用于虚拟 API 调用的应用程序。如果您还没有安装它,可以在这里找到下载链接:mockoon.com/。 确保环境正在运行,点击绿色播放按钮,并在第二列顶部点击“添加路由”按钮。在右侧屏幕上,我们将添加一些数据。让我们从路径开始。 在路由设置下,将 GET 保留为路由的默认动词,并继续命名路径为/schema。 继续导航到第二个选项卡,标头,并将单个标头设置为 Content-Type:application-json。另一方面,这将为我们的下一部分提供一些漂亮的颜色编码。 返回第一个选项卡,响应和正文。 在 Body 部分,继续复制以下结构。请注意,这不是我们以前在schema.json文件中的内容,而是一个类似的结构,我们以后需要解释。我们甚至会忽略其中的一些数据,因为它对我们当前的表单没有用处——有时 API 返回的数据我们并不真的用到是很常见的: { "fieldCount": 4, "fields": [ { "type": "input", "id": "firstName", "label": "First Name" }, { "type": "input", "id": "lastName", "label": "Last Name" }, { "type": "input", "id": "email", "label": "Email" }, { "type": "singleChoice", "label": "What's your favorite animal?", "opts": [ { "label": "Cat", "value": "cat" }, { "label": "Dog", "value": "dog" }, { "label": "Sea Otter", "value": "onlyvalidanswer" } ] } ] } 仔细观察这种情况下 API 返回的 JSON 的结构。您将开始看到后端试图描述它需要什么,以及前端渲染器期望得到什么的一些相似之处。 如果您在日常生活中面临这种选择,您会意识到我们可以采取两种方式: 我们可以在组件级别上改变前端实现以适应新的 API,这在某些情况下可能是我们想要的。 或者,我们可以制作一个小型库或文件,用于解释我们后端的 API。出于我之前描述的原因,我们选择这个选择。 现在我们有了虚拟 API,我们可以教会我们的应用程序如何翻译这种新的 API 格式为它所理解的内容。这个过程的这一部分非常重要,因为您不希望在后端发生变化时每次都要修改整个应用程序。 将新的 API 加载到应用程序中 现在,如果您进入App.vue,您会注意到我们通过import语句加载静态模式,如下所示: import schema from '@/data/schema.json';. 这在以前对我们非常有效,因为它是一个静态文件,但这一次,我们需要调用我们的 API 端点来获取我们表单的模式。让我们首先删除导入语句;我们不再需要它,可以安全地删除它。您还可以进入data()属性并将schema设置为默认值,即一个空对象: schema: {}, 我认为一个很好的加载表单模式的地方将是在我们的App.vue文件的 created hook 上。我们希望尽快完成这项工作,当加载时我们并不真的需要操作任何 DOM,只需设置对模式的内部属性的调用结果。 按照以下步骤: 让我们在App.vue文件的顶部导入 Axios,靠近Renderer导入,因为我们很快就要用到它: import axios from 'axios'; 继续向我们的App.vue组件添加一个新创建的 hook;在其中,我们将对我们的模拟 API 端点进行简单的 Axios 调用。记得检查 Mockoon 是否正在运行: created() { axios.get('http://localhost:3000/schema') .then(response => { this.schema = response.data; }) .catch(error => { console.log('Network error', error); }) } 我们正在调用我们的新端点http://localhost:3000/schema,使用 Axios 的GET方法。确保检查 Mockoon 是否使用端口3000作为你的模拟 API,或者随时根据需要调整 URL。Axios 调用返回一个 promise——如果失败,我们将记录错误。然而,如果调用成功,我们要确保捕获整个响应,并将这个响应的data属性传递给模式的内部数据。重要的是要记住,在这种特殊情况下,API 返回给我们的响应应该是直接的 JSON 模式对象。如果你的 API 返回不同的结构,比如嵌套对象或数组,你将需要相应地进行调整。 如果需要,打开浏览器并重新加载页面。看起来我们完全搞砸了应用程序,这是预料之中的。当我们将新的 API 响应分配给我们模式的data属性时,应用程序试图将每个项目加载到渲染器中,但它并没有准备好理解我们后端提供的这种新模式格式。 在下一节中,我们将看看如何构建一个非常简洁的实用程序库,让我们能够将这个新结构解析成渲染器能够理解的内容。 将 API 翻译成可工作的模式 现在我们的模拟 API 正在运行,下一步是创建一种方法,让我们的应用程序解析或翻译这个 API 结构成为我们以前理解的模式结构。如果你此时好奇地尝试运行应用程序,你会遇到大量的控制台错误,指责你的属性类型检查失败和v-model绑定失败。这在此时是预料之中的。 继续,在src文件夹内创建一个新文件夹;我们将称之为libraries。这不是一个严格的命名约定,所以随意根据你或你的团队的需要进行命名。在这个文件夹内,我们将创建一个名为Api.js的新文件。我们的目标是将处理 API 模式解析的所有代码放在这里。这样,我们可以在组件中导入我们需要的内容,并且在 API 到模式的翻译方面有一个统一的真相来源——如果其中任何一端因任何原因发生变化,我们只需要在这里更新它。 按照以下步骤操作: 让我们从添加一个入口点开始;它将是一个名为parse的函数,并将接受一个参数:来自 API 的schema端点的响应: export const parse = schema => { return schema; } 现在,我们只是将schema原样返回,所以我们可以从小的步骤开始。 继续并将此函数导入到App.vue的顶部: import { parse } from '@/libraries/Api'; 然后,在创建的钩子内部,更新then块以在分配给状态之前使用该函数: .then(response => { this.schema = parse(response.data); }) 现在我们可以回到Api.js,并且我们将对此解析器进行基本实现。实际上,这里的代码复杂性将取决于您的应用程序要求与 API 结构之间的差异有多大。对我们来说,幸运的是,这只意味着几行代码。 将以下代码添加到Api.js: export const parse = schema => { const fields = schema['fields']; const parsedSchema = {}; for (let i = 0; i < fields.length; i++) { const field = fields[i]; parsedSchema[field.id] = { component: componentForField(field.type), label: field.label, options: field.opts || null } } return parsedSchema; } function componentForField(field) { switch(field) { case 'singleChoice': return 'BaseSelect'; default: return 'BaseInput'; } } 让我们将这里发生的事情分解成小块: 首先,我们创建一个常量字段,它将从我们的 API 数据中提取fields属性,因为它是嵌套在那里的,我们并不真正关心它发送的其他数据。 我们创建一个新对象parsedSchema,在其中为表单的每个字段添加一个属性。 我们循环遍历fields中的每个项目并为其创建一个属性。在我们的 API 模式中,id属性保存字段的唯一名称,因此我们将使用它作为我们的属性名parsedSchema[field.id]。 我们为组件属性分配一个内部对象,这是我们新的componentForField函数的结果,在其中我们找出每种情况下我们必须使用哪个组件。 对于options属性,我们检查 API 模式中是否存在opts属性,如果存在则将其设置为 null。重要的是要记住,即使BaseInput组件不期望这个属性,例如,它也不会在乎它是否存在并设置为 null 或未定义。 最后,我们返回模式的解析版本,这样我们的应用程序就可以将表单呈现为其工作状态。 在浏览器中运行它,看到你的基于模式、基于 API 的动态表单! 总结 花点时间给自己一个巨大的鼓励。你不仅完成了本章,还完成了整本书!在本章中,您已经掌握了理解基于模式的表单的用例的知识和技能,以及创建Renderer组件来适应这些情况的能力。您知道如何创建一个库来解析后端输出到您自己的表单,并在需要时将表单数据反馈给 API。 现在,出去,制作一些动态的形式,然后多吃一些鳄梨!
posted @   绝不原创的飞龙  阅读(120)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示