When building Vue components I often need to create a component that has similar functionality to an existing base component. I want to re-use the base component and extend it to create a new one. A good pattern for this is the wrapper component.
Think of a BaseButton.vue
. It takes some props and emits some events:
<template>
<button
:class="`btn btn-${theme}`"
:disabled="disabled"
@click="handleClick"
>
<slot />
</button>
</template>
<script>
export default {
name: "BaseButton",
props: {
theme: {
type: String,
default: "info",
validator: val =>
[
"info",
"success",
"error"
].indexOf(val) !== -1
},
disabled: {
type: Boolean,
default: false
}
}
methods: {
handleClick() {
this.$emit("click");
}
}
};
</script>
<style scoped>
.btn {
outline: none;
border: none;
color: white;
}
.btn-info {
background: blue;
}
.btn-success {
background: green;
}
.btn-error {
background: red;
}
</style>
I now have a need to build a button that shows a loading state with some text and disables the button when passed a prop of loading
. I could cram this new functionality into the BaseButton, but a much cleaner solution is to use a wrapper component pattern. Let's create a LoadingButton.vue
.
<template>
<BaseButton v-bind="{ ...$props, ...$attrs }" v-on="$listeners">
<template v-if="loading">Loading...</template>
<slot v-else />
</BaseButton>
</template>
<script>
import BaseButton from "~/components/BaseButton";
export default {
components: {
BaseButon
},
props: {
...BaseButton.props,
loading: {
type: Boolean,
default: false
}
}
};
</script>
A couple of this are happening here:
- Pass all the components instance details down to the base components via
$props
,$attr
,$listeners
. See more Vue in the docs. - Use
BaseButton.props
to maintain the same props passed down but add additionalloading
prop to create the new functionality.
A great little pattern for component reusability 🔥
Originally posted on https://matthewblewitt.com/posts/vue-wrapper-component
Top comments (0)