Vuex - 从 child $emit 更新存储中的数据

Vuex - updating data in store from child $emit

我有一个 Vue 应用程序,它可以随机化标题和副标题或通过自定义输入组件手动编辑这两个值。当用户决定编辑时,他们的输入应显示在 parent 组件上保存这些结果。

我有随机发生器和 child 组件发出更新的标题,但在更新 parents 和状态以在保存时显示自定义输入标题和副标题时遇到麻烦并获得当我将控制台日志放入商店操作部分的 updateTitleAndSubtitle() 时,标题和副标题均出现“未定义”错误。

此代码的 objective 挑战在于 return 商店的新值,并能够显示自定义输入,同时在用户决定使用它时让随机化器随手可得。

任何关于我做错或遗漏的方向将不胜感激。我已经阅读了 3 天有关 Vuex 和 Vue2 的文章,并且有 2 个月的 Vue 使用经验。


自定义输入Child组件:

<template>
  <div>
    <label for="title">Edit Title: </label>
    <input
      type="text"
      id="title"
      :updateTitle="updateTitle"
      v-model="inputTitle"
    />

    <label for="title">Edit Subtitle: </label>
    <input
      type="text"
      id="subtitle" :updateSubtitle="updateSubtitle"
      v-model="inputSubtitle"
    />

  </div>
</template>

<script>
export default {
  name: 'CustomInput',
  props: {
    title: String,
    subtitle: String,
  },
  computed: {
    updateTitle() {
      console.log('updateTitle: ', this.title);
      return this.title;
    },
    updateSubtitle() {
      console.log('updateSubtitle: ', this.subtitle);
      return this.subtitle;
    },
    inputTitle: {
      get() {
        console.log('set title: ', this.title);
        return this.title;
      },
      set(title) {
        console.log('set title: ', title);
        this.$emit('input', title);
      },
    },
    inputSubtitle: {
      get() {
        return this.subtitle;
      },
      set(subtitle) {
        console.log('set subtitle: ', subtitle);
        this.$emit('input', subtitle);
      },
    },
  },
};
</script>

Parent 分量:

<template>
  <main class="home-page page">

    <div v-if="!editMode" class="display-information">
      <div class="title">
        <span class="bold">Title: </span>{{title}}
      </div>

      <div class="subtitle">
        <span class="bold">Subtitle: </span>{{subtitle}}
      </div>

      <div class="controls">
        <button id="randomize-button" class="control-button" @click="randomizeTitleAndSubtitle">
          Randomize
        </button>
        <button id="edit-button" class="control-button" @click="onEdit">Edit</button>
      </div>
    </div>

    <div v-else class="edit-controls">

      <CustomInput
        :title="title"
        :subtitle="subtitle"
        @update="v => onSave(v)"
      />

      <div class="controls">
        <button id="cancel-button" class="control-button" @click="onCancel">Cancel</button>
        <button id="save-button" class="control-button" @click="onSave">Save</button>
      </div>
    </div>
  </main>
</template>

<script>
// @ is an alias to /src
import CustomInput from '@/components/CustomInput.vue';
import { mapState, mapActions } from 'vuex';

export default {
  name: 'Home',
  components: {
    CustomInput,
  },
  data() {
    return {
      editMode: false,
    };
  },
  computed: {
    ...mapState(['title', 'subtitle']),
  },
  methods: {
    ...mapActions(['randomizeHeadings', 'updateHeadings']),
    onEdit() {
      this.editMode = true;
    },
    onCancel() {
      this.editMode = false;
    },
    onSave(v) {
      this.editMode = false;
      this.title = v.title;
      this.subtitle = v.subtitle;
      this.updateTitleAndSubtitle(v);
    },
  },
  mounted() {
    this.randomizeHeadings();
  },
};

Vuex 商店:

import randomWords from 'random-words';

export default new Vuex.Store({
  state: {
    title: '',
    subtitle: '',
  },
  mutations: {
    UPDATE_TITLE(state, value) {
      state.title = value;
    },
    UPDATE_SUBTITLE(state, value) {
      state.subtitle = value;
    },
  },
  actions: {
    randomizeTitle({ commit }) {
      const newTitle = randomWords();
      commit('UPDATE_TITLE', newTitle);
    },
    randomizeSubtitle({ commit }) {
      const newSubtitle = randomWords();
      commit('UPDATE_SUBTITLE', newSubtitle);
    },
    randomizeTitleAndSubtitle({ dispatch }) {
      dispatch('randomizeTitle');
      dispatch('randomizeSubtitle');
    },
    updateTitleAndSubtitle({ commit }) {
      const payload = {
        title: this.title || null,
        subtitle: this.subtitle || null,
      };

      commit('UPDATE_TITLE', payload);
      commit('UPDATE_SUBTITLE', payload);
    },
  },
  modules: {
  },
});

我在本地开发环境中测试了您的代码,发现您的代码需要进行大量更改才能更好地工作。这是新的 vuex 商店代码:

vuex store:

export default new Vuex.Store({
    state: {
        title: '',
        subtitle: '',
    },
    mutations: {
        UPDATE_TITLE(state, value) {
            state.title = value;
        },
        UPDATE_SUBTITLE(state, value) {
            state.subtitle = value;
        },
    },
    actions: {
        randomizeTitle({ commit }) {
            const newTitle = randomWords();
            commit('UPDATE_TITLE', newTitle);
        },
        randomizeSubtitle({ commit }) {
            const newSubtitle = randomWords();
            commit('UPDATE_SUBTITLE', newSubtitle);
        },
        randomizeTitleAndSubtitle({ dispatch }) {
            dispatch('randomizeTitle');
            dispatch('randomizeSubtitle');
        },
        updateTitleAndSubtitle({ commit }, inputUser) {
            /* I changed the structure of this action to work correctly */
            console.log(inputUser);
            commit('UPDATE_TITLE', inputUser.title);
            commit('UPDATE_SUBTITLE', inputUser.subtitle);
        },
    },
    modules: {
    },
});

这里还有新的 Parent component 代码:

Parent component:

<template>
  <main class="home-page page">

    <div v-if="!editMode" class="display-information">
      <div class="title">
        <span class="bold">Title: </span>{{title}}
      </div>

      <div class="subtitle">
        <span class="bold">Subtitle: </span>{{subtitle}}
      </div>

      <div class="controls">
        <button id="randomize-button" class="control-button" @click="randomizeTitleAndSubtitle">
          Randomize
        </button>
        <button id="edit-button" class="control-button" @click="onEdit">Edit</button>
      </div>
    </div>

    <div v-else class="edit-controls">

      <CustomInput
          :title="title"
          :subtitle="subtitle"
          @titleEvent = "myFuncTitle"
          @subTitleEvent = "myFuncSubTitle"
      />
      <!--
      I removed this part from your component.
      @update="v => onSave(v)"

      and also added custom events (titleEvent and subTitleEvent) to the component
      -->

      <div class="controls">
        <button id="cancel-button" class="control-button" @click="onCancel">Cancel</button>
        <button id="save-button" class="control-button" @click="onSave">Save</button>
      </div>
    </div>
  </main>
</template>

<script>
// @ is an alias to /src
import CustomInput from '../components/CustomInput.vue';
import { mapActions } from 'vuex';

export default {
  name: 'Parent',
  components: {
    CustomInput,
  },
  data() {
    return {
      editMode: false,
      /* defining new data for handling "cancel" button functionality */
      temporaryTitle: "",
      temporarySubTitle: ""
    };
  },

  computed: {
    /* defining setter and getter for each computed value separately */
    title: {
      // getter
      get: function () {
        return this.$store.state.title;
      },
      // setter
      set: function (newValue) {
        this.$store.commit('UPDATE_TITLE', newValue);
      }
    },
    subtitle: {
      // getter
      get: function () {
        return this.$store.state.subtitle;
      },
      // setter
      set: function (newValue) {
        this.$store.commit('UPDATE_SUBTITLE', newValue);
      }
    },
  },
  methods: {
    /* changing the name of actions according to the names defined in "store" */
    ...mapActions(['randomizeTitleAndSubtitle', 'updateTitleAndSubtitle']),
    onEdit() {
      this.editMode = true;
      this.temporaryTitle = this.$store.state.title;
      this.temporarySubTitle = this.$store.state.subtitle;
    },
    onCancel() {
      this.editMode = false;
      this.$store.commit('UPDATE_TITLE', this.temporaryTitle);
      this.$store.commit('UPDATE_SUBTITLE', this.temporarySubTitle);
    },
    myFuncTitle(event) {
      console.log(event);
      /* we could not set values to "computed" properties, if we had not defined "set: function ..." for them above.  */
      this.title = event;
    },
    myFuncSubTitle(event) {
      this.subtitle = event;
    },
    onSave(v) {
      this.editMode = false;
      console.log(v); /* "v" is not related to your data. notice the console */
      // this.title = v.title;
      // this.subtitle = v.subtitle;
      const payload = {
        title: this.title,
        subtitle: this.subtitle,
      };
      this.updateTitleAndSubtitle(payload);
    },
  },
  created() {
    this.randomizeTitleAndSubtitle();
  },
};
</script>

最后是新 Custom Input 组件的代码:

Custom Input:

<template>
  <div>
    <label for="title">Edit Title: </label>
    <input
        type="text"
        id="title"
        v-model="inputTitle"
        @input="$emit('titleEvent', $event.target.value)"
    />
    <!-- emitting event like above code for each input -->

    <label for="title">Edit Subtitle: </label>
    <input
        type="text"
        id="subtitle"
        v-model="inputSubtitle"
        @input="$emit('subTitleEvent', $event.target.value)"
    />

  </div>
</template>

<script>
export default {
  name: 'CustomInput',
  props: {
    title: String,
    subtitle: String,
  },
  computed: {

    inputTitle: {
      get() {
        console.log('set title: ', this.title);
        return this.title;
      },
      set(title) {
        console.log('set title: ', title);
      },
    },
    inputSubtitle: {
      get() {
        return this.subtitle;
      },
      set(subtitle) {
        console.log('set subtitle: ', subtitle);
      },
    },
  },
};
</script>

<style scoped>

</style>

我试图对代码的一些更改发表评论,但主要更改与根据“商店”中定义的名称更改 mapActions 操作的名称以及 为计算属性提供setter

如果您对我的代码有疑问,我建议您多阅读 vue 和 vuex 文档,尤其是与 custom events and computed setters and vuex actions 相关的页面。