Vuex 从状态加载现有表单数据以进行编辑
Posted
技术标签:
【中文标题】Vuex 从状态加载现有表单数据以进行编辑【英文标题】:Vuex load existing form data from state to edit 【发布时间】:2021-01-09 01:41:18 【问题描述】:我通过本教程尝试学习 Vue,我已经完成并且它可以工作,但我正在尝试做出我正在努力的改变。
https://savvyapps.com/blog/definitive-guide-building-web-app-vuejs-firebase
所以有一个“设置”页面,其中包含用户个人资料(他们可以编辑自己的姓名等)。当“设置”/“个人资料”页面加载时,我希望表单加载他们现有的数据,这样他们就可以修改它并按保存。
它当前加载为 :placeholder="userProfile.name"
的占位符 - 我希望它只使用实际值填充表单,而不是将其作为占位符。
感觉这样做应该非常简单,但不能让它优雅地工作。
Settings.vue
<template>
<section id="settings">
<div class="col1">
<h3>Settings</h3>
<p>Update your profile</p>
<transition name="fade">
<p v-if="showSuccess" class="success">profile updated</p>
</transition>
<form @submit.prevent>
<label for="name">Name</label>
<input v-model.trim="name" type="text" id="name" />
<label for="title">Job Title</label>
<input v-model.trim="title" type="text" id="title" />
<button @click="updateProfile()" class="button">Update Profile</button>
</form>
</div>
</section>
</template>
<script>
import mapState from "vuex";
export default
data()
return
name: "",
title: "",
showSuccess: false,
;
,
computed:
...mapState(["userProfile"]),
,
methods:
updateProfile()
this.$store.dispatch("updateProfile",
name: this.name !== "" ? this.name : this.userProfile.name,
title: this.title !== "" ? this.title : this.userProfile.title,
);
this.name = "";
this.title = "";
this.showSuccess = true;
setTimeout(() =>
this.showSuccess = false;
, 2000);
,
,
;
</script>
<style lang="scss" scoped>
</style>
我尝试将数据部分更改为此,这在我离开页面并返回时有效,但如果我刷新页面 (F5),则字段为空白,直到我离开页面并再次返回。
data()
return
name: this.$store.state.userProfile.name,
title: this.$store.state.userProfile.title,
showSuccess: false,
;
,
如果你需要,这里是我的商店:
存储/index.js
import Vue from "vue";
import Vuex from "vuex";
import * as fb from "../firebase";
import router from "../router/index";
Vue.use(Vuex);
// realtime firebase connection
fb.postsCollection.orderBy("createdOn", "desc").onSnapshot((snapshot) =>
let postsArray = [];
snapshot.forEach((doc) =>
let post = doc.data();
post.id = doc.id;
postsArray.push(post);
);
store.commit("setPosts", postsArray);
);
const store = new Vuex.Store(
state:
userProfile: ,
posts: [],
,
mutations:
setUserProfile(state, val)
state.userProfile = val;
,
setPosts(state, val)
state.posts = val;
,
,
actions:
async signup( dispatch , form)
// sign user up
const user = await fb.auth.createUserWithEmailAndPassword(
form.email,
form.password
);
// create user profile object in userCollections
await fb.usersCollection.doc(user.uid).set(
name: form.name,
title: form.title,
);
// fetch user profile and set in state
dispatch("fetchUserProfile", user);
,
async login( dispatch , form)
// sign user in
const user = await fb.auth.signInWithEmailAndPassword(
form.email,
form.password
);
// fetch user profile and set in state
dispatch("fetchUserProfile", user);
,
async logout( commit )
await fb.auth.signOut();
// clear userProfile and redirect to /login
commit("setUserProfile", );
router.push("/login");
,
async fetchUserProfile( commit , user)
// fetch user profile
const userProfile = await fb.usersCollection.doc(user.uid).get();
// set user profile in state
commit("setUserProfile", userProfile.data());
// change route to dashboard
if (router.currentRoute.path === "/login")
router.push("/");
,
async createPost( state , post)
await fb.postsCollection.add(
createdOn: new Date(),
content: post.content,
userId: fb.auth.currentUser.uid,
userName: state.userProfile.name,
comments: 0,
likes: 0,
);
,
async likePost(context, id, likesCount )
const userId = fb.auth.currentUser.uid;
const docId = `$userId_$id`;
// check if user has liked post
const doc = await fb.likesCollection.doc(docId).get();
if (doc.exists)
return;
// create post
await fb.likesCollection.doc(docId).set(
postId: id,
userId: userId,
);
// update post likes count
fb.postsCollection.doc(id).update(
likes: likesCount + 1,
);
,
async updateProfile( dispatch , user)
const userId = fb.auth.currentUser.uid;
// update user object
/*const userRef = */await fb.usersCollection.doc(userId).update(
name: user.name,
title: user.title,
);
dispatch("fetchUserProfile", uid: userId );
// update all posts by user
const postDocs = await fb.postsCollection
.where("userId", "==", userId)
.get();
postDocs.forEach((doc) =>
fb.postsCollection.doc(doc.id).update(
userName: user.name,
);
);
// update all comments by user
const commentDocs = await fb.commentsCollection
.where("userId", "==", userId)
.get();
commentDocs.forEach((doc) =>
fb.commentsCollection.doc(doc.id).update(
userName: user.name,
);
);
,
,
modules: ,
);
export default store;
编辑
我应该提到这些数据正在从 Firebase Firestore 加载到状态中。看起来这只是一个时间问题,当它在组件上设置 data() 时数据还没有完全加载 - 我添加了一些控制台日志。
Fetching user profile.. Settings.vue?e12e:29
Setting Data... index.js?4360:75
Performing setUserProfile commit.. index.js?4360:29
Setting user profile in state, last step..
再次,只是对 Vue 了解不够,还不知道如何最好地更改该顺序..
【问题讨论】:
【参考方案1】:v-model
获取并设置你传递给它的任何值。由于您要编辑状态属性,因此一旦您修改 <input>
的值,它就会尝试更改(又名 mutate)状态属性的值。这将打破不变性原则[1]。
这里的解决方案是将计算属性传递给v-model
,它使用getter 和setter,告诉组件从哪里获取值以及如何更新它。
默认情况下,computed
属性是一种简写形式,仅包含 getter。基本上,
computed:
name()
return this.$store.state.userProfile.name
...可以写成:
computed:
name:
get()
return this.$store.state.userProfile.name
您需要添加一个 setter 来提交适当的突变,以便更新状态:
computed:
...mapState(["userProfile"]),
name:
get()
return this.userProfile.name
,
set(val)
this.$store.commit('setUserProfile',
...this.userProfile,
name: val
);
,
title:
get()
return this.userProfile.title
,
set(val)
this.$store.commit('setUserProfile',
...this.userProfile,
title: val
);
计算的 setter 记录在 here。
[1] - 你使用 Vuex 的原因是你不想让任何组件直接modify your data。相反,您希望他们将更改提交到状态,以便使用该数据的每个组件都收到更改通知。如果您允许 v-model
直接更改您的数据,那么您将违反不变性原则,因此您的状态将不再是唯一的真相来源。
【讨论】:
这解决了问题。非常感谢您的详细解释:)【参考方案2】:要考虑两件事,首先是当您想从 state crate getter 中的变量中获取值时(作为良好的 vuex 实践):
Vuex 文件:
const store = new Vuex.Store(
state:
userProfile: ,
posts: [],
,
getters:
getUserProfile: (state) => state.userProfile
Settigs.vue 所以,要完成你想要的,你可以在挂载的方法中加载 data() 中的变量:
export default
data()
return
name: "",
title: "",
showSuccess: false,
;
,
computed:
...mapState(["getUserProfile"]),
,
mounted()
this.name = getUserProfile.name
所以,如果你期望用户刷新页面而不丢失加载的数据,你不能单独使用 vuex,因为当你刷新页面时,vuex 系统也会重启。如果您想维护刷新页面后加载的数据,请使用 localstorage 和 vuex 或类似的解决方案。
【讨论】:
对不起,我还应该提到,在教程中,它会从 Firebase 数据库中获取数据(包括用户配置文件)。因此,如果我刷新页面(表单变为空白),导航到主屏幕,然后返回个人资料页面,它会再次填写表单,而无需我在问题中输入任何带有解决方案的内容。只是我还没有完全理解它,无法弄清楚如何让它在页面刷新后进行“获取”。【参考方案3】:因为这只是一个时间问题:
我建议您将数据值绑定到您所在州的观察者。 每次状态更新时,您的组件都会简单地监听并相应地更新您的数据。
export default
data()
return
name: "",
title: "",
showSuccess: false,
;
,
computed:
...mapState(["userProfile"]),
,
watch:
userProfile:
handler( name, title )
this.name = name;
this.title = title;
,
deep: true, // deep is to listen to objects properly
immediate: true // immediate so the watcher triggers right away when the component is mounted
,
methods:
updateProfile()
this.$store.dispatch("updateProfile",
name: this.name !== "" ? this.name : this.userProfile.name,
title: this.title !== "" ? this.title : this.userProfile.title,
);
/* I just wouldn't reset the values here since they'll be updated in the watcher
this.name = "";
this.title = ""; */
this.showSuccess = true;
setTimeout(() =>
this.showSuccess = false;
, 2000);
,
,
;
【讨论】:
以上是关于Vuex 从状态加载现有表单数据以进行编辑的主要内容,如果未能解决你的问题,请参考以下文章
Vuex 状态随突变而变化 - apollo graphql 查询
Vue.js - 从 API 获取数据并在 Vuex 状态更改时重新加载组件