Create an ImageSelect component on top of vue-multiselect
Two tips before you learnt the concept of Adaptive Components and how you can build the base of it by proxifying props and events using v-bind
and v-on
.
Now it's time to show it in action. By any chance, do you know about vue-multiselect? It's an amazing select component built by Damian Dulisz. vue-multiselect can be used in many different ways given how flexible and customisable it is. That's really how a fine third-party component should be in order to be reusable.
Based on this example from its documentation, let's build an ImageSelect component. To do that, the example redefines some scoped slots that vue-multiselect exposes:
<multiselect v-model="value" :options="options">
<template slot="singleLabel" slot-scope="{ option }">
<img class="option__image" :src="option.img" alt="Sth" />
<span class="option__desc">
<span class="option__title">{{ option.title }}</span>
</span>
</template>
<template slot="option" slot-scope="{ option }">
<img class="option__image" :src="option.img" alt="Sth" />
<span class="option__desc">
<span class="option__title">{{ option.title }}</span>
<span class="option__small">{{ option.desc }}</span>
</span>
</template>
</multiselect>
I'm not getting into scoped slots, just assume that code works in case you don't know about them. The thing here is that I want to build an ImageSelect
component on top of that code.
From the last tip, you probably know already that you need to use v-bind="$props"
and v-on="$listeners"
in order to make that proxifying of props and events happen.
You also need to redeclare the props from the original vue-multiselect component, and you can take them from the MultiselectMixin of its source code:
<template>
<multiselect v-bind="$props" v-on="$listeners">
<template slot="singleLabel" slot-scope="{ option }">
<img class="option__image" :src="option.img" alt="No Man’s Sky" />
<span class="option__desc">
<span class="option__title">{{ option.title }}</span>
</span>
</template>
<template slot="option" slot-scope="{ option }">
<img class="option__image" :src="option.img" alt="No Man’s Sky" />
<span class="option__desc">
<span class="option__title">{{ option.title }}</span>
<span class="option__small">{{ option.desc }}</span>
</span>
</template>
</multiselect>
</template>
<script>
import Multiselect from "vue-multiselect";
import MultiselectMixin from "vue-multiselect/src/multiselectMixin";
export default {
components: {
Multiselect
},
props: MultiselectMixin.props
};
</script>
Here's how you can use this ImageSelect
component, passing by the minimal properties to make it work:
<template>
<ImageSelect
v-model="imageValue"
:options="imageOptions"
label="title"
track-by="title"
:show-labels="false"
/>
</template>
<script>
import ImageSelect from "./ImageSelect";
export default {
components: {
ImageSelect
},
data: () => ({
imageValue: null,
imageOptions: [
{ title: "Random img", img: "https://picsum.photos/300/150" },
{ title: "Cool image", img: "https://picsum.photos/300/151" }
]
})
};
</script>
If you run this code you will notice that there is something not working properly. In particular the show-labels
prop. The thing is that it's not a prop, but an attribute! And they're accessible through the $attrs
component instance option.
Basically we need to proxify not only the props, but also the attributes to make it work.
To do that, I'm going to use a computed property to merge both $props
and $attrs
into the same object:
<template>
<multiselect v-bind="allBindings" v-on="$listeners">
<!-- ... -->
</multiselect>
</template>
<script>
import Multiselect from "vue-multiselect";
import MultiselectMixin from "vue-multiselect/src/multiselectMixin";
export default {
components: {
Multiselect
},
props: MultiselectMixin.props,
computed: {
allBindings() {
// Need to proxify both props and attrs, for example for showLabels
return { ...this.$props, ...this.$attrs };
}
}
};
</script>
You can try yourself and run the code on this Codesandbox example I've prepared for you. You'll see it has some additional Adaptive Components, such as SingleSelect
and MultiSelect
.
Pss: they have some CSS tricks we'll cover on the next tips
Related Articles
The new Provide and Inject in Vue 3
Getting stuck into the prop drilling? Learn how provide/inject can make your components more flexible and independent in this short tutorial.
Jul 18, 2022
Sep 24, 2019
Using Scoped Slots in Vue.js
Quick example on how to use scoped slots for component reusability in vuejs
Sep 15, 2019
Sponsors
VueDose is proudly supported by its sponsors. If you enjoy it, consider supporting it to ensure the project maintainability.