TotomInc / vue3-select-component

A flexible & modern select-input control for Vue 3.
https://vue3-select-component.vercel.app/
MIT License
56 stars 6 forks source link

Dynamically change the list of options rendered inside the menu #74

Closed foxhound87 closed 4 months ago

foxhound87 commented 4 months ago

Hello, I'm using the new menu header slot with a filter (a button switch) which changes the options model.

But when the option ref is updated then I get this error:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'label')
    at default (vue3-select-component.js?v=e162d33e:130:57)
    at Array.map (<anonymous>)

this is my setup:

<OnSelectV5
    v-model="model"
    @search="onSelectSearch"
    :options="options"
    >
    <template #menu-header>
      <<FILTERS HERE>>
    </template>
</OnSelectV5>

const model = ref();

const options = computed(() =>
  store.data.filter((plant: IPlant) => {
      // filter logic based on the filter ref
    })
    .map((item) => ({
      label: item.name,
      value: item.name,
    }))
);
foxhound87 commented 4 months ago

This also happens if I use a ref for options instead of computed and reassign the items to the options ref from a watch

foxhound87 commented 4 months ago

I have cloned the repo and trying to fix this:

should be related to those lines:

    getOptionLabel: (option: GenericOption) => option.label,
    getMultiValueLabel: (option: GenericOption) => option.label,

it seems to be fixed if I put a ? to option.label:

    getOptionLabel: (option: GenericOption) => option?.label,
    getMultiValueLabel: (option: GenericOption) => option?.label,
TotomInc commented 4 months ago

If I understand correctly this issue, you are dynamically changing the list of options by creating a filter on the #menu-header template.

Here's what I've done to reproduce the issue:

<script setup lang="ts">
import { computed, ref } from "vue";
import VueSelect, { type Option } from "vue3-select-component";

type UserOption = Option<number> & { username: string };

const activeUsers = ref<number[]>([]);
const usersFilter = ref<"it" | "hr">("it");

const itUsers: UserOption[] = [
  { label: "Alice", value: 1, username: "alice" },
  { label: "Bob", value: 2, username: "bob" },
  { label: "Charlie", value: 3, username: "charlie" },
  { label: "David", value: 4, username: "david" },
];

const hrUsers: UserOption[] = [
  { label: "Eva", value: 5, username: "eva" },
  { label: "Frank", value: 6, username: "frank" },
  { label: "Grace", value: 7, username: "grace" },
  { label: "Helen", value: 8, username: "helen" },
];

const userOptions = computed(() => usersFilter.value === "it" ? [...itUsers] : [...hrUsers]);

const switchFilter = () => {
  usersFilter.value = usersFilter.value === "it" ? "hr" : "it";
};
</script>

<template>
  <VueSelect
    v-model="activeUsers"
    :options="userOptions"
    :is-multi="true"
    placeholder="Pick users"
  >
    <template #menu-header>
      <button type="button" @click="switchFilter">
        Switch filter type
      </button>
    </template>
  </VueSelect>
</template>

To reproduce, open the menu, select a value, switch the filter. Check the console, it should have an error.

The issue arise everywhere getOptionLabel() and getMultiValueLabel() are called, because internally the component uses the :options prop to retrieve the label of the option(s):

{
  getOptionLabel: (option: GenericOption) => option.label,
  getMultiValueLabel: (option: GenericOption) => option.label,
}

However, selected options are not passed anymore since we've changed the filter. There are no work-arounds at the time of writing on vue3-select-component.


displayedOptions (new)

When passed to the component, only these options will be rendered on the menu. However, make sure you are passing all possible options in the options prop.

@foxhound87, what do you think about it? You will use the displayedOptions to display the list of options you want to be rendered with your custom filter.

foxhound87 commented 4 months ago

Hello, thank you for your investigation of the issue. How displayedOptions should be used? Is it to be released still?

Do you mean we should have both options with all the available options and displayedOptions with only the options we want to show?

TotomInc commented 4 months ago

That's right, displayedOptions should be used in conjunction with options, where:

I'm pushing this feature on a branch soon. Would you be down to read the changes?

foxhound87 commented 4 months ago

Thank you I will take a look!

TotomInc commented 4 months ago

Should be fixed on v0.4.0.

See: