首页 > 解决方案 > 在 vue 中为创建表单处理数据层次结构的最佳方法

问题描述

假设我正在创建一个允许教师为学生创建作业的 vue.js 应用程序。每个作业都由介绍性文本和练习列表组成。每个练习也由文本和附件列表组成。

我想从充分利用 Vue 组件的角度来弄清楚创建表单以添加作业的最佳方式是什么。

假设实体被表示为具有以下结构的对象:

assignment: {
  text: string,
  dueDate: Date,
  exercises: array
}

exercise: {
  text: string,
  attachments: array
}

attachment: {
  text: string
}

完成创作形式的最天真的方法是拥有这样的东西:

// in <template>
<input type="text" v-model="assignment.text" />
<button @click="addExercise()">Add exercise</button>
<div v-for="exercise in assignment.exercises">
  <input type="text" v-model="exercise.text" />
  <button @click="addAttachment(exercise)">Add attachment</button>
  <div v-for="attachment in exercise.attachments">
      <input type="text" v-model="attachment.text" />
  </div>
</div>
<button @click="submit()">Create assignment</button>

// in <script>
data() {
  return {
    assignment: {
      text: '',
      exercises: [],
    }
  }
},
methods() {
  addExercise() {
    this.assignment.push({
      text: '',
      attachments: [],
    })
  },
  addAttachment(exercise) {
    exercise.attachments.push({
      text: '',
    })
  }
}

但这非常不雅,因为您将所有内容都转储到单个组件中。

我能想到的第一件事就是有AssignmentForm.vue,ExerciseForm.vueAttachmentForm.vue.

这样我就可以分别处理每个实体的数据和表单标记,并在我的主表单中组合所有内容。

但是,我不确定我将如何管理状态?我不能只是将子组件本地的所有内容复制到我的主窗体中,因为这没有任何意义。但与此同时,v-for如果我没有一个结构来保存我的主要本地数据中的所有子表单,我将如何迭代我的 's?我还需要有一种方法来从我的子表单组件中收集所有数据,并将其组合成一个 json 对象,以便在提交表单时发送到我的后端。

你会怎么做?

标签: javascriptvue.jsvuejs2

解决方案


我使用 VeeValidate 嵌套数组解决了这个问题

文档示例非常基础,我在代码和box.io 上的示例项目中对其进行了扩展

我们有一个看起来像这样的最终对象

{
  "group-name": "ewqqeeqw",
  "members": [
    {
      "name": "aaron smith",
      "email": "aarons@gmail.com"
    },
    {
      "name": "aaron jones",
      "email": "aaronj@gmail.com"
    },
    {
      "name": "aaron key",
      "email": "aaronk@gmail.com"
    }
  ]
}

我从 vee validate 中获得的好处是状态管理和字段验证都提供了开箱即用的功能。我需要做的就是将项目添加到数组中,然后根据循环生成表单字段

     <div>
        <ion-label>MEMBERS</ion-label>
        <div v-for="(m, index) in values.members" :key="index">
          <div>
            <ion-item>
              <Field
                as="ion-input"
                type="text"
                placeholder="name"
                :name="`members[${index}].name`"
                rules="required|min:8"
              />
            </ion-item>
            <span class="error">
              {{ errors[`members[${index}].name`]?.split(".")[1] }}</span
            >

            <ion-item>
              <Field
                as="ion-input"
                type="email"
                placeholder="email"
                :name="`members[${index}].email`"
                rules="required|email"
              />
            </ion-item>
            <span class="error">
              {{ errors[`members[${index}].email`]?.split(".")[1] }}</span
            >
          </div>
          <ion-button
            @click="removeMember(index, values)"
            style="zoom: 0.7; margin-bottom: 8px"
          >
            REMOVE MEMBER
          </ion-button>
        </div>
      </div>
      <div style="padding-top: 12px">
        <ion-button type="submit">SUBMIT</ion-button>
        <ion-button @click="addMember(values)">ADD MEMBER</ion-button>
      </div>

推荐阅读