There are some cases where your component can be defined by the kind of variable you are receiving or the type of data that you have; then, you need to change the component on the fly, without the need to set a lot of Vue v-if, v-else-if, and v-else directives.
In those cases, the best thing to do is use dynamic components, when a computed property or a function can define the component that will be used to be rendered, and the decision is made in real time.
These decisions can sometimes be simple to make if there are two responses, but they can be more complex if there's a long switch case, where you may have a long list of possible components that need to be used.
Getting ready
The prerequisite for this recipe is Node.js 12+.
- @vue/cli
- @vue/cli-service-global
To complete this recipe, we will use our Vue project and the Vue CLI, as we did in the Accessing your children components data recipe.
How to do it...
Follow these steps to create a dynamically injected component:
- Open the StarRating.vue component.
- In the <script> part of the component, we need to create a computed property with a new computed value called starComponent. This value will check whether the user has voted. If they haven't, it will return the StarRatingInput component; otherwise, it will return the StarRatingDisplay component:
<script>
import StarRatingInput from './StarRatingInput.vue';
import StarRatingDisplay from './StarRatingDisplay.vue';
export default {
name: 'StarRating',
components: { StarRatingDisplay, StarRatingInput },
props: {
maxRating: {
type: Number,
required: false,
default: 5,
},
rating: {
type: Number,
required: false,
default: 0,
},
votes: {
type: Number,
required: false,
default: 0,
},
},
data: () => ({
rank: 0,
voted: false,
}),
computed: {
starComponent() {
if (!this.voted) return StarRatingInput;
return StarRatingDisplay;
},
},
methods: {
vote(rank) {
this.rank = rank;
this.voted = true;
},
},
};
</script>
- In the <template> part of the component, we will remove both of the existing components and replace them with a special component called <component>. This special component has a named attribute that you can point to anywhere that returns a valid Vue component. In our case, we will point to the computed starComponent property. We will take all the bind props that were defined by both of the other components and put them inside this new component, including the text that has been placed inside <slot>:
<template>
<component
:is="starComponent"
:max-rating="maxRating"
:rating="rating || rank"
:votes="votes"
@final-vote="vote"
>
Rate this Place
</component>
</template>
How it works...
Using the Vue special <component> component, we declared what the component should render according to the rules that were set on the computed property.
Being a generic component, you always need to guarantee that everything will be there for each of the components that can be rendered. The best way to do this is by using the v-bind directive with the props and rules that need to be defined, but it's possible to define it directly on the component as well since it will be passed down as a prop.
See also
You can find more information about dynamic components at https://v3.vuejs.org/guide/component-dynamic-async.html#dynamic-async-components.