Robinho de Morais
Robinho de Morais

Reputation: 23

Event doesn't work with more than one tomselect in the component

I'm using tomselect with vuejs3 and I realized that when I use more than one tomselect in the same component and use the dropdown_close and item_remove events, the tomselect that was not used stops working these events, so I can't capture the moment of the user's action and perform a new report query and make it more dynamic for user navigation, how can I resolve this situation?

<template>
            <TomSelect
              v-model="filtroEmpPedido"
              class="w-full"
              refKey="tomSelectEmpPedido"
              id="tomSelectEmpPedido"
              multiple
              :options="{
                plugins: {
                  remove_button: {
                    title: 'Remover',
                  },
                  clear_button: {
                    title: 'Remover Todos',
                  },
                },
              }"
            >
              <option v-for="item in empresapedido" :key="item" :value="item.cd_emppedido">
                {{ item.ds_empresa }}
              </option>
            </TomSelect>

</template>

<script setup>
  const tomSelectTransp = ref();
  provide('bind[tomSelectTransp]', (el) => {
    tomSelectTransp.value = el;
  });


  const tomSelectEmpPedido = ref();
  provide('bind[tomSelectEmpPedido]', (el) => {
    tomSelectEmpPedido.value = el;
  });

  onMounted(async () => {
    tomSelectTransp.value.TomSelect.on('dropdown_close', () => {
      rastreio();
    });
    tomSelectTransp.value.TomSelect.on('item_remove', () => {
       rastreio();
    });


tomSelectEmpPedido.value.TomSelect.on('dropdown_close', () => {
      rastreio();
    });
    tomSelectEmpPedido.value.TomSelect.on('item_remove', () => {
      rastreio();
    });
  });
</script>

Component TomSelect

Main.vue

<template>
  <select
    v-select-directive="{ props, emit, computedOptions }"
    class="tom-select"
  >
    <slot></slot>
  </select>
</template>

<script setup>
import { computed, watch, toRaw, ref, onMounted, inject } from "vue";
import { setValue, init, updateValue } from "./index";
import dom from "@left4code/tw-starter/dist/js/dom";

const vSelectDirective = {
  mounted(el, { value }) {
    // Clone the select element to prevent tom select remove the original element
    const clonedEl = dom(el).clone().insertAfter(el)[0];
    dom(el).attr("hidden", true);

    // Initialize tom select
    setValue(clonedEl, value.props);
    init(el, clonedEl, value.props, value.emit, value.computedOptions);

    // Attach instance
    tomSelectRef.value = clonedEl;
  },
  updated(el, { value }) {
    const clonedEl = dom(el).next()[0];
    const modelValue = toRaw(value.props.modelValue);
    updateValue(
      el,
      clonedEl,
      modelValue,
      value.props,
      value.emit,
      value.computedOptions
    );

    // Attach instance
    tomSelectRef.value = clonedEl;
  },
};

const props = defineProps({
  options: {
    type: Object,
    default() {
      return {};
    },
  },
  modelValue: {
    type: [String, Number, Array],
    default: "",
  },
  refKey: {
    type: String,
    default: null,
  },
});

const emit = defineEmits();

const tomSelectRef = ref();
const bindInstance = () => {
  if (props.refKey) {
    const bind = inject(`bind[${props.refKey}]`);
    if (bind) {
      bind(tomSelectRef.value);
    }
  }
};

// Compute all default options
const computedOptions = computed(() => {
  let options = {
    ...props.options,
    plugins: {
      dropdown_input: {},
      ...props.options.plugins,
    },
  };

  if (Array.isArray(props.modelValue)) {
    options = {
      persist: false,
      create: true,
      onDelete: function (values) {
        return confirm(
          values.length > 1
            ? "Are you sure you want to remove these " +
                values.length +
                " items?"
            : 'Are you sure you want to remove "' + values[0] + '"?'
        );
      },
      ...options,
      plugins: {
        remove_button: {
          title: "Remove this item",
        },
        ...options.plugins,
      },
    };
  }

  return options;
});

// Watch value change
watch(
  computed(() => props.modelValue),
  () => {
    emit("change");
  }
);

onMounted(() => {
  bindInstance();
});
</script>

index.js

import dom from "@left4code/tw-starter/dist/js/dom";
import TomSelect from "tom-select";
import _, { clone } from "lodash";

const setValue = (el, props) => {
  if (props.modelValue.length) {
    dom(el).val(props.modelValue);
  }
};

const init = (originalEl, clonedEl, props, emit, computedOptions) => {
  // On option add
  if (Array.isArray(props.modelValue)) {
    computedOptions = {
      onOptionAdd: function (value) {
        // Add new option
        const newOption = document.createElement("option");
        newOption.value = value;
        newOption.text = value;
        originalEl.add(newOption);

        // Emit option add
        emit("optionAdd", value);
      },
      ...computedOptions,
    };
  }

  clonedEl.TomSelect = new TomSelect(clonedEl, computedOptions);

  // On change
  clonedEl.TomSelect.on("change", function (selectedItems) {
    emit(
      "update:modelValue",
      Array.isArray(selectedItems) ? [...selectedItems] : selectedItems
    );
  });
};

const getOptions = (options, tempOptions = []) => {
  options.each(function (optionKey, optionEl) {
    if (optionEl instanceof HTMLOptGroupElement) {
      getOptions(dom(optionEl).children(), tempOptions);
    } else {
      tempOptions.push(optionEl);
    }
  });

  return tempOptions;
};

const updateValue = (
  originalEl,
  clonedEl,
  modelValue,
  props,
  emit,
  computedOptions
) => {
  // Remove old options
  for (const [optionKey, option] of Object.entries(
    clonedEl.TomSelect.options
  )) {
    if (
      !getOptions(dom(clonedEl).prev().children()).filter((optionEl) => {
        return optionEl.value === option.value;
      }).length
    ) {
      clonedEl.TomSelect.removeOption(option.value);
    }
  }

  // Update border style
  dom(clonedEl.TomSelect.wrapper).css(
    "border-color",
    dom(originalEl).css("border-color")
  );

  // Add new options
  dom(clonedEl)
    .prev()
    .children()
    .each(function () {
      clonedEl.TomSelect.addOption({
        text: dom(this).text(),
        value: dom(this).attr("value"),
      });
    });

  // Refresh options
  clonedEl.TomSelect.refreshOptions(false);

  // Update value
  if (
    (!Array.isArray(modelValue) &&
      modelValue !== clonedEl.TomSelect.getValue()) ||
    (Array.isArray(modelValue) &&
      !_.isEqual(modelValue, clonedEl.TomSelect.getValue()))
  ) {
    clonedEl.TomSelect.destroy();
    dom(clonedEl).html(dom(clonedEl).prev().html());
    setValue(clonedEl, props);
    init(originalEl, clonedEl, props, emit, computedOptions);
  }
};

export { setValue, init, updateValue };

Upvotes: 1

Views: 404

Answers (0)

Related Questions