Pick out specific elements from the component <slot>
.
<template>
<div class="header">
<subslot element="h1" /> ⬅ Pick only the `h1` element from the default slot
</div>
</template>
npm i vue-subslot
- 🔥 Cleaner Slot API Give your users a cleaner and more readable API!
- 🧠 Full Slot control Filter out and limit unwanted content from slots!
- 🐥 Tiny
1.04 KB
minzipped!
Have you ever developed a parent-child component set, and wanted to allow users to pass in the child-component without specifiying a slot but still have the same level of control as named-slots? With Subslot, you can!
Demo 1: Inline filter attributes
Imagine being able to offer the following API with parent-child components Card and CardHeader.
<card>
<!-- The Card Header will be positioned separetely from the content -->
<card-header>
My special card
</card-header>
My card content
</card>
Using Subslot, this is all the code you need to make this possible. This is what Card.vue looks like.
<template>
<div class="card">
<div class="card-header">
<!-- Pick out the Card Header from the default slot -->
<subslot element="@CardHeader" limit="1" />
</div>
<div class="card-content">
<!-- Use the remainder -->
<subslot not element="@CardHeader" />
</div>
</div>
</template>
<script>
import Subslot from 'vue-subslot';
import CardHeader './CardHeader.vue';
export default {
name: 'Card',
components: {
Subslot,
CardHeader,
}
};
</script>
Demo 2: Named Subslots
Alternatively to using inline filter attributes, you can define subslots on the component. With this approach, you can access subslots like you would normal slots but via $subslots
. This is what Card.vue would look like.
<template>
<div class="card">
<div
v-if="$subslots.cardHeader"
class="card-header"
>
<subslot name="cardHeader" />
</div>
<div class="card-content">
<!-- Use the remainder -->
<subslot />
</div>
</div>
</template>
<script>
import Subslot from 'vue-subslot';
import CardHeader './CardHeader.vue';
export default {
name: 'Card',
components: {
Subslot,
CardHeader,
},
mixins: [
Subslot.define({
// Use a string filter
cardHeader: '@CardHeader:1', // Limit 1
cardHeader: '@CardHeader[3:2]', // Offset 3, Limit 2
// Or an object filter
cardHeader: {
element: '@CardHeader',
limit: 1,
},
}),
],
};
</script>
As a string, it filters the vnodes by tag (as opposed to component)
<subslot element="div" />
Filter the vnodes with tag child-component
<subslot element="ChildComponent" />
Use the @
prefix to use the component from the components
hash
<subslot element="@ChildComponent" />
Or, pass in the direct Component reference
<subslot :element="ChildComponent" />
Pass in an array
<subslot :element="[ChildComponentA, '@ChildComponentB', 'div']" />
Use the asterisk to match any element (incl. components). This can be used to filter out text/white-space.
<subslot element="*" />
<subslot
element="ChildComponent"
offset="1"
/>
<subslot
element="ChildComponent"
offset="1"
limit="1"
/>
Set the not
boolean to inverse the filter and get everything that doesn't match.
<subslot not element="@ChildComponent" />
Inverse the element match-all to match only text nodes.
<subslot not element="*" />
Like normal slots, what you pass into the slot of subslot
will be the fallback content of that subslot
.
<subslot name="banner">
<default-banner />
</subslot>
@no-match
: Emitted when there are no matching vnodes
<subslot
:vnodes="$slots.namedSlot"
element="@ChildComponent"
/>
Unfortunately not due to how functional components are implemented in Vue.js.
Functional components are stateless and are immediately invoked as a function that outputs vNodes. The outputted vNodes are passed into the slot in place of the functional component. Because Subslot doesn't actually receive the functional component, it's impossible to detect them.
- vue-proxi - 💠 Tiny proxy component
- vue-vnode-syringe - 🧬 Add attributes and event-listeners to
<slot>
content 💉 - vue-pseudo-window - 🖼 Declaratively interface window/document in your Vue template
- vue-v - render vNodes via component template
- vue-frag - 🤲 Directive to return multiple root elements