QSelect component

QSelect allows your user to select from a variety of options. Its properties and functionalities are similar to the ones from QInput, including label behavior and error handling.

Requirements

TypePath / VersionPurposeOptional
Styles../../assets/main.cssCSS VariablesNo
Functions../../use/uuidAssign ids to itemsNo
Components../../components/Form/QButton.vueFull form exampleYes

Usage

Import the following component/s:

import QSelect from '../../components/Form/QSelect.vue';

Placeholder prefix

QSelect uses the label as a first, inactive element in the options. In case the label alone is not enough, you can extend it with a prefix. Doing so makes the placeholder become lower case.

Output:

Example

<template>
  <form @submit.prevent="onSubmit">
    <q-select labelPrefix="Please " label="Number of attendees" :options="selectOptions" v-model="selectValue"></q-select>
    <p>Output: {{ selectValue }}</p>
    <q-button type="submit" label="Submit" />
  </form>
</template>

<script setup>
import { ref } from 'vue';

const selectValue = ref(null);
const selectOptions = [
  'One Visitor',
  'Two Visitors',
  'Three Visitors',
];

const onSubmit = () => alert(`${selectValue.value} will be attending`);
</script>

Bind objects

Beside binding an array of strings, you can also bind an array of objects. These have to include the following props:

  • value (required) => what gets bound to v-model
  • key (optional) => a :key prop to identify the unique entry
  • text (optional) => text to be shown instead of the value
  • disabled (optional) => whether option element is disabled or not

QSelect will then try the following:

  1. Create one option element for each value property and bind it.
  2. If value does not exist, fall back to binding the whole object
  3. Try and bind the key property to the option element
  4. If key does not exist, fall back to binding the value
  5. Try and show the text property for each option
  6. If text does not exist, fall back to binding the value

Output: [ ] visitors are attending

Example

<template>
  <form @submit.prevent="onSubmit">
    <q-select labelPrefix="Please " label="Number of attendees" :options="selectOptionsText" v-model="selectValue"></q-select>
    <p>Output: [ {{ selectValue }} ] visitors are attending</p>
    <q-button type="submit" label="Submit" />
  </form>
</template>

<script setup>
import { ref } from 'vue';

const selectValue = ref(null)
const selectOptionsText = [
  { key: 1, text: "One Visitor", value: 1 },
  { key: 2, text: "Two Visitors", value: 2, disabled: true },
  { key: 3, text: "Three visitors", value: 3 }
]

const onSubmit = () => alert(`${selectValue.value} will be attending`);
</script>

Required fields

See QInput@required-fields for behavior and customization

Output:

Example

<template>
  <form @submit.prevent="onSubmit">
    <q-select required requiredSign="(please select one)" label="Number of attendees" :options="selectOptions" v-model="selectValue"></q-select>
    <p>Output: {{ selectValue }}</p>
    <q-button type="submit" label="Submit" />
  </form>
</template>

<script setup>
import { ref } from 'vue';

const selectValue = ref(null)
const selectOptions = [
  'One Visitor',
  'Two Visitors',
  'Three Visitors',
];


const onSubmit = () => alert(`${selectValue.value} will be attending`);
</script>

Multiple values behavior

When declaring the multiple HTML5 attribute, QSelect will not consider all selected entries. While this behavior could be adjusted, I'd recommend to just use QCheckbox whenever the selection of multiple elements is necessary

Output: [ ] visitors are attending

Full component's code

QSelect

<template>
  <label v-if="label" :for="id" class="q-input-label">
    {{ label }}
    <span class="q-input-required-sign" v-if="required">
      {{
        requiredSign
      }}
    </span>
  </label>
  <select
    class="q-input-base"
    :class="{
      'q-error': !!error,
    }"
    v-bind="{
      ...$attrs,
      onChange: ($event) => $emit('update:modelValue', $event.target.value),
    }"
    :id="id"
    :required="required"
    :placeholder="labelPrefix ? labelPrefix + label.toLowerCase() : label"
    :aria-label="label"
    :aria-required="required"
    :aria-describedby="error ? `${id}-error` : null"
    :aria-invalid="error ? true : null"
  >
    <option
      selected
      disabled
      aria-disabled
    >{{ labelPrefix ? labelPrefix + label.toLowerCase() : label }}</option>
    <option
      v-for="option in options"
      :value="option.value ? option.value : option"
      :key="option.key ? option.key : option"
      :selected="option === modelValue"
      :aria-selected="option.text ? option.text : option.value ? option.value : option"
      :disabled="option.disabled"
      :aria-disabled="option.disabled"
    >{{ option.text ? option.text : option.value ? option.value : option }}</option>
  </select>
  <small
    v-if="error"
    class="q-input-error-msg"
    :id="`${id}-error`"
    aria-live="assertive"
  >{{ error }}</small>
</template>

<script setup>
import uuid from "../../use/uuid";
const id = uuid();

defineProps({
  labelPrefix: {
    type: String,
    default: null,
  },
  label: {
    type: String,
    default: "",
  },
  options: {
    type: Array,
    required: true,
  },
  modelValue: {
    type: [String, Number],
    default: "",
  },
  error: {
    type: String,
    default: "",
  },
  required: {
    type: Boolean,
    default: false,
  },
  requiredSign: {
    type: String,
    default: "*",
  },
});

</script>

<style scoped>
option {
  background-color: var(--background-color-primary);
  color: var(--text-color-primary);
}

.q-input-base,
.q-input-label {
  display: block;
  width: 100%;
  font-size: var(--text-size-sm);
}

.q-input-label {
  color: var(--accent-color-primary);
  font-size: var(--text-size-sm);
  font-weight: 600;
}

.q-input-required-sign {
  color: var(--color-error);
}

.q-input-base {
  background-color: transparent;
  color: var(--text-color-primary);
  caret-color: var(--text-color-primary);
  border: none;
  border-bottom: var(--gap-xxs) solid transparent;
  padding: var(--gap-sm) var(--gap-xs);
  margin: var(--gap-sm) 0;
}

.q-input-base:focus {
  outline: none;
  transition: var(--duration-quickest);
  border-bottom: var(--gap-xxs) solid var(--accent-color-primary);
}

.q-input-base:focus.q-error {
  border-bottom: var(--gap-xxs) solid var(--color-error);
}

.q-input-error-msg {
  font-size: var(--text-xs);
  color: var(--color-error);
}
</style>