繁体   English   中英

如何为我创建和/或更新的每个资源创建可重用的表单组件(Vue 3、vue-router、Pinia)

[英]How can I create a reusable form component for each resource I create and/or update (Vue 3, vue-router, Pinia)

我有一个使用 Pinia 存储的 Vue 3 应用程序,它存储来自我的 rest API 的 CRUD 数据。我刚刚开始使用 Vue 3(来自较小的 vue 2 项目),这是我第一次使用 Pinia,所以我仍在学习其中的复杂性两者的。

我通过我的 api 管理的一个资源称为Applications ,我有一个可组合项管理 API 调用以检索所有应用程序、1 个应用程序或更新所选应用程序。 我不想创建一个用于UPDATE的表单组件和一个用于CREATE应用程序的表单组件,我想创建一个处理这两者的表单组件。 到目前为止,我可以使用包含application_id的路由使用现有应用程序填充我的表单,如果application_id不在我的route.params中,我会创建一个新应用程序。 我只是不确定如何告诉表单“嘿,让我们更新这个应用程序而不是创建它。”。 我考虑过使用v-if指令,每个指令都创建一个<button> (一个运行更新,一个运行创建方法)取决于我的route.params中有一个application_id ,但这似乎效率低下(这可能是正确的,我只是缺乏知识)。 这是我的代码:

// ApplicationStore.js  (pinia store)
import { defineStore } from "pinia";
// Composable for axios API calls
import { getApplications, getApplicationByID, createApplication } from "@/composables/applications";

export const useApplicationStore = defineStore("application", {
  state: () => ({
    applications: [], //list of applications from database
    application: {},  //currently selected application for edit form
    loading: false,
    success: "Successfully Created",
    error: "",
  }),
  getters: {},
  actions: {
    async fetchApplications() {
      this.loading = true;
      this.applications = [];
      const { applications, error } = await getApplications();
      
      this.applications = applications;
      this.error = error;      
      this.loading = false;
    },
    async fetchApplicationByID(id) {
      this.loading = true;
      const { application, error } =  await getApplicationByID(id);
            
      this.application = application;
      this.error = error;
      this.loading = false;
    },
    async createNewApplication() {
      this.loading = true;
      const { application, results, error } = await createApplication(this.application);
      this.application = application;
      this.error = error;
      this.loading = false;

      if (results.status === 201) {
        // show this.success toast message
      }
    }
  }
});

这是我的ApplicationForm组件。 它当前查找route.param.id以查看是否选择了应用程序,如果是,它会填充表单:

// ApplicationForm.vue
<template>
  <section class="columns">
    <div class="column">
      <div v-if="error" class="notification is-danger">{{ error }}</div>
      <div class="field">
        <label class="label">Name</label>
        <input v-model="application.name" class="input" type="text" />
      </div>
      <div class="field">
        <label class="label">Location</label>
        <input v-model="application.location" class="input" type="text" />
      </div>
      <div class="control">
        <button @click="createNewApplication" class="button">Save</button>
      </div>
    </div>
  </section>
</template>

<script setup>
  import { useRoute } from "vue-router";
  import { useApplicationStore } from "@/stores/ApplicationStore";
  import { storeToRefs } from "pinia";

  const route = useRoute();
  const { applications, application, error } = storeToRefs(useApplicationStore());
  const { createNewApplication } = useApplicationStore();
  
  //checking if there's an id parameter, if so it finds the application from the list in the store
  if (route.params.id) {
    application.value = applications.value.find(app => app.id === Number(route.params.id));
  } else {
    //form is blank
    application.value = {};
    error.value = "";
  }
</script>

是否有一种首选方法可以将这种单一形式用于创建和更新? 我想知道slots是否是一个很好的用例? 但后来我想我最终还是会为每个 CRUD 操作制作多个表单组件。 另外,我考虑过使用v-if根据应用程序是否在商店中来呈现按钮,如下所示:

<button v-if="route.params.id" @click="updateApplication" class="button">Update</button>
<button v-else @click="createNewApplication" class="button">Save</button>

我忍不住觉得有更好的方法来处理这个问题(我将在这个项目和未来的项目中大量使用它)。 这是我的第一个大型 vue/pinia 应用程序。 到目前为止,我很喜欢这个堆栈,但这些小事情让我怀疑我是否有效地做到了这一点。

如果主要希望表单的 UI 除了一些小差异(例如按钮文本)之外保持不变,您可以使表单发出自定义“提交”事件,然后从呈现表单的父组件处理该事件(即在更新页面上你有<ApplicationForm @submit="updateApplication">并且在创建页面上你有<ApplicationForm @submit="createNewApplication" />

// ApplicationForm.vue
<template>
  <section class="columns">
    <div class="column">
      <div v-if="error" class="notification is-danger">{{ error }}</div>
      <div class="field">
        <label class="label">Name</label>
        <input v-model="application.name" class="input" type="text" />
      </div>
      <div class="field">
        <label class="label">Location</label>
        <input v-model="application.location" class="input" type="text" />
      </div>
      <div class="control">
        <button @click="$emit('submit')" class="button">{{ buttonText }}</button>
      </div>
    </div>
  </section>
</template>

至于文本,您可以将其作为道具(例如buttonText )传递给ApplicationForm组件。 如果表单的某些部分比“更新”和“创建”表单之间的不同文本有更大的不同,那就是您使用插槽的时候。

我不建议让<ApplicationForm />组件负责读取路由参数; 这通常应该只由负责呈现页面的 Vue 组件完成(然后它应该通过 props 传递该数据,以便组件尽可能可重用)

所以你的父组件看起来像这样:

<ApplicationForm v-if="application" @submit="updateApplication" />
<ApplicationForm v-else @submit="createNewApplication" />

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM