开发者问题收集

Vue2-“TypeError:无法读取未定义的属性(读取‘值’)”

2022-03-06
3983

我有一个带有自定义输入组件的应用,我试图在保存时向父组件发出更改事件,并通过提交/分派将这些值发送到 $store。我确认父组件可以在我之前调试组件时接收这些值(正在检索最初从此应用中的随机器生成的相同值,并且不明白为什么我会收到此错误:

[Vue warn]: Error in v-on handler: "TypeError: Cannot read properties of undefined (reading 'value')"

found in

---> <CustomInput>

它仅在用户尝试手动编辑新标题/副标题时显示,并在每次击键后显示。这是计时错误吗??


自定义输入:

<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: {
    value: {
      type: Object,
      required: true,
    },
  },
  computed: {
    updateTitle() {
      console.log('updateTitle: ', this.value.title);
      return this.value.title;
    },
    updateSubtitle() {
      console.log('updateSubtitle: ', this.value.subtitle);
      return this.value.subtitle;
    },
    inputTitle: {
      get() {
        return this.value.title;
      },
      set(title) {
        console.log('setting new title: ', title);
        this.$emit('input', title);
      },
    },
    inputSubtitle: {
      get() {
        return this.value.subtitle;
      },
      set(subtitle) {
        console.log('setting new subtitle: ', subtitle);
        this.$emit('input', subtitle);
      },
    },
  },
};
</script>

父组件:

<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
        :value="{ title, subtitle }"
        @input="onSave(v = { title, subtitle }, $event.target.value)"
      />

      <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>
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(['randomizeTitleAndSubtitle', 'updateTitleAndSubtitle']),
    onEdit() {
      this.editMode = true;
    },
    onCancel() {
      this.editMode = false;
    },
    onSave(v) {
      this.editMode = false;
      console.log('returned value object: ', v);
      this.$store.dispatch('UPDATE_TITLE', v.title);
      this.$store.dispatch('UPDATE_SUBTITE', v.subtitle);
    },
  },
  mounted() {
    this.randomizeTitleAndSubtitle();
  },
};
</script>
2个回答

错误指向 @input

<CustomInput
   :value="{ title, subtitle }"                     ❌
   @input="onSave(v = { title, subtitle }, $event.target.value)"
   />

CustomInput 发出带有字符串的 input 事件:

export default {
  ⋮
  computed: {
    ⋮
    inputTitle: {
      ⋮
      set(title) {            👇
        this.$emit('input', title);
      },
    },
    inputSubtitle: {
      ⋮
      set(subtitle) {          👇
        this.$emit('input', subtitle);
      },
    },
  },
};

因此 $event 就是该字符串,它不包含 target 属性,从而导致您观察到的错误。

我认为您可能已从 <input> 元素上的 @input 复制了该代码,在这种情况下 $event 应该是 InputEvent 对象。

解决方案

解决问题,从标记中删除 .target.value

<CustomInput
   :value="{ title, subtitle }"              ✅
   @input="onSave(v = { title, subtitle }, $event)"
   />

demo

tony19
2022-03-07

弄清楚并清理与我最初寻找的内容相关的代码。在数据中添加一个空对象作为来自自定义输入组件发出的对象值的占位符,并从自定义输入组件中删除不必要的代码。将其清理为仅包含两个单独的 $emit 事件的最小值 ~ 只有一个!

自定义输入:

<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: 'CustomInput',
  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>

Waffles
2022-03-07