Listbox provide
inject InjectionKey
useListbox
bind on
active focused
script setup
Chapter summary
Reusability separates good Vue code from great Vue
code, and the Composition API opens up new frontiers
in reusability. Let's explore that concept.
provide inject
provide inject
ref computed watch watchEffect setup
Chapter summary
Reusable components, e.g. renderless and compound
components, are great user-facing APIs...but the
authoring experience is tough.
provide inject
provide inject
enter
<!-- CustomTagsInput.vue -->
<script setup>
import { ref } from 'vue'
import TagsInput from 'path/to/TagsInput'
const tags = ref([])
</script>
<template>
<TagsInput
v-model="tags"
v-slot="{ removeTag, inputBindings }"
>
<span v-for="tag in tags">
<span>{{ tag }}</span>
<button
type="button"
@click="removeTag(tag)"
>
×
</button>
</span>
<!--
Multiple attributes and event listeners get bound to
the input element. They're all contained inside the
inputBindings object, so that you can v-bind
them more easily
-->
<input
placeholder="Add tag..."
v-bind="inputBindings"
/>
</TagsInput>
</template>
v-slot
v-slot
v-slot
enter
role tabindex aria-selected
aria-activedescendant aria-orientation
v-model Listbox
Listbox
ListboxOption v-for
ListboxOption
Listbox ListboxOption
<template>
<Listbox
:options="options"
v-model="myOption"
v-slot="{
bindings,
focused, focus, focusFirst, focusLast,
selected, select
}"
>
<ul v-bind="bindings">
<ListboxOption
v-for="option in options"
:key="option"
:option="option"
v-slot="{
bindings,
isFocused, isSelected,
focusPrevious, focusNext
}"
>
<li v-bind="bindings">
<span>{{ option }}</span>
<CheckIcon v-show="isSelected()" />
</li>
</ListboxOption>
</ul>
</Listbox>
</template>
<script setup>
import { ref } from 'vue'
import { CheckIcon } from '@heroicons/vue/solid'
import { Listbox, ListboxOption } from './Listbox'
import { options } from 'path/to/options'
const myOption = ref(options[0])
</script>
v-model v-
for v-slot
ListboxOption Listbox
option
select
A nicely built renderless or compound component makes
you feel like you're writing HTML with superpowers.
<!-- MyRenderlessComponent.vue -->
<template>
<!--
If you bind data to this slot, it becomes a
scoped slot.
-->
<slot />
</template>
<script setup>
// Normal Vue setup code goes here
</script>
this.$slots this.$scopedSlots
.ts .js
export const Root = {
setup: (props, { slots }) {
...
return () => slots.default({ ... })
}
}
export const Child = {
setup: (props, { slots }) {
...
return () => slots.default({ ... })
}
}
export const AnotherChild = {
setup: (props, { slots }) {
...
return () => slots.default({ ... })
}
}
provide inject
provide inject
Listbox
ListboxOption
Listbox aria-activedescendant
ListboxOption
isFocused true
mouseenter
aria-activedescendant isFocused true
isFocused ListboxOption false
Listbox
ListboxOption
Listbox mouseenter
ListboxOption
Listbox
Listbox option-1
aria-activedescendant
Listbox
ListboxOption
isFocused true
option-1 isFocused() true
false option-2
ListboxOption emit
Listbox
ListboxOption
<template>
<Listbox
:options="options"
v-model="myOption"
v-slot="{
bindings,
focused, focus, focusFirst, focusLast,
selected, select
}"
>
<ul v-bind="bindings">
<ListboxOption
v-for="option in options"
:key="option"
:option="option"
v-slot="{
bindings,
isFocused, isSelected,
focusPrevious, focusNext
}"
>
<li v-bind="bindings">
<span>{{ option }}</span>
<CheckIcon v-show="isSelected()" />
</li>
</ListboxOption>
</ul>
</Listbox>
</template>
activeDescendant
ListboxOptions
ListboxOption mouseenter Listbox
Listbox
provide inject
ListboxOption
provide Listbox inject
ListboxOption emit Listbox
ListboxOption
Listbox
Listbox focus
Listbox provide focus
ListboxOption inject focus
provide inject
provide inject
provide inject
provide inject
provide inject
provide inject
Listbox
provide and inject ! They're effective and
interesting, but relatively obscure, and can get
dizzyingly complex.
.vue
template script style
.vue
.js
.vue
.js
.js
provide
inject
Listbox
Listbox
Usually, writing reusable components is a great
experience! In my opinion, compound components are
the exception to the rule.