簡體   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