0

I'm a beginner with Vue and JS and I'm struggling to update the Vue instance's data attribute based on user input. This is what I have as the template for the Vue component (located inside taskTemplate variable):

<div>
  <input type="text" placeholder="Insert your task" v-model="desc"/>
  <p>{{ desc }}</p>
</div>

The Vue component is defined as follows:

Vue.component("task-item", {
  props: {
    id: Number,
    desc: String,
  },
  template: taskTemplate
});

And this is populated in the HTML as follows:

<div id="task-list">
  <task-item
    v-for="item in taskList"
    v-bind="item"
  ></task-item>
  <div id="add-more">
    <button v-on:click="newTask" type="button">Add a new task</button>
  </div>
</div>

Where the task list is created with the Vue instance:

var app = new Vue({
  el: "#task-list",
  data: {
    taskList: [
      { id: 1, desc: "" },
      { id: 2, desc: "" },
    ]
  },
  methods: {
    newTask() {
      this.taskList.push({ id: this.taskList.length + 1, desc: "" })
    }
  }
});

My problem is that after updating the input element in the webpage, the component's property gets updated, but if I type in the console app.taskList[0].desc it still returns an empty string.

The end goal is to send the data the user has introduced in an API call, so if I can access Vue components instead of the taskList within the data property it is still ok. I would like to know the best practices here as well.

1

2 Answers 2

1

Props shouldn't be used in a two-way binding. Instead, bind their value to input :value, and emit any changes to the parent component.

<div>
  <input 
     type="text" 
     placeholder="Insert your task" 
     :value="desc"
     @input="$emit('update:desc', $event.target.value)"
  />
  <p>{{ desc }}</p>
</div>

In the parent component then you have to listen to update event and update the source value:

<div id="task-list">
  <task-item
    v-for="item in taskList"
    :key="item.id" // do not forget about key
    v-bind="item"
    @update:desc="item.desc = $event"
    // or you can use the sync modifier to listen to update events for all item's props.
    // v-bind.sync="item"
  ></task-item>
  <div id="add-more">
    <button v-on:click="newTask" type="button">Add a new task</button>
  </div>
</div>
Sign up to request clarification or add additional context in comments.

Comments

1

Try to use :value and @input instead v-model :

Vue.component('task-item', {
  template: `
    <div>
      <input type="text" 
             placeholder="Insert your task" 
             :value="desc"
             @input="$emit('update', $event.target.value)"/>
      <p>{{ desc }}</p>
    </div>
  `,
  props: {
    id: Number,
    desc: String,
  },
  //template: taskTemplate
})

new Vue({
  el: '#demo',
  data: {
    taskList: [
      { id: 1, desc: "" },
      { id: 2, desc: "" },
    ]
  },
  methods: {
    newTask() {
      this.taskList.push({ id: this.taskList.length + 1, desc: "" })
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
  <div id="task-list">
    <task-item
      v-for="(item, index) in taskList"
      :key="index"
      v-bind="item"
      @update="item.desc = $event"
    ></task-item>
    <div id="add-more">
      <button v-on:click="newTask" type="button">Add a new task</button>
    </div>
  </div>
</div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.