vue.jsvuejs2vuetify.js

setting up v-autocomplete with search function


Trying to setup v-autocomplete, without a watcher, the flow would be:

  1. Type a string, value is accepted by function
  2. Function searches api for string and returns a list
  3. List is put into "entries"
  4. Computed property "tagsFound" is reevaluated.
  5. "tagsFound" are displayed (since they are :items) The main difference between the docs here and my code is my attempt to do this without a watcher rather with a simple function. Relevant code:
<v-autocomplete
          v-model="newTag"
          :items="tagsFound"
          :loading="loading"
          :search-input.sync="search"
          color="white"
          hide-no-data
          hide-selected
          :placeholder="$t('search_terms.new_tag')"
        ></v-autocomplete>
    ...
    data() {
      return {
        newTag: '',
        entries: [],
    ....
    methods: {
    ...
     async search(term){
        this.query.term = term
        this.entries = await this.searchTerms(this.query)
      },
    ...
    computed: {
      tagsFound(){
        return this.entries
      }
    }

Expected behavior is search for the term typed and display the results as a dropdown. Actual behavior is that it does not search and therefore does not display anything.


Solution

  • The sync modifier effectively makes a prop behave like v-model, so just like with v-model there's a prop and an event. The value needs to be a property, not a method, so :search-input.sync="search" doesn't make sense if search is a method.

    The tagsFound computed property in your example isn't really doing anything. If you're just going to return entries you might as well just use entries directly in your template.

    Not sure why you would want to do this without a watch but it can be done, either by splitting search-input.sync into a prop/event pair or by using a computed property with a getter and setter. The example below uses the latter approach.

    function fakeServer (search) {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve([
            'Red', 'Yellow', 'Green', 'Brown', 'Blue', 'Pink', 'Black'
          ].filter(c => c.toLowerCase().includes(search.toLowerCase())))
        }, 1000)
      }) 
    }
    
    new Vue({
      el: '#app',
    
      data () {
        return {
          newTag: '',
          entries: [],
          queryTerm: ''
        }
      },
    
      computed: {
        search: {
          get () {
            return this.queryTerm
          },
          
          set (searchInput) {
            if (this.queryTerm !== searchInput) {
              this.queryTerm = searchInput
              this.loadEntries()
            }
          }
        }
      },
      
      created () {
        this.loadEntries()
      },
    
      methods: {
        async loadEntries () {
          this.entries = await fakeServer(this.queryTerm || '')
        }
      }
    })
    <link href="https://unpkg.com/vuetify@1.5.16/dist/vuetify.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet">
    <script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>
    <script src="https://unpkg.com/vuetify@1.5.16/dist/vuetify.js"></script>
    <div id="app">
      <v-app>
        <v-autocomplete
          v-model="newTag"
          :items="entries"
          :search-input.sync="search"
        ></v-autocomplete>
      </v-app>
    </div>