创建自定义输入

在本指南中,我们将一步步讲解如何创建、注册和使用自定义输入。具体来说,我们将创建一个"一次性密码"输入(简称"OTP")。OTP通常用于两步验证,当用户需要输入通过短信或验证器应用发送的代码时。让我们开始吧!

SFC 构建工具

本指南假设您正在使用标准的 Vue 3 构建工具,如 Vite、Nuxt 3 或 Vue CLI,这将允许您导入 .vue 单文件组件。

创建组件

首先,让我们创建输入的组件文件。我们将其命名为 OneTimePassword.vue

<script setup>
  const props = defineProps({
    context: Object,
  })
</script>

<template>
  <div>这里还有更多内容...</div>
</template>

FormKit 提供了许多我们想要保留的开箱即用的输入特性 —— 如标签、帮助文本和显示错误消息。我们真正想要修改的只是我们输入的输入部分。我们可以通过使用 @formkit/vue 包中的 createInput 实用函数来保留这些标准的 FormKit 特性。

当我们构建输入时,我们会想要可视化其进度,所以让我们创建一个示例表单来:

  1. 导入 OneTimePassword.vue
  2. 将导入的组件传递给 createInput()
  3. 使用返回值(一个 输入定义)作为 <FormKit> 组件的 type 属性。

我们将这个示例表单命名为 Register.vue

加载实时示例

太好了!现在我们可以迭代我们的 OneTimePassword.vue 文件并查看结果。首先要注意的是,我们的输入已经支持标签、帮助文本、验证和其他通用的 FormKit 属性。这些特性都是 createInput() 提供的。

另外,注意上面例子中的 <pre> 标签吗?它正在输出表单数据的当前状态。我们将使用这个来可视化我们自定义输入的值。由于我们的输入目前没有值,所以它在表单的数据中不会出现。是时候改变这个情况了!

输入 & 输出

让我们再次打开 OneTimePassword.vue,并将我们的 <div> 改为 <input> 标签。我们将从一个单独的文本输入开始,然后从那里开始工作。但是我们如何实际设置和显示我们自定义输入的值呢?

所有自定义输入都会将强大的 context 对象 作为 context 属性传递。为了让我们的输入能够 设置 其值,它需要调用 context.node.input(value)。为了正确地 显示 我们输入的值,我们应该将输入的 :value 属性设置为 context._value

加载实时示例

我们的小输入已经长大了!它可能看起来不太好看,但现在它可以读取和写入值了。作为证明,尝试将表单的 values 对象的初始值设置为 { two_factor_code: '12345' },你会看到输入自动填充了值。

我们的输入要求

好的,现在我们已经理解了如何创建输入,如何使用它,以及如何读取和写入值 - 让我们来解决我们一次性密码输入的实际"业务逻辑"。以下是我们的要求:

  • 用户输入一系列数字,每个数字都有自己的 <input> 标签。
  • 输入的值应始终是所有数字的连接。
  • 我们只希望在所有数字都已完成时输入才改变其值(如果用户还没有完成,就不需要在每次按键时提交和验证)。
  • 它应该允许用户点击给定的数字进行编辑。
  • 当用户输入一个数字时,它应该自动聚焦到下一个输入。
  • 它应该支持复制/粘贴。

添加一个 prop

对于我们的第一个要求,我们需要 n<input> 标签。也许最好将数字的数量作为一个 prop 暴露出来。为了做到这一点,我们需要告诉我们的 createInput 函数,我们想要接受一个新的 prop:

createInput(OneTimePassword, {
  props: ['digits'],
})

我们现在可以访问 context.digits。回到 OneTimePassword.vue,让我们使用它来输出正确数量的 <input> 标签。

加载实时示例

好的 - 我们有了多个输入!我们的第一个要求已经完成:

  • 用户输入一系列数字,每个数字都有自己的 <input> 标签。
样式

我们在上面的例子中添加了一点 CSS,但总的来说,我们不打算在这个指南中深入研究样式。建议使用 context.classes.yourKey 作为你的输入元素的类名。

交互性

注意在上面的例子中,当你在一个输入框中输入时,所有其他的输入框都同步到相同的值了吗?挺酷的,但不是我们想要的。这是因为我们仍然在使用相同的输入处理器和 :value。以下是我们改进输入的计划:

  • 每个输入应该只修改最终字符串中其相应索引处的字符。
  • 输入处理器应该在下一个输入上调用 focus()
  • 当字符串的长度与 digits 相同时,我们通过调用 context.node.input() 更新输入的值。
加载实时示例

太好了!这开始像我们期望的那样工作。让我们再次检查我们的要求:

  • 用户输入一系列数字,每个数字都有自己的 <input> 标签。
  • 输入的值应始终是所有数字的连接。
  • 我们只希望在所有数字都已完成时输入才改变其值(如果用户还没有完成,就不需要在每次按键时提交和验证)。
  • 它应该允许用户点击给定的数字进行编辑。
  • 当用户输入一个数字时,它应该自动聚焦到下一个输入。
  • 它应该支持复制/粘贴。

复制和粘贴

看起来我们只剩下一件事要做了 —— 支持复制和粘贴。幸运的是,浏览器有一个 paste 事件。为了确保我们的用户体验是一流的,我们会做一个假设:如果用户正在复制/粘贴,他们试图复制和粘贴整个代码。而不是代码的单个数字。这似乎是合理的。

我们需要做的就是捕获我们的任何输入标签上的复制/粘贴事件,获取正在粘贴的文本,并将 tmp 值设置为那个数字字符串。让我们再写一个事件处理器:

handlePaste(e) {
  const paste = e.clipboardData.getData('text')
  if (typeof paste === 'string') {
    // 如果长度正确,就粘贴它。
    this.tmp = paste.substr(0, this.max)
    const inputs = e.target.parentElement.querySelectorAll('input')
    // 聚焦在最后一个字符上
    inputs.item(this.tmp.length - 1).focus()
  }
}
加载实时示例

我们的所有要求都已经完成了!

注册

现在我们已经制作了一个出色的输入,让我们将它注册到我们的应用程序中,这样我们就可以在任何地方只需使用字符串 otp 就可以使用它。打开你的 Vue 应用程序的主文件(app.use(formKit) 所在的地方)。我们只需添加一下:

import { createApp } from 'Vue'
import App from 'App.vue'
import OneTimePassword from './OneTimePassword.vue'
import { plugin, defaultConfig, createInput } from '@formkit/vue'

const app = createApp(App)
app.use(
  plugin,
  defaultConfig({
    inputs: {
      otp: createInput(OneTimePassword, {
        props: ['digits'],
      }),
    },
  })
)
app.mount('#app')

完成!现在你可以在你的应用程序的任何地方使用你的输入:

<FormKit type="otp" digits="4" />

下一步

我们的一次性密码输入工作得很好!以下是我们可以进一步完善的一些额外功能的想法:

  • 附带一个验证规则,以对后端进行两因素认证调用。
  • 额外的样式,真正让它突出。
  • 如果表单只包含一个一次性密码输入,你可以自动提交表单!
  • 完成自定义输入清单
  • 发布它!如果这个输入(或者你制作的任何其他输入)对你有用,那么它可能对其他人也有用。你可能会考虑开源它!

希望这个指南帮助你理解如何声明、编写和注册自定义输入。如果你想深入了解,试着阅读关于FormKit 的核心内部创建自定义输入的内容!

想要更多?从阅读 FormKit 核心开始。深入了解