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