Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to merge 2 third party components in VueJS

I am using Form Tags components bootstrap-vue framework. I want to use vue-simple-suggest component (from npm) with form tags to suggest words related to user's query. User can select multiple words from suggestion, and the selected word will be saved in form tags as a pill as shown in image below.

enter image description here

I don't know how to merge both the components as a single component (or more better way), so that I can use UI feature of bootstrap with auto-suggest feature of a third party module.

I am learning VueJs, I don't know what should I learn to do this?

Here is my code:

<template>
  <div>
    <vue-simple-suggest
      v-model="chosen"
      mode="select"
      :list="simpleSuggestionsList"
      :filter-by-query="true"
      :destyled="false"
    >

      <b-form-tags 
        placeholder="Enter Keyword"
        size="lg"
        tag-variant="success"
        tag-pills
        remove-on-delete
        separator=","
        class="my-3"
        @input="updateValue"
      ></b-form-tags>

    </vue-simple-suggest>

  </div>
</template>

<script>
import VueSimpleSuggest from 'vue-simple-suggest'
import 'vue-simple-suggest/dist/styles.css'

export default {
  name: "SeedWordsSuggestions",

  data() {
    return {
      chosen: '',
      seedWords: []
    }
  },

  components: {
    VueSimpleSuggest
  },

  methods: {

    simpleSuggestionsList() {
      return [
        'Angular',
        'ReactJs',
        'VueJs'
      ]
    },

    addSelectedWord(e) {
      console.log(`addSelectedWord`, e)
    },

    updateValue(value) {
      const pos = value.length
      this.seedWords.push(value[pos - 1])
      console.log(this.seedWords)
    }
  }
}
</script>

<style scoped>

</style>
like image 258
Divya Avatar asked Oct 28 '22 01:10

Divya


1 Answers

The following solution hacks together the two components to create an editable combobox with tag-pills.

According to the caveat docs of vue-simple-suggest, its custom input components must emit the input, focus and blur events, as well as have a value prop. In addition, there are a few undocumented events that are required from the component: click, keydown, and keyup.

b-form-tags has a value prop, but is missing several of the required events. However, you could access its internal input element to attach your own event handlers that forward-$emit the events:

export default {
  async mounted() {
    // wait a couple ticks to ensure the inner contents
    // of b-form-tags are fully rendered
    await this.$nextTick()
    await this.$nextTick()

    // <b-form-tags ref="tags">
    const input = this.$refs.tags.getInput()
    const events = [
      'focus',
      'blur',
      'input',
      'click',
      'keydown',
      'keyup'
    ]
    events.forEach(event =>
      input.addEventListener(event, e => this.$refs.tags.$emit(event, e))
    )
  },
}

The changes above alone will cause the vue-simple-suggest to properly appear/disappear when typing. However, it doesn't add/remove tags when interacting with the auto-suggestions. That behavior could be implemented by the following features:

  1. ENTER or TAB keypress causes hovered auto-suggestion to be added as a tag. If nothing hovered, the keypress adds the first auto-suggestion as a tag.
  2. Clicking auto-suggestion adds the auto-suggestion as a tag.
  3. BACKSPACE on an auto-suggestion tag deletes it.

Feature 1 implementation:

  1. Add refs to the vue-simple-suggest and b-form-tags so that we could access the components in JavaScript later:
<vue-simple-suggest ref="suggest">
  <b-form-tags ref="tags" />
</vue-simple-suggest>
  1. Add a keydown-handler on the inner input of b-form-tags:
export default {
  mounted() {
    //...

    // <b-form-tags ref="tags">
    const input = this.$refs.tags.getInput()
    input.addEventListener('keydown', e => this.onKeyDown(e))
  },
}
  1. Implement the handler as follows:
export default {
  methods: {
    async onKeyDown(e) {
      if (e.key === 'Enter' || e.key === 'Tab') {
        // prevent default so that the auto-suggestion isn't also
        // added as plaintext in b-form-tags
        e.preventDefault()

        // <vue-simple-suggest ref="suggest">
        if (this.$refs.suggest.hovered) {
          this.$refs.tags.addTag(this.$refs.suggest.hovered)

        } else {
          const suggestions = await this.$refs.suggest.getSuggestions(e.target.value)
          if (suggestions.length > 0) {
            this.$refs.tags.addTag(suggestions[0])

          } else {
            // no match, so clear chosen
            this.chosen = ''
          }
        }
      }
    }
  }
}
  1. To prevent conflict with our handler, disable b-form-tag's automatic tag-adding upon ENTER by adding no-add-on-enter prop:
<b-form-tags no-add-on-enter />

Feature 2 implementation:

  1. Bind a suggestion-click-event handler:
<vue-simple-suggest @suggestion-click="onSuggestionClick">
  1. Implement the handler as follows:
export default {
  methods: {
    onSuggestionClick(suggestion) {
      this.$refs.tags.addTag(suggestion);
    },
  }
}

Feature 3 implementation:

  1. Add the remove-on-delete prop to b-form-tags:
<b-form-tags remove-on-delete />

full demo


As an aside, you might be better off with Vuetify's v-combobox, which supports the combination of the two components you're trying to merge, but I'll leave that to you to explore :)

like image 57
tony19 Avatar answered Nov 12 '22 16:11

tony19