[英]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.