Search code examples
javascriptvue.jsvuejs3vitevue-select

Vue3 and Vue-Select: How to emit child data to parent when using taggable prop?


I am using the Vue-Select library with Vue3. My objective is to let the user choose tags from an options list and also be able to create new tags if the tags do not exist in the options list.

This works fine in a child component, but I am having trouble passing/emitting the data up to the parent component. I need the data in the parent because I am going to package it up for form processing later.

How can I successfully get the tags from the child component and into the parent component's formData.tags property?

The code and sandbox link:

components/PostEditorTags.vue:

<template>
  <v-select
    :create-option="(tag) => ({ label: tag, value: tag })"
    v-model="selected"
    :options="options"
    multiple
    taggable
    @input="$emit('input', selected)"
    placeholder="add a tag"
  ></v-select>
  <br />
  child component data:
  <pre>{{ selected }}</pre>
</template>

<script setup>
import { ref } from 'vue';
import vSelect from 'vue-select';
import 'vue-select/dist/vue-select.css';

defineEmits(['input']);

const options = [
  { value: 'one', label: 'One' },
  { value: 'two', label: 'Two' },
  { value: 'three', label: 'Three' },
  { value: 'four', label: 'Four' },
  { value: 'five', label: 'Five' },
  { value: 'six', label: 'Six' },
  { value: 'seven', label: 'Seven' },
  { value: 'eight', label: 'Eight' },
  { value: 'nine', label: 'Nine' },
  { value: 'ten', label: 'Ten' },
];

let selected = ref([]);
</script>

components/Parent.vue:

<template>
  <h1>Post Tags</h1>
  <PostEditorTags @input="setTagsArr" />
  <br />
  <br />
  parent component data:
  <pre>{{ formData.tags }}</pre>
</template>

<script setup>
import PostEditorTags from './PostEditorTags.vue';

const formData = {
  // title: '',
  // content: '',
  tags: null,
};

function setTagsArr(x) {
  formData.tags = x;
}
</script>


Solution

  • Try with watcher instead of event

    components/PostEditorTags.vue:

    <template>
      <v-select
        :create-option="(tag) => ({ label: tag, value: tag })"
        v-model="selected"
        :options="options"
        multiple
        taggable
        placeholder="add a tag"
      ></v-select>
      <br />
      child component data:
      <pre>{{ selected }}</pre>
    </template>
    
    <script setup>
    import { ref, defineEmits, watch } from 'vue';
    import vSelect from 'vue-select';
    import 'vue-select/dist/vue-select.css';
    
    const options = [{ value: 'one', label: 'One' }, { value: 'two', label: 'Two' }, { value: 'three', label: 'Three' }, { value: 'four', label: 'Four' }, { value: 'five', label: 'Five' }, { value: 'six', label: 'Six' }, { value: 'seven', label: 'Seven' }, { value: 'eight', label: 'Eight' }, { value: 'nine', label: 'Nine' }, { value: 'ten', label: 'Ten' },];
    
    let selected = ref([]);
    
    watch(
      () => selected.value,
      (newValue, oldValue) => {
        act(newValue);
      }
    );
    
    const emit = defineEmits(['input'])
    const act = (val) => emit('input', val);
    </script>
    

    Make your data reactive in components/Parent.vue:

    <template>
      <h1>Post Tags</h1>
      <PostEditorTags @input="setTagsArr" />
      <br />
      <br />
      parent component data:
      <pre>{{ formData.tags }}</pre>
    </template>
    
    <script setup>
    import { reactive } from 'vue'
    import PostEditorTags from './PostEditorTags.vue';
    
    const formData = reactive({
      // title: '',
      // content: '',
      tags: null,
    });
    
    function setTagsArr(x) {
      formData.tags = x;
    }
    </script>
    

    your demo