Skip to content
Advertisement

Mustache result is not updated

I was just playing around with Vue.js and found a problem I cannot explain why it happens. Here is my MWE:

Vue.component('list-entry', {
  'template': '<input type="text" :value="t" @input="fireEvent()" ref="text">',
  'props': ['t', 'idx'],
  'methods': {
    fireEvent() {
      this.$emit('update:t', {
        'value': this.$refs.text.value,
        'idx': this.idx
      })
    }
  }
})

new Vue({
  el: "#app",
  data: () => ({
    texts: [
      'Foo', 'Bar', 'Baz'
    ]
  }),
  methods: {
    update(ev) {
      console.log('Set ' + ev.value + ' at index ' + ev.idx)
      this.texts[ev.idx] = ev.value
      console.log(this.texts)
    }
  }
})
input {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <div class='container'>
    <list-entry 
      v-for="(t, idx) in texts" 
      :key="t" 
      :t="t" 
      :idx="idx" 
      @update:t="update($event)"
      ></list-entry>
  </div>
  {{ texts }}
</div>

Let me explain it a bit. There is a global data property texts that contains a few strings. For each of these strings, a custom component list-entry is generated. The strings are propagated using v-bind to these components.

Upon change of the text in the inputs, I want to update the global data property. Therefore a update:t event is fired and listened on in the main app. Here the update(ev) function should do the things.

If you run it you will see that in the console, the corresponding messages appear and the array is updated as well. However, in the output on the HTML screen (replaced from {{ texts }}), the value is not updated.

Now I am confused. Is the data property updated or not? Why is it not visible in the mustache output? Why is it correctly output in the console at the same time?

Advertisement

Answer

You could achieve the desired behavior with a short code by using Using v-model on Components

Vue.component('list-entry', {
  'template': `<input type="text" :value="value" @input="$emit('input', $event.target.value)" />`,
  props: ['value'],
})

new Vue({
  el: "#app",
  data: () => ({
    texts: [
      'Foo', 'Bar', 'Baz'
    ]
  }),

})
input {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <div class='container'>
    <list-entry v-for="(t, idx) in texts" :key="idx" v-model="texts[idx]"></list-entry>
  </div>
  {{ texts }}
</div>
Advertisement