[Vuejs]-Unexpected behavior from datalist in vue while using vue directives (v-model, @input) on input field?

1đź‘Ť

âś…

The code works as you wrote it:

  1. <p>{{input}}</p> – this is the first line. On init it’s empty, so now lines are shown.

  2. <input v-model="input" list="list" /> this is the second line, but on init it is displayed first.

So, when your app reacts to data change, the input is pushed one line down. If you initialize your input data with something else than '', then you can see that nothing unexpected happens (OK, maybe unexpected, but not extraordinary 🙂 ):

new Vue({
  el: '#app',
  data: {
    input: '',
    list: [{
        name: "Item 1",
        code: "i1"
      },
      {
        name: "Item 2",
        code: "i2"
      },
      {
        name: "Item 3",
        code: "i3"
      },
      {
        name: "Item 4",
        code: "i4"
      },
    ]
  },
  methods: {
    loseFocus(evt) {
      evt.target.blur()
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <p>{{ input || 'Data comes here' }}</p>
  <input v-model="input" list="list" @input="loseFocus" />
  <datalist id="list">
    <option v-for="item in list" :value="item.code">{{item.name}}</option>
  </datalist>
</div>

EDIT

The problem was not the “jumping” of the input, but that the datalist appeared open after picking an option.

The problem is that this element stays focused after the input/change event, so it behaves in an unwanted way. (But as it’s supposed to behave.)

I added @input on the element, and created a method to blur the element (so it loses focus, and the datalist doesn’t open/closes).

2nd EDIT

We discussed the question a bit more in the chat, and came up a with a snippet that is closer to the actual solution @KshitijDhakal sought:

new Vue({
  el: "#app",
  data: {
    input: '',
    list: [{
        type: 'posts',
        title: 'Posts',
        code: 'i1'
      },
      {
        type: 'albums',
        title: 'Albums',
        code: 'i2'
      }
    ],
    singleItem: {
      title: ''
    }
  },
  methods: {
    fetchData: function(type, id) {
      return fetch(`https://jsonplaceholder.typicode.com/${type}/${id}`)
        .then(response => response.json())
        .then(json => {
          return json
        })
    },
    onSelect: async function(e) {
      if (this.list.map(el => el.code).includes(e.target.value)) e.target.blur()
      this.singleItem = await this.fetchData(this.list.find(el => el.code === e.target.value).type, 1)
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <input v-model="input" list="list" @input="onSelect" />
  <datalist id="list">
    <option v-for="item in list" :value="item.code">{{item.title}}</option>
  </datalist>
  <div>
    <h2>
      {{singleItem.title}}
    </h2>
  </div>
</div>
👤muka.gergely

Leave a comment