向 Vue Headless UI 列表框(选择)组件添加本机验证?

问题描述 投票:0回答:1

我正在尝试向 Vue3 或 Nuxt 3 Vue Headless UI Listbox (Select) 组件添加本机验证支持。根据我的理解,它不直接支持,所以我想知道哪种是最好、最简单的方法。

您能指导我如何对 ListBox 采用本机验证吗?

DropDown.vue:

<template>
  <div class="flex-col items-center">
    <label class="w-1/6 pb-1 text-sm font-semibold">{{ label }}</label>
    <Listbox
      v-model="item"
      @update:modelValue="onItemChange"
      :required="required"
      :default-value="defaultSelectionRef"
    >
      <div class="relative z-10 w-full">
        <ListboxButton
          class="dropdown relative w-full pl-3 text-sm text-left sm:text-sm rounded h-12 border border-gray-300 dark:border-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
          :required="required"
        >
          <span class="block truncate dark:text-white">{{ item.text }}</span>
          <span
            class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
          >
            <Icon icon="fe:arrow-down" />
          </span>
        </ListboxButton>
        <transition
          leave-active-class="transition duration-100 ease-in"
          leave-from-class="opacity-100"
          leave-to-class="opacity-0"
        >
          <ListboxOptions
            class="absolute mt-1 w-full overflow-auto rounded shadow-md bg-white py-1 ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm dark:bg-slate-800 dark:text-white"
            :required="required"
          >
            <ListboxOption
              v-slot="{ active }"
              v-for="option in options"
              :key="option.value"
              :value="option"
              as="template"
              :disabled="option.disabled"
              :required="required"
            >
              <li
                :class="[
                  active ? 'bg-[#E5F0FC] dark:bg-slate-700' : '',
                  'relative select-none py-2 pl-4 pr-4 cursor-pointer',
                ]"
                :required="required"
              >
                <span :class="option.class">{{ option.text }}</span>
              </li>
            </ListboxOption>
          </ListboxOptions>
        </transition>
      </div>
    </Listbox>
  </div>
</template>

<script setup>
import { Icon } from "@iconify/vue";
import {
  Listbox,
  ListboxButton,
  ListboxOptions,
  ListboxOption,
} from "@headlessui/vue";

const props = defineProps({
  label: {
    type: String, // label for the dropdown field
    required: false,
  },
  defaultSelection: {
    type: Object, // default selected item in drop down
    required: false,
  },
  options: {
    type: Array, // array of options in the drop down
    required: true,
  },
  required: {
    type: Boolean, // condition based on which dropdown becomes required or not
    required: false,
  },
  modelValue: {
    type: [String, Number, Object, null], // name of the corresponding model for dropdown field
    required: false,
  },
});

const defaultSelectionRef = computed(() => {
  //Get existing model value for the dropdown
  const modelValue = props.modelValue;

  //If there is no existing value return 1st element of dropdown options array
  if (modelValue === undefined || modelValue === null) {
    return props.options[0];
  }

  //If there is existing value then return matching element from options array
  return (
    props.options.find((obj) => obj.value === modelValue) ||
    props.options.find((obj) => obj.value === modelValue.value)
  );
});

const item = ref(defaultSelectionRef.value);

const emits = defineEmits(["onItemChange", "update:modelValue"]);

watch(defaultSelectionRef, (newValue) => {
  item.value = newValue;
});

// Emit the updated modelValue when the selectedOption changes
watchEffect(() => {
  emits("update:modelValue", item.value.value);
});

const onItemChange = () => {
  emits("onItemChange", props.model, item.value);
};
</script>

pages/test.vue中的用法:

<template>
    <form @submit.prevent="submitForm">
      <div class="overflow-x-auto shadow-md sm:rounded-lg">
        <table class="w-full text-sm dark:text-white">
          <caption />
          <th id="tableHeader" />
          <tbody>
            <tr class="bg-white dark:bg-gray-800 dark:border-gray-700">
              <td
                class="px-4 py-4 whitespace-nowrap text-center bg-gray-500 text-white dark:text-black"
              >
                <DropDown
                  v-model="formData.type"
                  :label="$t('pages.modal.select-type')"
                  :options="options"
                  :required="true"
                />
              </td>
            </tr>
          </tbody>
        </table>
      </div>
  
      <button
        type="submit"
        class="mt-2 mb-2 text-green-700 border-green-700 focus:ring-green-300 hover:bg-green-700 dark:hover:bg-green-500 dark:focus:ring-green-800 dark:border-green-500 dark:text-green-500 block rounded-full hover:text-white border focus:ring-4 focus:outline-none font-medium text-sm px-5 py-2.5 text-center dark:hover:text-white"
      >
        {{ $t("pages.modal.submit") }}
      </button>
    </form>
  </template>
  
  <script setup>
  //Import static values for dropdowns
  import {
    options,
  } from "~/public/Static/DefaultValues";
  const formData = ref({});
  
  const submitForm = async (event) => {
    console.log("Form submitted successfully");
  };
  </script>
  
  <style>
  </style>

如果用户未选择该值,我想阻止提交表单。我正在尝试找出一种直接方法来查看是否可以直接验证并显示本机消息。我知道它可以通过一些额外的方法和编码来完成,但尝试尽可能避免它并找到最佳和简单的方法。

javascript html vue.js nuxt.js headless-ui
1个回答
0
投票

我只尝试了一些功能,省略了一些样式,并得到了这个结果。在我看来,这是直接验证选择框并在未选择任何内容时显示消息的最简单的解决方案。当然,验证框架会更加灵活,但我相信这应该足够了。

定义组件:

<template>
  <div>
    <label>{{ label }}</label>
    <Listbox v-model="inputValue" :defaultValue="options[0]">
      <div ref="target">
        <ListboxButton
          v-slot="{ value }" 
          @click="isOpen = !isOpen"
        >
          <span v-if="inputValue" class="block truncate dark:text-white">{{ value }}</span>
          <span v-else class="block truncate dark:text-white">Choose an option</span>
        </ListboxButton>
        <ListboxOptions>
          <ListboxOption
            as="template"
            v-slot="{ active }"
            v-for="option in options"
            :key="option.id"
            :value="option.name"
            :disabled="option.unavailable"
          >
            <li :class="[active ? 'bg-[#E5F0FC] dark:bg-slate-700' : '']">
              <span>{{ option.name }}</span>
            </li>
          </ListboxOption>
        </ListboxOptions>
      </div>
    </Listbox>
    <div v-if="showError" class="text-red-500">
      <strong>Please select something</strong>
    </div>
  </div>
</template>

<script setup>
import { computed, onMounted, ref } from 'vue'
import {
  Listbox,
  ListboxButton,
  ListboxOptions,
  ListboxOption,
} from '@headlessui/vue';

const props = defineProps({
  label: {
    type: String,
  },
  options: {
    type: Array,
    required: true,
  },
  required: {
    type: Boolean,
  },
  modelValue: {
    type: [String, Number, Object, null],
  },
});

const emits = defineEmits(['update:modelValue'])
const showError = ref(false)
const isOpen = ref(false)
const target = ref(null)

const inputValue = computed({
    get() {
        return props.modelValue
    },
    set(value) {
        emits('update:modelValue', value)
    }
})

// This method can be handled also within a custom directive
function handleClickOutside(event) {
  if (!target.value.contains(event.target) && isOpen.value && props.required) {
    showError.value = !inputValue.value
  } else if (inputValue.value) {
    showError.value = false
  }
}
 
onMounted(() => {
  document.addEventListener('click', handleClickOutside);
})
</script>

使用组件:

<template>
  <form @submit.prevent="submitForm">
    <DropDown
      v-model="optionValue"
      :label="'Label XYZ'"
      :options="options"
      :required="true"
    />

    <button type="submit">
      Submit
    </button>
  </form>
</template>

<script setup>
import { ref } from 'vue'
import DropDown from '../components/DropDown.vue'

const options = [
  { id: 1, name: 'Durward Reynolds', unavailable: false },
  { id: 2, name: 'Kenton Towne', unavailable: false },
  { id: 3, name: 'Therese Wunsch', unavailable: false },
  { id: 4, name: 'Benedict Kessler', unavailable: true },
  { id: 5, name: 'Katelyn Rohan', unavailable: false },
]

const optionValue = ref(null)

const submitForm = async (event) => {
  // Implement some logic before submitting
  console.log("Form submitted successfully")
}
</script>

我希望代码足够不言自明,并且能够为您提供帮助。

© www.soinside.com 2019 - 2024. All rights reserved.