如何从具有多个组件的动态表单中 $emit 数据

How to $emit data from dynamic form with multiple components

我正在开发一款用户可以创建任务列表的应用程序。创建列表后,将从数据库中提取列表供用户使用,并select对每个任务进行适当的响应。

回应:- 是或否

选择 'No':启用原因下拉列表 select 任务未完成的原因 - 选择 'other' 选项将显示自定义原因输入框。

用户还可以为每个任务添加评论,一旦表单完成,他们就可以提交。

由于显示每个任务的组件是动态的,我不知道如何捕获表单响应。我在组件周围创建了一个 <form> - 单击后应提交表单,但我不知道如何从组件获取数据。

CodeSandbox

App.vue

<template>
  <button @click="toggleComplete">Complete All</button>
  <form @submit.prevent="submitModal">
    <checklist-item
      v-for="taskList in myChecklist.tasks.data"
      :key="taskList.id"
      :taskDetails="taskList"
      :marked="markAll"
    >
    </checklist-item>
    <div class="">
      <button class="" type="submit">Submit</button>
    </div>
  </form>
</template>

<script>
import checklistItem from "./components/checklistItem.vue";
export default {
  name: "App",
  components: {
    checklistItem,
  },
  data() {
    return {
      markAll: false,
      myChecklist: {
        success: true,
        name: "Checklist 1",
        roomId: 1,
        notes: "Notes about the checklist",
        tasks: {
          data: [
            {
              id: 1,
              name: "Task 1",
              frequency: "D",
              status: null,
              comment: null,
              reason: null,
              updated_at: "2021-08-09",
            },
            {
              id: 2,
              name: "Task 2",
              frequency: "M",
              status: null,
              comment: null,
              reason: null,
              updated_at: "2021-08-09",
            },
            {
              id: 3,
              name: "Task 3",
              frequency: "Y",
              status: null,
              comment: null,
              reason: null,
              updated_at: "2021-08-09",
            },
          ],
        },
      },
    };
  },
  methods: {
    toggleComplete() {
      this.markAll = !this.markAll;
    },
    submitModal() {
      console.log("inside");
    },
  },
};
</script>

<style>
</style>

ChecklistItem.vue

<template>
  <div class="flex justify-center items-center">
    <div
      class="border border-gray-200 w-11/12 bg-white rounded-lg shadow-sm hover:shadow-2xl duration-500 px-2 py-4 my-1"
    >
      <div class="flex flex-col m-auto w-11.5/12">
        <div class="">
          <div class="text-xl font-semibold">
            {{ taskDetails.name }}
          </div>
          <div class="pb-2 sm-pb-0">
            <p>Due: <strong>Today</strong></p>
          </div>
          <!-- Yes Button -->
          <div class="yesClass flex justify-center-0 pb-2">
            <input
              @change="disableSelect()"
              type="radio"
              :checked="marked"
              :name="taskDetails.id"
              :id="taskDetails.id + 'yes'"
              class=""
            />
            <label
              :for="taskDetails.id + 'yes'"
              class="yesClass border text-center border-orange-400 rounded-2xl w-full py-0.5 hover:bg-green-400 hover:text-white hover:border-green-200"
            >
              <span>Yes</span>
            </label>
          </div>
          <!-- No Button -->
          <div class="noClass flex justify-center pb-2">
            <input
              @change="enableSelect()"
              type="radio"
              :name="taskDetails.id"
              :id="taskDetails.id + 'no'"
              class=""
            />
            <label
              :for="taskDetails.id + 'no'"
              class="border text-center border-orange-400 rounded-2xl w-full py-0.5 label-checked:bg-green-200 hover:bg-red-800 hover:text-white hover:border-red-200"
            >
              <span>No</span>
            </label>
          </div>
          <!-- Reason Dropdown -->
          <p>Reason:</p>
          <div class="disabled flex justify-center">
            <br />
            <select
              :disabled="!notCompletedToggle"
              name="reason"
              id="reasons"
              class="w-full mt-1 rounded-lg py-0.5 my-0.5"
              :class="[notCompletedToggle ? 'bg-red-500' : '']"
              ref="selectedItem"
              @change="customEventToggle($event)"
            >
              <option value="reason_1" :selected="!notCompletedToggle">
                Select Reason
              </option>
              <option value="reason_1">Reason 1</option>
              <option value="reason_2">Reason 2</option>
              <option value="reason_3">Reason 3</option>
              <option value="other">Other --></option>
            </select>
          </div>
          <div
            class="flex justify-center my-0.5"
            :class="{ hidden: !customReasonToggle }"
          >
            <input
              type="text"
              class="bg-gray-200 w-full rounded-md mt-1 py-0.5"
              v-model="customReason"
            />
          </div>
        </div>
        <div class="">
          <div class="flex items-end justify-start text-grey">
            <label>Comment:</label>
          </div>
          <div class="flex items-end justify-center">
            <textarea
              type="text"
              class="bg-gray-200 rounded-lg w-full h-16"
            ></textarea>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  components: {},
  emits: ["custom-event-toggle"],
  props: ["taskDetails", "marked"],
  data() {
    return {
      notCompletedToggle: false,
      customReasonToggle: false,
      customReason: "",
    };
  },
  methods: {
    customEventToggle(event) {
      if (event.target.value === "other") {
        this.customReasonToggle = true;
      } else {
        this.customReasonToggle = false;
      }
    },
    disableSelect() {
      this.notCompletedToggle = false;
      this.customReasonToggle = false;
    },
    enableSelect() {
      this.notCompletedToggle = true;
    },
  },
};
</script>

<style scoped>
input[type="radio"] {
  display: none;
}
.yesClass input[type="radio"]:checked + label {
  background-color: #78be20;
  color: white;
}
.noClass input[type="radio"]:checked + label {
  background-color: #da291c;
  color: white;
}
</style>

您可以在每个事件上从 checklist-item 组件内部发出事件,以设置 myChecklist.tasks.data 数组中的值。

methods: {
    updateData(property, value) {
      this.$emit("update-form-data", {
        id: this.taskDetails.id,
        property,
        value,
      });
    },
    customEventToggle(event) {
      this.updateData("reason", event.target.value);
      if (event.target.value === "other") {
        this.customReasonToggle = true;
      } else {
        this.customReasonToggle = false;
      }
    },
    disableSelect() {
      this.updateData("status", "YES");
      this.notCompletedToggle = false;
      this.customReasonToggle = false;
    },
    enableSelect() {
      this.updateData("status", "NO");
      this.notCompletedToggle = true;
    },
    setComment(event) {
      this.updateData("comment", event.target.value);
    },
}

然后在父组件中监听事件并更新你的数据数组:

<checklist-item
      v-for="taskList in myChecklist.tasks.data"
      :key="taskList.id"
      :taskDetails="taskList"
      :marked="markAll"
      @update-form-data="
        this.myChecklist.tasks.data.find((task) => task.id === $event.id)[
          $event.property
        ] = $event.value
      "

Here's your updated sandbox。单击提交时将数据记录到控制台。