# 表单

虽然您可以自由地将 FormulateInput 元素用作独立元素, 但将它们组合成更传统的形式通常很有用。这样做就像将 FormulateInput 字段 包装在 FormulateForm 组件中一样简单。

FormulateForm 会积极收集所有来自 FormulateInput 域包装的元素的值, 并使用各自的 name prop 作为对象的属性名称。您可以用 v-model 像在表单域上一样使用读取和写入表单值 。 我们以注册表单为例:

查看源码
<template>
  <FormulateForm
    class="login-form"
    v-model="formValues"
  >
    <h2 class="form-title">Register</h2>
    <p>
      You can place any elements you want inside a form. The inputs themselves
      can even be deeply nested.
    </p>
    <FormulateInput
      name="name"
      type="text"
      label="Your name"
      placeholder="Your name"
      validation="required"
    />
    <FormulateInput
      name="email"
      type="email"
      label="Email address"
      placeholder="Email address"
      validation="required|email"
    />
    <div class="double-wide">
      <FormulateInput
        name="password"
        type="password"
        label="Password"
        placeholder="Your password"
        validation="required"
      />
      <FormulateInput
        name="password_confirm"
        type="password"
        label="Confirm your password"
        placeholder="Confirm password"
        validation="required|confirm"
        validation-name="Confirmation"
      />
    </div>
    <FormulateInput
      type="submit"
      label="Register"
    />
    <pre
      class="code"
      v-text="formValues"
    />
  </FormulateForm>
</template>

<script>
export default {
  data () {
    return {
      formValues: {}
    }
  }
}
</script>

<style scoped>
.login-form {
  padding: 2em;
  border: 1px solid #a8a8a8;
  border-radius: .5em;
  max-width: 500px;
  box-sizing: border-box;
}
.form-title {
  margin-top: 0;
}
.login-form::v-deep .formulate-input .formulate-input-element {
  max-width: none;
}
@media (min-width: 420px) {
  .double-wide {
    display: flex;
  }
  .double-wide .formulate-input {
    flex-grow: 1;
    width: calc(50% - .5em);
  }
  .double-wide .formulate-input:first-child {
    margin-right: .5em;
  }
  .double-wide .formulate-input:last-child {
    margin-left: .5em;
  }
}
</style>

# 设置初始值

要重新填充整个表单的数据,您可以设置表单的 values prop。这使得创建 “更新” 表单(如帐户页面)变得容易:

<template>
  <FormulateForm
    values="formValues"
  >
    <FormulateInput
      type="text"
      name="name"
      label="Your name"
    />
    <FormulateInput
      type="email"
      name="email"
      label="Your email"
    />
    <FormulateInput
      type="submit"
      label="Save account"
    />
  </FormulateForm>
</template>

<script>
export default {
  data () {
    return {
      formValues: {
        name: 'Jenny Taylor',
        email: 'jenny.taylor@example.com'
      }
    }
  }
}
</script>

# 模型绑定

在上面的例子中,formValues 用于最初设置字段值,但之后它永远不会改变。 通常,被动地使用表单的值是有帮助的。要做到这一点,请使用 v-model 而不是 values 双向绑定到表单值 ——如果您更改该对象的值,表单字段将更新。例如,您在其中一个文本字段中键入,该对象将更新!

  <FormulateForm
    v-model="formValues"
  >
    ...
  </FormulateForm>

注意

不建议在表单提交处理程序中通过 v-model 使用您的数据。 相反,使用传递给您的提交处理程序的数据。阅读 提交表单 了解更多信息。

# 提交表单

虽然使用 v-model 获取和设置表单的值很容易, 但 @submit 事件是从表单中检索最终值以进行处理或 提交到后端的首选方式。这里有许多的原因:

  • 所有验证规则(包括异步验证规则)都通过之前,该 @submit 事件不会触发。
  • @submit 事件中发出的数据是深度克隆的,可以安全地更改而没有副作用。

您可以像标准 <form> 元素的 submit 一样监听监听 @submit 事件。如果您从提交处理程序返回一个 Promise, FormulateForm 将自动在 上下文对象 中公开 isLoading 属性。

表单提交控制流程图

Form submission control flow

<template>
  <FormulateForm
    @submit="submitHandler"
    #default="{ isLoading }"
  >
    <FormulateInput
      label="What is your name?"
      name="name"
      help="Please enter your name"
      validation="required"
    />
    <FormulateInput
      type="submit"
      :disabled="isLoading"
      :label="isLoading ? 'Loading...' : 'Submit this form'"
    />
  </FormulateForm>
</template>

<script>
export default {
  methods: {
    async submitHandler (data) {
      await this.$axios.post('/my/api', data)
      alert(`Thank you, ${data.name}`)
    }
  }
}
</script>

Outputs:

Please enter your name

注意上面的表单在它包含的任何字段通过验证之前不会触发 alert 对话框。整洁哦。

提示

由于验证规则是异步的,并且 文件上传 时, 将等待解析 @submit 事件,相当于表单提交也是异步的。

# 未经验证的提交处理程序

有时您可能不想选择加入 @submit 事件的默认行为, 而是希望在每次尝试提交表单时同步收到通知。 对于这些边缘情况,您可以绑定到 @submit-raw 事件。

此事件在所有提交尝试时触发,即使输入未通过验证。由您决定如何处理它。 事件的载荷是一个 FormSubmission 实例 (opens new window).

# 表单验证 2.5

<FormulateForm> 组件始终了解其每个表单域的验证状态。 此外,除非每个嵌套的 FormulateInput 都有有效值,否则不会调用 @submit 处理程序。 表单的验证状态也可以通过 default 插槽提供给您的模板。在这个例子中, 我们只在所有字段都通过验证时启用提交按钮:

<FormulateForm
  #default="{ hasErrors }"
>
  <FormulateInput
    type="email"
    label="Please enter a superhero email address"
    validation="required|email"
    validation-behavior="live"
  />
  <FormulateInput
    type="checkbox"
    label="Select some of your favorite superheros"
    validation="required|min:2"
    :options="{
      batman: 'Batman',
      blackpanther: 'Black Panther'
      captainamerica: 'Captain America,
      catwoman: 'Catwoman',
      hulk: 'Hulk,
      superman: 'Superman',
      wonderwoman: 'Wonder woman',
    }"
    validation-behavior="live"
  />
  <FormulateInput
    type="submit"
    :disabled="hasErrors"
  />
</FormulateForm>

# 验证失败的消息 2.5

在长表单上,当提交包含无效字段的表单时,在提交按钮附近显示错误消息会很有帮助, 因为验证错误可能在可视区域之外。为此,请使用 invalid-message prop。

<FormulateForm
  invalid-message="Not all fields were filled out properly"
>
  <FormulateInput
    label="Email"
    type="email"
    name="email"
    validation="required|email"
  />
  <FormulateInput
    label="First name"
    name="first_name"
    validation="required"
  />
  <FormulateInput
    label="Last name"
    name="first_name"
    validation="required"
  />
  <!-- 表单错误将显示在这 -->
  <FormulateErrors />
  <FormulateInput type="submit" />
</FormulateForm>

invalid-message prop 可以是一个 String,Array 或者一个 Function。 函数被传递一个包含所有无效表单域的对象, 你应该返回一个 StringArray 字符串。 对上面的例子稍作调整,我们就可以输出失败的字段的名称:

<FormulateForm
  :invalid-message="invalidMessage"
>
...
<script>
export default {
  methods: {
    invalidMessage(fields) {
      const fieldNames = Object.keys(fields)
      const listOfNames = fieldNames.map(fieldName => fieldName.replace('_', ' '))
      return `Invalid fields: ${listOfNames}`
    }
  }
}
</script>

提示

有关该 FormulateErrors 组件的更多信息,请阅读 表单错误处理.

# 带条件的字段

要使字段有条件,请使用简单的 Vue 指令,例如 v-if

<FormulateForm v-model="values">
  <FormulateInput
    type="select"
    name="planet"
    label="What is your favorite rocky planet?"
    :options="{ mercury: 'Mercury', venus: 'Venus', earth: 'Earth', mars: 'Mars' }"
  />
  <FormulateInput
    v-if="values.planet === 'earth'"
    key="earth"
    name="earth_moon"
    label="What is the name of earth’s moon?"
  />
  <FormulateInput
    v-if="values.planet === 'mars'"
    key="mars"
    name="mars_sunset"
    label="What color is the Martian sunset?"
  />
</FormulateForm>
{}

条件字段和 key

由于 Vue 修补 DOM 的方式,通常最佳做法是在可以动态换出的每个 FormulateInput 上放置一个 key。 这确保了 Vue 在修补时不会重复使用 DOM 元素。有关更多信息,请阅读 Vue 文档中有关 重(chong)用元素 (opens new window) 的信息。

# 保留条件值

您是否注意到在上面的示例中,当相应的值被删除时, 表单数据中的值也被 FormulateInput 删除了? 如果您需要将这些值保留在表单数据中,请将 keep-model-data prop 设置为 true。 如果你只想要保存一两个字段的数据,你也可以将 keep-model-data 直接设置在相应的 <FormulateInput> 上。

<FormulateForm
  v-model="values"
  :keep-model-data="true"
>
  <FormulateInput
    type="select"
    name="planet"
    label="What is your favorite rocky planet?"
    :options="{ mercury: 'Mercury', venus: 'Venus', earth: 'Earth', mars: 'Mars' }"
  />
  <FormulateInput
    v-if="values.planet === 'earth'"
    key="earth"
    name="earth_moon"
    label="What is the name of earth’s moon?"
  />
  <FormulateInput
    v-if="values.planet === 'mars'"
    key="mars"
    name="mars_sunset"
    label="What color is the Martian sunset?"
  />
</FormulateForm>
{}

# 忽略表单域 2.5+

复杂的表单通常具有不需要提交给服务器的表单域, 例如仅用于控制表单显示的表单域。这些表单域可以通过添加 ignored prop 来选择退出表单参与:

<FormulateForm
  v-model="values"
>
  <FormulateInput
    label="Select your meal"
    v-model="meal"
    type="select"
    :options="{burger: 'Hamburger', pasta: 'Pasta'}"
    ignored
  />
  <FormulateInput
    v-if="meal === 'burger'"
    label="Build your own burger"
    type="checkbox"
    name="burger"
    :options="{
      meat: 'Meat',
      lettuce: 'Lettuce',
      tomato: 'Tomato',
      cheese: 'Cheese'
    }"
  />
  <FormulateInput
    label="Select a pasta sauce"
    v-if="meal === 'pasta'"
    name="sauce"
    type="radio"
    :options="{
      bolognese: 'Bolognese',
      carbonara: 'Carbonara',
      tortellini: 'Tortellini'
    }"
  />
</FormulateForm>
Notice this field wont end up in the form data.
{}

# 命名表单

Vue Formulate 引入了 “命名表单” 的概念,作为通过 $formulate 插件 全局 访问和操作表单的机制。 要利用命名表单,只需为任何 <FormulateForm> 组件提供一个唯一的 name prop 即可。 在任何当前安装的表单中名称应该是唯一的。 命名表单后,您可以轻松调用多个命名的表单方法。

方法 说明
handle(err, formName) 用于在表单上设置错误消息,通常来自后端服务器。阅读有关 错误处理 了解更多信息。
reset(formName, values) 重置表单的值、验证消息和错误消息。
resetValidation(formName) 重置所有验证和错误消息。
setValues(formName) 设置表单模型的值(即使没有 v-model 定义)
submit(formName) 用于以编程方式提交表单。
查看源码
<template>
  <FormulateForm
    name="login"
    @submit="setSomeErrors"
    v-model="formData"
    class="login-form"
  >
    <h2 class="form-title">Login</h2>
    <FormulateInput
      name="email"
      label="Email"
      validation="required|email"
    />
    <FormulateInput
      name="password"
      label="Password"
      validation="required"
    />
    <FormulateErrors />
    <div class="actions">
      <FormulateInput
        type="submit"
      />
      <FormulateInput
        type="button"
        label="Reset"
        data-ghost
        @click="reset"
      />
    </div>
    <code class="code code--block">{{ formData }}</code>
  </FormulateForm>
</template>

<script>
export default {
  data () {
    return {
      formData: {}
    }
  },
  methods: {
    setSomeErrors () {
      // do some processing...
      const errors = {
        fieldErrors: { username: 'Sorry, no such username exists!' },
        formErrors: ['Incorrect login, please try again.']
      }
      this.$formulate.handle(errors, 'login')
    },
    reset () {
      this.$formulate.reset('login')
    }
  }
}
</script>

<style>
.actions {
  display: flex;
  margin-bottom: 1em;
}
.actions .formulate-input {
  margin-right: 1em;
  margin-bottom: 0;
}
.login-form {
  padding: 2em;
  border: 1px solid #a8a8a8;
  border-radius: .5em;
  max-width: 500px;
  box-sizing: border-box;
}
.form-title {
  margin-top: 0;
}
</style>

# 事件

事件 说明
created 在应用默认值后,在表单的生命周期创建后触发
failed-validation 当表单提交由于验证失败时发出,传递一个以字段名称作为属性和组件实例作为值的对象。
input 当表单中的任何值更改时发出。
submit-raw 在任何表单提交尝试时发出,即使是无效字段。
submit 如果 所有字段都通过验证,则由任何标准表单提交事件发出

# Props

名称 说明
debounce 以表单形式清除所有表单域的抖动的时间量(单位毫秒)。 Note: this is not reactive, it only applies to inputs when they register
invalid-message String, ArrayFunction, 当表单提交时显示无效字段的错误消息。

# 上下文对象 2.5

表单包含一个 default 插槽,它传递了一个表单上下文对象。 此对象类似于 表单域上下文对象,但要简单得多。

Property 说明
errors 通过 错误处理 功能分配的一组显式表单错误(不是验证错误)。
hasErrors Boolean 指示表单是否有验证错误
hasValue Boolean 指示表单是否有任何值
isValid hasErrors 的倒数
isLoading If the form is currently loading. This is automatically managed by returning a promise from your `@submit handler.
values 表单本身的输入值。