Search code examples
typescriptvuejs3multi-selectvue-composition-apiprimevue

How to pass dynamic ref to a computed property in Composition API?


I'm trying to write a single computed property that will match 2 reactive variables, by make it dynamic. I'm using Typescript, Vue3-Composition API and PrimeVue.

Using Prime Vue I have 2 similar MultiSelect components with this structure:

<MultiSelect
  v-model="selectedClients"
  :selected-items-label="itemsSelected"
  :options="clientsList"
  option-label="firstName"
  option-value="id"
/>

<MultiSelect
  v-model="selectedCars"
  :selected-items-label="itemsSelected"
  :options="carsList"
  option-label="carType"
  option-value="id"
/>

I want to bind selected-items-label to a computed property itemsSelected, something like this:

const itemsSelected = computed((selector: any) => {
  return selector.value.length > 1 ? "{0} items selected" : "{0} item selected";
});

I know I can use 2 diferent computed properties just by changing between selectedClients and selectedCars, but this seems to be redundant.

const accountsItemsSelected = computed(() => {
  return selectedClients.value.length > 1
    ? "{0} items selected"
    : "{0} item selected";
}); 

Can I pass a dynamic param to a computed property to achieve this with a single computed prop ?


Solution

  • You could use a higher order function:

    const counter = computed(
      () =>
        ({ length }) =>
          `${length || 'No'} item${length === 1 ? '' : 's'} selected`
    )
    

    Usage:

    <MultiSelect
      v-model="selectedClients"
      :selected-items-label="counter(selectedClients)"
      :options="clientsList"
      option-label="firstName"
      option-value="id"
    />
    <MultiSelect
      v-model="selectedCars"
      :selected-items-label="counter(selectedCars)"
      :options="carsList"
      option-label="carType"
      option-value="id"
    />
    

    Demo.

    Note: In the example above, the ref passed has to have a value with .length (an array or a string) at all times.

    If it's ever falsey (false, null, undefined, etc...), it will error.
    To allow falsey values, change it to:

    const counter = computed(
      () => (r) => `${r?.length || 'No'} item${r?.length === 1 ? '' : 's'} selected`
    )