Reputation: 6931
I'm writing a Vue component, which accepts 2 slots:
<template>
<div class="Component">
<div class="Component-Label">
<slot
name="label"
class="Component-LabelInner"
/>
</div>
<div class="Component-Input">
<slot
name="input"
class="Component-InputInner"
/>
</div>
</div>
</template>
<style scoped>
.Component { ... }
.Component-Label { ... }
.Component-LabelInner { ... }
.Component-Input { ... }
.Component-InputInner { width: 100%; ... }
</style>
For layout purposes, I absolutely need to apply some styles to the <slot>
elements - the ones with Component-LabelInner
and Component-InputInner
classes.
(To be precise I need to apply a rule width: 100%
to the Component-InputInner
, as usually I'll pass there <input>
element and I want everything I pass there to stretch to the container.)
The problem is that after <slot>
elements get replaced by the content provided to the slots, class
attribute isn't inherited (it seems no attributes are inherited on slots) and CSS selectors for .Component-LabelInner
and .Component-InputInner
don't work.
Can you somehow add CSS classes to the element that <slot>
gets replaced with?
Upvotes: 2
Views: 2987
Reputation: 6019
You can not bind class to slot
tag. There are some solutions to handle this case:
mounted
hook (it works but looks as bad practice):Vue.component("slots-comp", {
template: "#slotsCompTemplate",
mounted() {
// each slot is an array, because you can pass a set of nodes wrap them with template tag
// I use only first VNode for example
this.$slots.one && this.$slots.one[0].elm.classList.add("one");
this.$slots.two && this.$slots.two[0].elm.classList.add("two");
}
});
new Vue({
el: "#app",
data: {}
})
.one {
color: red
}
.two {
color: blue
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<slots-comp>
<div slot="one">One</div>
<div slot="two">Two</div>
</slots-comp>
</div>
<script type="text/x-template" id="slotsCompTemplate">
<div>
<slot name="one"></slot>
<slot name="two"></slot>
</div>
</script>
Vue.component("slots-comp", {
template: "#slotsCompTemplate",
data() {
return {
classes: {
one: ["one"],
two: ["two"]
}
}
}
});
new Vue({
el: "#app",
data: {}
})
.one {
color: red
}
.two {
color: blue
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<slots-comp>
<div slot-scope="props" :class="props.classes.one" slot="one">One</div>
<div slot-scope="props" :class="props.classes.two" slot="two">Two</div>
</slots-comp>
</div>
<script type="text/x-template" id="slotsCompTemplate">
<div>
<slot :classes="classes" name="one"></slot>
<slot :classes="classes" name="two"></slot>
</div>
</script>
Add changes to CSS to apply styles on all internal elements:
.Component-Input > *
{
/* my rules for child elements */
}
.Component-Label> *
{
/* my rules for child elements */
}
Or add wrapper element for slots with classes Component-InputInner
etc. and add similar styles for them.
Hope it will help.
Upvotes: 3