javascript - Vuex 从状态加载现有表单数据以进行编辑

标签 javascript firebase vue.js vuex

我按照本教程尝试学习 Vue,我已经完成并且它可以工作,但我正在尝试做出我正在努力的改变。
https://savvyapps.com/blog/definitive-guide-building-web-app-vuejs-firebase
所以有一个“设置”页面,其中包含用户个人资料(他们可以编辑他们的名字等)。当“设置”/“配置文件”页面加载时,我希望表单加载他们现有的数据,以便他们可以修改它并按保存。
它当前作为占位符加载 :placeholder="userProfile.name" - 我希望它只用实际值填写表格,而不是将其作为占位符。
感觉这样做应该非常简单,但不能让它优雅地工作。
设置.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 的了解还不够,还不知道如何最好地更改该顺序..

最佳答案

v-model 获取 你传递给它的任何东西的值(value)。由于您要编辑状态属性,因此只要修改 <input>的值,它将尝试更改(又名变异)状态属性的值。这将打破不变性原则 [1] .
这里的解决方案是将计算属性传递给 v-model它使用 getter 和 setter,您可以在其中告诉组件从哪里获取值以及如何更新它。
默认 computed properties 是一种简写形式,仅包含 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要直接更改您的数据,您将违反不变性原则,因此您的状态将不再是唯一的事实来源。

关于javascript - Vuex 从状态加载现有表单数据以进行编辑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64018053/

相关文章:

javascript - jquery匿名函数声明的含义

基于 Firebase 数据库的 ios 应用程序 - 在本地合并更改

javascript - Firebase JS API : How to avoid `off()` getting executed before `on()` is called/finished executing?

vue.js - 如何在 Nuxt、Vue 中实现 Facebook Messenger 客户聊天 SDK?

javascript - Vue.js 错误的变量得到更新

javascript - 文本输入的模式属性在 Microsoft Edge 上无法按预期工作

javascript - Google Play Store Apache Cordova 漏洞(Cordova 版本应高于 4.1)

javascript - Vue Js应用无法显示页面

javascript - D3 代替 jquery 进行 dom 管理 - 可能吗?

firebase - 在 Android 设备上发布时未发送 OTP