Vuex Action 提交突变

Vuex Action committing mutation

我有一个 vue 应用程序,用户可以在其中随机设置标题和副标题,或者使用自定义输入组件编辑字段。

当用户选择编辑时,我想将更新后的标题和副标题从输入组件发送到商店,以便在填写所需的值后单击保存按钮时改变标题和副标题状态输入组件。

目前能够将值从 parent 传递到 child 并且有一个 emit present 供 parent 收听,但是,我不确定如何更新原始的值到自定义值并作为 $emit.

的结果得到“未定义”

我似乎找不到解决这个问题的方法,我去过的所有论坛都没有帮助,所以我真的希望这里有人可以帮助我解决我的问题;非常感谢。

Parent.vue

<template>
  <main class="home-page page">
    <div v-if="!editMode">
      <div>
        <span>Title: </span>{{title}}
      </div>

      <div>
        <span>Subtitle: </span>{{subtitle}}
      </div>

      <div>
        <button @click="randomizeTitleAndSubtitle">
          Randomize
        </button>
        <button @click="onEdit">Edit</button>
      </div>
    </div>

    <div v-else>

      <DoubleInput
        :value="{ title, subtitle }"
      />

      <div>
        <button @click="onCancel">Cancel</button>
        <button @click="onSave">Save</button>
      </div>
    </div>
  </main>
</template>

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

export default {
  name: 'Parent',
  components: {
    DoubleInput,
  },
  data() {
    return {
      editMode: false,
    };
  },
  computed: {
    ...mapState(['title', 'subtitle']),
  },
  methods: {
    ...mapActions(['randomizeTitleAndSubtitle', 'updateTitleAndSubtitle']),
    onEdit() {
      this.editMode = true;
    },
    onCancel() {
      this.editMode = false;
    },
    onSave() {
      this.editMode = false;
      const newTitle = this.title;
      const newSubtitle = this.subtitle;
      this.updateTitleAndSubtitle({ newTitle, newSubtitle });
    },
  },
  mounted() {
    this.randomizeTitleAndSubtitle();
  },
};
</script>

Child.vue

<template>
  <div>
    <label>Edit Title: </label>
    <input type="text" ref="title" :value="value.title" @input="updateValue()" />

    <label>Edit Subtitle: </label>
    <input type="text" ref="subtitle" :value="value.subtitle" @input="updateValue()" />

  </div>
</template>

<script>
export default {
  name: 'Child',
  props: ['value'],
  methods: {
    updateValue() {
      this.$emit('input', {
        title: this.$refs.title.value,
        subtitle: this.$refs.subtitle.value,
      });
    },
  },
};
</script>

商店

import Vue from 'vue';
import Vuex from 'vuex';
import randomWords from 'random-words';

Vue.use(Vuex);

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 }, value) {
      const payload = {
        title: value.title || null,
        subtitle: value.subtitle || null,
      };

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

您在 onSave() 方法中对 updateTitleAndSubtitle 的调用未将新标题和副标题传递给操作。

onSave() {
  this.editMode = false;
  this.updateTitleAndSubtitle({ title: this.title, subtitle: this.subtitle });
},

此外,我会犹豫使用 state.titlestate.subtitle 作为您的编辑模式变量,因为按照惯例您应该只通过突变更改这些值。相反,将本地变量作为 prop 传递给 child 组件并使用它调用方法。

<DoubleInput v-model="title_subtitle" />
<script>
  // ...
  data() {
    return {
      // ...
      title_subtitle: {},
    };
  },
  // ...
  methods: {
    onEdit() {
      this.editMode = true;
      this.title_subtitle = {
        title: this.title,
        subtitle: this.subtitle,
      };
    },
    // ...
    onSave() {
      this.editMode = false;
      this.updateTitleAndSubtitle(this.title_subtitle);
    },
  },
  // ...
}

鉴于您的评论,代码不必要地复杂的原因就更有意义了。只要您的 @input 处理程序不尝试写回它,就可以将您的状态变量作为道具传递。你不需要本地变量。试试这个:

<CustomInput :value="{ title: this.title, subtitle: this.subtitle }" @input="onSave" />

  onSave(value) {
    this.editMode = false;
    this.updateTitleAndSubtitle(value);
  }

我遇到最大问题的地方是 Vuex 商店,而不是我想的父子生命周期。 emit 工作正常,需要将一些计算属性添加到自定义输入组件中。我接近商店的方式是完全倒退的,并且将 updateTitleAndSubtitle() 操作破坏为如下所示。最后,添加一个 @input 将更新后的值对象发送到 onEdit() 以将值设置为数据中的空对象。然后,使用具有新值的对象 dispatch/commit 到商店! Vualá ~ 所需的行为,没有错误,并最终用一些时间弄清楚了。

我缺少的是将新发出的数据对象传递给存储操作,然后改变状态。这个代码挑战背后的整个概念是从商店获取数据,通过组件修改数据,将修改后的数据发送回商店,然后更改状态。这有点矫枉过正,但这是我在工作中解决现有应用程序中更大问题所需的实践和概念。

这是代码分解!

自定义输入:

<template>
  <div>
    <label for="title">Edit Title: </label>
    <input
      type="text"
      id="title"
      :setTitle="setTitle"
      ref="title"
      :value="value.title"
      @input="updateValue()"
    />

    <label for="title">Edit Subtitle: </label>
    <input
      type="text"
      id="subtitle"
      :setSubtitle="setSubtitle"
      ref="subtitle"
      :value="value.subtitle"
      @input="updateValue()"
    />

  </div>
</template>

<script>
export default {
  name: 'DoubleInput',
  props: {
    value: {
      type: Object,
      required: true,
    },
  },
  computed: {
    setTitle() {
      // console.log('set title: ', this.value.title);
      return this.value.title;
    },
    setSubtitle() {
      // console.log('set subtitle: ', this.value.subtitle);
      return this.value.subtitle;
    },
  },
  methods: {
    updateValue() {
      this.$emit('input', {
        title: this.$refs.title.value,
        subtitle: this.$refs.subtitle.value,
      });
    },
  },
};
</script>

家长:

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

    <!-- <span class="bold">Title:</span> {{ title }} <br>
    <span class="bold">Subtitle:</span> {{ subtitle }}

    <hr> -->

    <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
        :value="{ title, subtitle }"
        @input="v => onEdit(v)"
      />     

      <div class="controls">
        <button id="cancel-button" class="control-button" @click="onCancel">Cancel</button>
        <button id="save-button" class="control-button" @click="onSave(v)">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,
      v: {},
    };
  },
  computed: {
    ...mapState(['title', 'subtitle']),
  },
  methods: {
    ...mapActions(['randomizeTitleAndSubtitle', 'updateTitleAndSubtitle']),
    onEdit(v) {
      this.editMode = true;
      this.v = v;
      // console.log('returned value object: ', v);
    },
    onCancel() {
      this.editMode = false;
    },
    onSave() {
      this.editMode = false;
      this.$store.dispatch('updateTitleAndSubtitle', this.v);
    },
  },
  mounted() {
    this.randomizeTitleAndSubtitle();
  },
};
</script>

<style lang="stylus" scoped>
.bold
  font-weight bold

.controls
  width 100%
  display flex
  justify-content space-around
  max-width 20rem
  margin-top 2rem
  margin-left auto
  margin-right auto

.control-button
  height 2.5rem
  border-radius 1.25rem
  background-color white
  border 0.125rem solid black
  padding-left 1.25rem
  padding-right 1.25rem

  &:hover
    cursor pointer
    background-color rgba(0, 0, 0, 0.1)
</style>

商店:

import Vue from 'vue';
import Vuex from 'vuex';
import randomWords from 'random-words';

Vue.use(Vuex);

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);
    },
    setTitle({ commit }, value) {
      commit('UPDATE_TITLE', value);
    },
    setSubtitle({ commit }, value) {
      commit('UPDATE_SUBTITLE', value);
    },
    randomizeTitleAndSubtitle({ dispatch }) {
      dispatch('randomizeTitle');
      dispatch('randomizeSubtitle');
    },
    updateTitleAndSubtitle({ dispatch }, value) {
      dispatch('setTitle', value.title);
      dispatch('setSubtitle', value.subtitle);
    },
  },
  modules: {
  },
});