0
votes

In a large Vue app using Vuetify, I have the following re-usable component used throughout the app:

<template>
  <v-combobox
    v-model="input"
    :items="available"
    :item-value="itemValue"
    :item-text="itemText"
    :label="label"
    :disabled="disabled"
    :hint="hint"
    chips
    multiple
    persistent-hint
    @input="myInput"
  >
    <template slot="selection" slot-scope="data">
      <v-chip
        :close="!disabled"
        @input="remove(data.item)"
      >
        <strong>{{ data.item.code }}</strong>
      </v-chip>
    </template>
  </v-combobox>
</template>

<script>
export default {
  name: 'BaseSelectChip',
  props: {
    hint: String,
    value: Array,
    label: String,
    available: Array,
    disabled: Boolean,
    itemText: {
      type: String,
      required: true,
    },
    itemValue: {
      type: String,
      required: true,
    },
  },
  computed: {
    input: {
      get() {
        return this.value;
      },
      set(val) {
        this.$emit('input', val);
      },
    },
  },
  methods: {
    myInput(items) {
      this.input = [...this.items];
      this.$emit('input', items);
    },
    remove(item) {
      const newItems = this.input.slice();
      newItems.splice(this.input.indexOf(item), 1);
      this.$emit('input', newItems);
    },
  },
};
</script>

and it is typically used like so:

<BaseSelectChip
  label="Liveness Options"
  :value="tenantBiometricLiveness"
  :available="getAvailableLiveness"
  :disabled="!hasBiometric"
  itemValue="code"
  itemText="name"
  :hint="livenessHint"
  @input="livenessUpdated"
/>

To this point, all of the implementations in my app have allowed multiple selections, but I have a case where I want to limit it to a single selection. Per the docs, it is a a boolean, but also says that this prop Accepts array for value. In addition, it states that the default value is false, but when it is passed by itself to v-combobox, it allows for multiple options to be selected. If I define a prop in BaseSelectChip like so:

props: {
  multiple: {
    type: Boolean,
    default: true,
  },
}

and then pass it to v-combobox

  <v-combobox
    v-model="input"
    :items="available"
    :item-value="itemValue"
    :item-text="itemText"
    :label="label"
    :disabled="disabled"
    :hint="hint"
    chips
    :multiple="multiple"
    persistent-hint
    @input="myInput"
  >

no matter how I pass it from the parent component, my screen is just blank, and I get no console warnings, which tells me Veutify is dying silently somewhere.

When I remove multiple from the v-combobox element, I get a blank item in my field, even though there is no item in the field (the items prop): blank chip in v-combobox

In addition, if there is valid value passed to the field, removing the multiple prop caused the data value to not be shown for some reason (the same result as when there is no data, as shown above).

So my questions are these: 1) How do I pass the value for the multiple prop to <BaseSelectChip> (and subsequently to v-combobox) as needed by v-combobox? 2) How can I configure the combobox to only allow one item to be selected at time, and only show a chip when an item is actually selected?

UPDATE: So to implement @skirtle's suggestion below: I modified BaseSelectChip like so:

  <v-combobox
    v-model="input"
    :items="available"
    :item-value="itemValue"
    :item-text="itemText"
    :label="label"
    :disabled="disabled"
    chips
    :multiple="multiple"
    @input="myInput"
  >

...

props: {
  multiple: {
      type: Boolean,
      required: false,
      default: true,
    },
  },

and then added :multiple="false" in the parent component. That seems to work fine, but there's now one side effect: in BaseSelectChip, the value that is emitted from myInput (coming from the @input event on the combobox) is an Observer instead of an Array. This causes problems in the parent component, because it's looking for an array. Why is it using an Observer?

1
You seem to have called the prop multiple but you're passing it to v-combobox as multipleItems.skirtle
Oh, sorry, that's a typo. I actually tried it multiple ways to see if the name of what I passed it made a difference (it didn't).wonder95
You should just need to pass :multiple="false" in the template that creates BaseSelectChip. That should get you equivalent behaviour to omitting the multiple attribute on v-combobox. But that's just the start, you've got several lines of code that assume the value will be an array. The docs line 'Accepts array for value' is a little confusing but it means that when multiple is true the value (bound with v-model) will be an array. If multiple is false then the value will no longer be an array.skirtle
Great, could you do me a favor and add that as an answer so I can respond? Thanks!wonder95

1 Answers

0
votes

Any prop that has a type of Boolean can be written using a shorthand to pass true. So when you write:

<v-combobox multiple>

That is just a shorthand for:

<v-combobox :multiple="true">

This is not specific to Vuetify, it is general Vue behaviour and applies to all components.

In your case you have set the default to true, so you will need to explicitly pass false:

<BaseSelectChip
  :multiple="false"
>

Be careful to include the : at the front or it'll pass the string 'false' instead, which is very different.

You mentioned an entry in the docs that said:

Accepts array for value

What this means is that if you set multiple to true then the value prop of v-combobox will need to be an array. In your code you aren't setting the prop value directly but you are setting it via v-model.

If multiple is set to false then value will not be expecting an array. As you're using v-model this will affect you in both directions. You have a few lines that seems to assume the value will be an array, so they will need changing.

This is probably why you're seeing a single, empty chip. I'm guessing that you're passing in an empty array. But with multiple set to false it'll just treat that array as the selected value. There's no special handling of arrays in that case, it's just a value like any other. It'll be looking for [].code and [].name for the item-value and item-text, which will be undefined.