# 分组

这个 group 体系 只包含 group 类型表单域. 它是一种特殊的表单域,用于在逻辑上将一个或多个字段组合在一起。它适用于:

# Data organization

您可以使用该 group 类型对数据进行逻辑分组。嵌套在一个 group 下的字段将作为它们自己的对象捆绑在一起。

<FormulateForm
  v-model="fields"
>
  <FormulateInput
    type="text"
    name="name"
    label="Your name"
  />
  <FormulateInput
    type="group"
    name="address"
  >
    <FormulateInput
      type="text"
      name="address"
      label="Street address"
    />
    <FormulateInput
      type="text"
      name="city"
      label="City"
    />
    <FormulateInput
      type="select"
      name="state"
      label="State"
      :options="{VA: 'Virginia', CA: 'California', NY: 'New York'}"
    />
  </FormulateInput>
</FormulateForm>
{}

提示

为了一致性,group 即使该字段不可重复,一个分组的值也将始终是一个数组。 如果您需要一个普通对象,我们建议在 表单 submit 处理程序中解构数组。

# 可重复的分组

通过将 repeatable prop 设置为 true ,让我们组内的任何内容,包括非公式化元素在内的任何内容都可以无限重复。 还添加了一个 “添加更多” 按钮,以及每个项目的删除按钮。repeatable 可以使用特殊 props插槽 进一步定制一个分组。

查看源代码
<template>
  <div class="form-wrapper">
    <FormulateForm
      v-model="formData"
    >
      <FormulateInput
        type="group"
        name="attendees"
        :repeatable="true"
        label="Who is going to attend?"
        add-label="+ Add Attendee"
        validation="required"
      >
        <div class="attendee">
          <FormulateInput
            name="name"
            validation="required"
            label="Attendee’s name"
          />
          <FormulateInput
            type="email"
            name="email"
            validation="required|email"
            label="Attendee’s email"
          />
          <strong class="price" v-text="`$100`" />
        </div>
      </FormulateInput>
      <FormulateInput
        type="radio"
        label="Select your payment method"
        name="payment"
        :options="{paypal: 'PayPal', credit: 'Credit Card'}"
      />
      <strong>Total: {{ total }}</strong>
      <FormulateInput
        type="submit"
        label="Purchase tickets"
      />
    </FormulateForm>
    <code class="code code--block">{{ formData }}</code>
  </div>
</template>

<script>
export default {
  data () {
    return {
      formData: {}
    }
  },
  computed: {
    total () {
      const count = Array.isArray(this.formData.attendees) ? this.formData.attendees.length : 1
      return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD'}).format(count * 100)
    }
  }
}
</script>

<style scoped>
.form-wrapper {
  padding: 2em;
  border: 1px solid #a8a8a8;
  border-radius: .5em;
  box-sizing: border-box;
}
@media (min-width: 650px) {
  .attendee {
    display: flex;
  }
}

@media (min-width: 720px) {
  .attendee {
    display: block;
  }
}

@media (min-width: 850px) {
  .attendee {
    display: flex;
  }
  .attendee .formulate-input {
    margin-right: 1.5em;
  }
}
.attendee .formulate-input {
  margin-right: 2em;
  margin-bottom: 0;
}

strong {
  display: block;
  margin: 1em 0;
}

strong.price {
  margin-top: 1.25em;
  margin-bottom: 0;
  height: 2.5em;
  display: inline-flex;
  align-items: center;
}

code {
  margin-top: 2em;
}
</style>
Remove
$100
Total: $100.00
{}

注意

使用分组创建具有深度嵌套的非常大的表单很容易,但是请记住, 所有表单域都相互依赖——这样做会产生性能问题。如果您发现性能问题, 请尝试利用 表单或表单域上的 debounce prop.

# 验证功能

默认情况下,组内的字段与任何其他字段一样可以验证。例如,在分组中的必填字段中新加一个字段 会阻止该表单提交,直到该字段填入了内容。但是,您也可以直接在组上放置验证规则。 因此,在 group 组件上放置 “required” 规则 可确保您的组中至少有 1 个可重复项。

<FormulateForm>
  <FormulateInput
    type="group"
    label="Add your social media pages"
    :repeatable="true"
    validation-name="social media links"
    validation="min:2,length"
    add-label="+ Social link"
  >
    <FormulateInput
      label="Social media link"
      validation="required|url"
    />
  </FormulateInput>
  <FormulateInput
    type="submit"
  />
</FormulateForm>
Remove

提示

在分组组件上没有 blur 事件,因此默认情况下,当有人尝试提交表单时将显示验证错误。 如果您希望立即显示错误,请考虑使用 error-behavior="live"

提示

confirm 规则 目前在分组内无法执行。这将在即将发布的版本中修复。查看 跟踪问题 (opens new window)

# 使用自定义验证规则

将自定义验证规则应用于 group 字段允许非常精细和强大的规则来满足您的特定场景。 这些规则使您可以访问其所有子字段中的数据,从而允许复杂的跨字段验证。

查看源码
<template>
  <FormulateForm
    class="form-wrapper"
    @submit="handle"
  >
    <FormulateInput
      name="name"
      label="Your name"
      placeholder="Your name"
      validation="required"
    />
    <FormulateInput
      label="Your shipping address"
      type="group"
      validation="address"
      :validation-rules="{ address: addressRule }"
      :validation-messages="{ address: addressMessage }"
    >
      <FormulateInput
        name="street"
        placeholder="Street address"
      />
      <div class="triple">
        <FormulateInput
          name="city"
          placeholder="City"
        />
        <FormulateInput
          type="select"
          name="state"
          :options="{VA: 'VA', PA: 'PA', WA: 'WA'}"
          placeholder="State"
        />
        <FormulateInput
          name="zip"
          placeholder="Zip"
        />
      </div>
    </FormulateInput>
    <FormulateInput
      type="submit"
    />
  </FormulateForm>
</template>

<script>
export default {
  methods: {
    addressRule ({ value }) {
      if (Array.isArray(value)) {
        const [address] = value
        return address.street && address.city && address.state && address.zip
      }
      return false
    },
    addressMessage ({ value }) {
      if (Array.isArray(value)) {
        const [address] = value
        const missing = ['street', 'city', 'state', 'zip'].reduce((missing, field) => {
          if (!address[field]) {
            missing.push(field)
          }
          return missing
        }, [])
        return `Your shipping address still requires: ${missing.join(', ')}.`
      }
      return 'Please fill out your shipping address.'
    },
    handle () {
      alert('All validation complete, form submitted.')
    }
  }
}
</script>

<style scoped>
.form-wrapper {
  padding: 2em;
  border: 1px solid #a8a8a8;
  border-radius: .5em;
  box-sizing: border-box;
  max-width: 450px;
}


.form-wrapper::v-deep .formulate-input-element {
  max-width: none;
}

@media (min-width: 650px) {
  .triple {
    display: flex;
  }

  .triple .formulate-input {
    margin-bottom: .25em;
    margin-right: 1em;
    flex-grow: 1;
  }

  .triple .formulate-input[data-classification="select"] {
    flex: 0 0 5em;
  }

  .triple .formulate-input:last-child {
    margin-right: 0;
  }
}

@media (min-width: 720px) {
  .triple {
    display: block;
  }

  .triple .formulate-input {
    margin-bottom: 1.5em;
    margin-right: 0;
  }
}

@media (min-width: 850px) {
  .triple {
    display: flex;
  }
  .triple .formulate-input {
    margin-bottom: .25em;
    margin-right: 1em;
  }

  .triple .formulate-input:last-child {
    margin-right: 0;
  }
}

</style>

# 为分组设置错误信息 2.5

在分组上显式设置错误信息时,我们需要一种方法来指示错误字段位于哪个索引。 为了使这尽可能简单,请使用 group-errors prop 和 “点符号” 来引用索引中的表单域。例如:

<FormulateInput
  type="group"
  :repeatable="true"
  name="invitees"
  :group-errors="{
    '0.email': 'This email is already in use',
    '1.name': 'Pretty sure Rensmold isn’t a real last name'
  }"
  :value="[
    { name: 'Todd Berkins', email: 'todd@example.com' },
    { name: 'Stella Rensmold', email: 'stella@example.com' },
  ]"
>
  <FormulateInput
    name="name"
    label="Invitee's name"
  />
  <FormulateInput
    name="email"
    label="What is this user's email?"
  />
</FormulateInput>
Remove
  • This email is already in use.
Remove
  • Yeah...pretty sure “Rensmold” isn’t a real last name.

注意错误信息总是从分组的索引开始。group-errors prop 的属性必须始终以它们所应用到的组的索引开始。

你还可以 通过 <FormulateForm> 使用点符号 来设置分组的错误信息

# 分组的当前索引

要操作分组的不同字段,获取当前组项的索引很有帮助。幸运的是,插槽可以提供帮助。例如 default,插槽提供 index 作为 groupProps 的上下文变量:

例如:

 <FormulateInput
    type="group"
    name="attendees"
    :repeatable="true"
    add-label="+ Add Attendee"
    #default="{ index }"
>
  <p>This is Group # {{ index }} </p>
  <FormulateInput
    name="price"
    disabled
    :value="getPrice(index)"
    label="Price"
  />
</FormulateInput>

# Props

分组里的字段,有一些独特的 props

Prop 说明
add-label 当可重复时,这是显示在 “+ 添加” 按钮上的标签(默认为 label || name
limit 当可重复时,这是组项目的最大数量。
minimum 当可重复时,这是组项目的最小数量。
remove-label 当可重复时,这是显示在 “删除” 按钮上的标签。
remove-position 在组内表单域的 beforeafter 显示删除按钮 (默认为 after)
repeatable Boolean 类型,指示该字段是否可重复。
group-errors Object 点符号属性 (就像 0.name) 的错误

# 插槽

分组有几个独特的插槽(和匹配的 插槽组件):

插槽名 说明
addmore repeatable 时的添加更多按钮。
此插槽中的上下文对象包括一个 addMore函数,应调用该函数来添加新项目。
default 默认插槽中的任何内容都将被视为组的一部分, 当存在 repeatable 时, 上下文对象将另外具有 "index" 属性。
grouping 主要的可重复区域,负责渲染内部内容。
remove repeatable 时的删除按钮。
此插槽中的上下文对象包括 index 和一个 removeItem 函数用于删除该项目
repeatable 负责渲染每一行表单域。
此插槽中的上下文对象包括一个 removeItem 函数,应调用该函数以删除该项目。

# 事件 2.5

分组类型有两个独特的事件:

事件名 说明
@repeatableAdded 将新的可重复项添加到分组时触发。
@repeatableRemoved 从组中删除可重复项时触发

# 自定义样式 class 名

除了所有 公共样式 class 名 之外, 还可以使用以下 class 名:

默认值 说明
grouping .formulate-input-grouping 所有可重复字段的包装器(仅在可重复时存在)
groupRepeatable .formulate-input-group-repeatable 每组分组字段的包装器(即使对于不可重复的组也存在)。
groupRepeatableRemove .formulate-input-group-repeatable-remove 字段组的删除按钮。
groupAddMore .formulate-input-group-add-more 可重复分组的 “添加更多按钮” 周围的包裹器。