1

I am very new to Vuejs so although I can probably devise a solution myself by using a watcher or perhaps a lifecycle hook I would like to understand why the following does not work and what should be done instead.

The problem is that the mutated local data doesn't update whenever the component consumer changes the property cellContent. The parent owns cellContent so using the property directly is a no-no (Vue seems to agree).

<template>
  <textarea
    v-model="mutableCellContent"
    @keyup.ctrl.enter="$emit('value-submit', mutableCellContent)"
    @keyup.esc="$emit('cancel')">
  </textarea>
</template>

<script>
export default {
  name: 'CellEditor',
  props: ['cellContent', 'cellId'],
  data () {
    return {
      mutableCellContent: this.cellContent
    }
  }
}
</script>

<style>
...
</style>

2 Answers 2

1

In data (mutableCellContent: this.cellContent) you are creating a copy of the prop, that's why when the parent changes, the local copy (mutableCellContent) is not updated. (If you must have a local copy, you'd have to watch the parent to update it.)

Instead, you should not keep a copy in the child component, just let the state be in the parent (and change it through events emitted in the child). This is a well known the best practice (and not only in Vue, but in other frameworks too, if I may say it).

Example:

Vue.component('cell-editor', {
  template: '#celleditor',
  name: 'CellEditor',
  props: ['cellContent', 'cellId'],
  data () {
    return {}
  }
});

new Vue({
  el: '#app',
  data: {
    message: "Hello, Vue.js!"
  }
});
textarea { height: 50px; width: 300px; }
<script src="https://unpkg.com/vue"></script>

<template id="celleditor">
  <textarea
    :value="cellContent"
    @keyup.ctrl.enter="$emit('value-submit', $event.currentTarget.value)"
    @keyup.esc="$event.currentTarget.value = cellContent">
  </textarea>
</template>

<div id="app">
  {{ message }}
  <br>
  <cell-editor :cell-content="message" @value-submit="message = $event"></cell-editor>
  <br>
  <button @click="message += 'parent!'">Change message in parent</button>
</div>

Sign up to request clarification or add additional context in comments.

3 Comments

This was my initial solution as it seemed the simplest and what I was used to. The difference is you using :value and me using v-model. Gotta read up on when to use what.
v-model="something" is the same as :value="something" @input="something = $event.target.value". I'm using :value only exactly because I don't want the something = $event.target.valuepart, which is an assignment to a prop. Instead we emit an event and let the parent decide if it wants to edit the prop instead (if it edits, the change will update the child component automatically).
I also had to add a :key="currentEditCell" to the component element. Otherwise what was in the textarea buffer wasn't overridden by the new cellContent.
1

You have to create a watcher to the prop cellContent.

Vue.config.productionTip = false
Vue.config.devtools = false
Vue.config.debug = false
Vue.config.silent = true


Vue.component('component-1', {
  name: 'CellEditor',
  props: ['cellContent', 'cellId'],
  data() {
    return {
      mutableCellContent: this.cellContent
    }
  },
  template: `
    <textarea
      v-model="mutableCellContent"
      @keyup.ctrl.enter="$emit('value-submit', mutableCellContent)"
      @keyup.esc="$emit('cancel')">
    </textarea>
  `,
  watch: {
    cellContent(value) {
      this.mutableCellContent = value;
    }
  }
});

var vm = new Vue({
  el: '#app',
  data() {
    return {
      out: "",
      cellContent: ""
    }
  },
  methods: {
    toOut(...args) {
      this.out = JSON.stringify(args);
    },
    changeCellContent() {
      this.cellContent = "changed at " + Date.now();
    }
  }
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
  <component-1 :cell-content="cellContent" @value-submit="toOut" @cancel="toOut"></component-1>
  <p>{{out}}</p>
  <button @click="changeCellContent">change prop</button>
</div>

2 Comments

Any idea why mutatedCellContent isn't reactive to cellContent to begin with? Why is the watcher necessary?
Because the mutableCellContent attribute is initialized with the value of the` cellContent` property and is not the cellContent property itself. If you just want the reactive value, instead of intercepting your changes, you can use cellContent directly.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.