我正在try 创建一个自定义的 Select 组件,它可以像下面这样接受选项:

<MySelect v-model='value'>
  <MyOption value='1'>Option 1</MyOption>
  <MyOption value='2'>Option 2</MyOption>
</MySelect>

这是我当前的自定义select组件的实现:

<template>
    <div class="select">
        <div class="text" @click="visible=!visible">{{modelValue ?? text}} <IconChevronDown/></div>
        <div class="options" v-if="visible">
            <span v-for="option in options" class="p-4 hover:bg-neutral-1 cursor-pointer capitalize" @click="select(option)">{{option}}</span>
        </div>
    </div>
</template>

<script setup>
const visible = ref(false)
const props = defineProps({
    text: String,
    options: Array,
    modelValue: String
})
const emit = defineEmits(["update:modelValue"])

function select(option){
    visible.value = false;
    emit("update:modelValue", option)
}
</script>

这个组件接受选项作为一个props 和一个字符串array.但我需要更多的自定义选项,如添加图标或应用样式到文本.因此,将选项作为HTML传递的能力解决了这个问题.如果你能分享你的 idea ,如何实现这一点,我将不胜感激!

P.S. MySelect组件不应包含本机 Select 和选项标签.创建自定义 Select 组件的全部目的是为了设计自定义.

推荐答案

您可以创建自定义插槽渲染函数并收集插槽子组件的默认插槽:

VUE SFC PLAYGROUND

通常我更喜欢我的另一个答案中发布的作用域插槽解决方案,但没有人会停止将2个解决方案合并为1:使用这里的默认插槽和另一个答案的#option.因此,所选组件可以由两个插槽馈电.

<script setup>
import { ref } from 'vue'
import MySelect from './MySelect.vue';
import MyOption from './MyOption.view';

const selected = ref();
</script>

<template>
  <my-select v-model="selected">
    <my-option value="1"><span style="color:red">Red option</span></my-option>
    <my-option value="2"><span style="color:blue">Blue option</span></my-option>
  </my-select>
</template>

MySelect.vue

<template>
    <div class="select">
        <div class="text" @click="visible=!visible" style="cursor:pointer">
            <component v-if="modelValue" :is="options[modelValue]"/>
            <template v-else>{{ text }}</template>
        </div>
        <div class="options" v-if="visible">
            <render-options></render-options>
        </div>
    </div>
</template>

<script setup>
import {ref, useSlots} from 'vue';
const visible = ref(false)
const props = defineProps({
    text: {type: String, default: 'Toggle dropdown'},
    options: Array,
    modelValue: String
})
const options = ref({});
const emit = defineEmits(["update:modelValue"])

const $slots = useSlots();
const renderOptions = () => {
    options.value = {};
    return $slots.default()
    .map(vnode => {
        // collection options' default slot
        options.value[vnode.props.value] = vnode.children.default;
        Object.assign(vnode.props ??= {}, {onClick: () => select(vnode.props.value)}, vnode.props ?? {});
        return vnode;
    });
}

function select(option){
    visible.value = false;
    emit("update:modelValue", option)
}
</script>

MyOption.view

<script setup>

</script>

<template>
  <div class="option">
    <slot></slot>
  </div>
</template>
<style scoped>
.option{
  padding: 5px 10px;
  border: 1px solid gray;
  border-radius: 4px;
  cursor: pointer;
}
</style>

Javascript相关问答推荐

如何在不分配整个数组的情况下修改包含数组的行为主体?

django无法解析余数:[0] from carray[0]'

微软Edge编辑和重新发送未显示""

Redux查询多个数据库Api reducerPath&

如何将Map字符串,布尔值转换为{key:string;value:bo布尔值;}[]?<>

我正在建立一个基于文本的游戏在react ,我是从JS转换.我怎样才能使变量变呢?

使用POST请求时,Req.Body为空

在Matter.js中添加从点A到另一个约束的约束

TinyMCE 6导致Data:Image对象通过提供的脚本过度上载

按下单键和多值

使用自动识别发出信号(&Q)

有没有办法更改Chart.js 3.x.x中主要刻度的字体?

在没有任何悬停或其他触发的情况下连续交换图像

如何设置时间选取器的起始值,仅当它获得焦点时?

如何检测当前是否没有按下键盘上的键?

扩散运算符未按预期工作,引发语法错误

如果查询为空,则MongoDB将所有文档与$in匹配

使用Java脚本在div中创建新的span标记

REACT-本机错误:错误类型错误:无法读取未定义的容器的属性

如何从嵌套的json中获取最大值