Vue组成API在v-for内部不具有反应性?

时间:2020-10-22 16:17:28

标签: vuejs2 vuetify.js vue-composition-api

我正在使用Vue 2和Vuetify(不是Vue 3)创建表单构建器网站。在发现有问题之前,我一直做得很好。就是这种情况。我使用以下代码从反应性数组渲染了文本字段(输入)。

<template>
// ... some other unrelated code
<template v-if="answer.type !== 1">
  <v-col 
    v-for="(_, i) in answer.options" 
    :key="`#question-${answer.id}-${i}`" 
    cols="12"
  >
    <div class="flex flex-row justify-between items-center">
      <TextField
        v-model="answer.options[i]"
        class="ml-4" 
        label="Option"
        underlined 
        hideLabel 
      />
      <v-btn @click="deleteOption(i)" icon>
        <v-icon>mdi-close</v-icon>
      </v-btn>
    </div>
  </v-col>
  <v-col>
    <TextButton @click="addOption()" text="+ ADD OPTION" />
  </v-col>
</template>
// ... some other unrelated code
</template>

<script>
  import { reactive, ref, watch } from '@vue/composition-api'
  import useInquiry from '@/composables/useInquiry'
  import TextButton from '@/components/clickables/TextButton.vue'
  import TextField from '@/components/inputs/TextField.vue'

  export default {
    components: { TextField, TextButton },
    setup() {
      const { answer, addOption, deleteOption } = useInquiry()

      return { answer, addOption, deleteOption }
    }
  }
</script>

这是我的useInquiry可组合逻辑

import { reactive, watch, se } from '@vue/composition-api'
import ID from '@/helpers/id'

export default () => {
  const answer = reactive({
    id: ID(), // this literally just generates an ID
    type: 2,
    options: ['', '']
  })

  const addOption = () => {
    answer.options.push('')
  }

  const deleteOption = at => {
    const temp = answer.options.filter((_, i) => i !== at)
    answer.options = []
    answer.options = temp
  };

  return { answer, addOption, deleteOption }
}

最后,这是我的TextField.vue

<template>
  <v-text-field 
    v-model="inputValue"
    :label="label"
    :single-line="hideLabel"
    :type="password ? 'password' : 'text'"
    :outlined="!underlined"
    :dense="!large"
    hide-details="auto"
  />
</template>

<script>
  import { ref, watch } from '@vue/composition-api'

  export default {
    model: {
      props: 'value',
      event: 'change'
    },
    props: {
      label: String,
      value: String,
      password: Boolean,
      underlined: Boolean,
      large: Boolean,
      hideLabel: Boolean
    },
    setup(props, context) {
      const inputValue = ref(props.value)

      watch(inputValue, (currInput, prevInput) => {
        context.emit('change', currInput)
      })

      return { inputValue }
    }
  }
</script>

问题是,每当单击删除按钮时,即使我没有单击最后一个,被删除的输入也始终是数组中的最后一个。我试图通过使用Vue的composition watch方法观看它来记录我的反应性数组。显然,数据已正确更新。问题在于v模型看起来不同步,最后一个输入始终是被删除的输入。

1 个答案:

答案 0 :(得分:1)

对我很好.... TextField.vue

问题在TextField.vue中。您在setup的{​​{1}}内部实际所做的是(Vue 2 API):

TextField.vue

...这是data() { return { inputValue: this.value } } 数据成员的一次一次初始化,其值为inputValue prop。因此,当删除value之一并重用组件时(因为Vue一直都在这样做-尤其是在options中使用索引时):key不会更新为新值inputValue道具中的...

您根本不需要valueinputValue选项,只需将其删除并使用此模板即可:

model

请注意,在我的示例中,我使用的是<v-text-field :value="value" @input="$emit('input', $event)" :label="label" :single-line="hideLabel" :type="password ? 'password' : 'text'" :outlined="!underlined" :dense="!large" hide-details="auto" /> 而不是$event.target.value,因为我使用的是本机$event元素,而不是Vuetify的自定义组件输入...

工作示例...

<input>
const {
  reactive,
  ref,
  watch
} = VueCompositionAPI
Vue.use(VueCompositionAPI)

const BrokenInput = {
  model: {
    props: 'value',
    event: 'change'
  },
  props: {
    value: String,
  },
  setup(props, context) {
    const inputValue = ref(props.value)

    watch(inputValue, (currInput, prevInput) => {
      context.emit('change', currInput)
    })

    return {
      inputValue
    }
  },
  template: `<input type="text" v-model="inputValue" />`
}

const FixedInput = {
  props: {
    value: String,
  },
  template: `<input type="text" :value="value" @input="$emit('input', $event.target.value)"/>`
}

const useInquiry = () => {
  const answer = reactive({
    id: 1,
    type: 2,
    options: ['1', '2', '3', '4', '5']
  })

  const addOption = () => {
    answer.options.push('')
  }

  const deleteOption = at => {
    const temp = answer.options.filter((_, i) => i !== at)
    answer.options = temp
  };

  return {
    answer,
    addOption,
    deleteOption
  }
}

const app = new Vue({
  components: { 'broken-input': BrokenInput, 'fixed-input': FixedInput },
  setup() {
    return useInquiry()
  },
})
app.$mount('#app')