Accessing data directly from a child or a parent component without knowing whether they exist can be very dangerous.
In Vue, it's possible to make your component behavior like an interface and have a common and abstract function that won't change in the development process. The process of dependency injection is a common paradigm in the developing world and has been implemented in Vue as well.
There are some pros and cons to using Vue's internal dependency injection, but it is always a good way to make sure that your children components know what to expect from the parent component when you're developing it.
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 Creating a dynamically injected component recipe.
How to do it...
Follow these steps to create a dependency injection component:
- Open the StarRating.vue component.
- In the <script> part of the component, add a new property called provide. In our case, we will just be adding a key-value to check whether the component is a child of the specific component. Create an object in the property with the starRating key and the true value:
<script>
import StarRatingInput from './StarRatingInput.vue';
import StarRatingDisplay from './StarRatingDisplay.vue';
export default {
name: 'StarRating',
components: { StarRatingDisplay, StarRatingInput },
provide: {
starRating: true,
},
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>
- Open the StarRatingDisplay.vue file.
- In the <script> part of the component, we will add a new property called inject. This property will receive an object with a key named starRating, and the value will be an object that will have a default() function.
This function will log an error if this component is not a child of the StarRating component:
<script>
export default {
name: 'StarRatingDisplay',
props: {
maxRating: {
type: Number,
required: false,
default: 5,
},
rating: {
type: Number,
required: false,
default: 0,
},
votes: {
type: Number,
required: false,
default: 0,
},
},
inject: {
starRating: {
default() {
console.error('StarRatingDisplay need to be a child of
StarRating');
},
},
},
methods: {
getStarName(rate) {
if (rate <= this.rating) {
return 'star';
}
if (Math.fround((rate - this.rating)) < 1) {
return 'star_half';
}
return 'star_border';
},
},
};
</script>
- Open the StarRatingInput.vue file.
- In the <script> part of the component, we will add a new property called inject. This property will receive an object with a key named starRating, and the value will be an object that will have a default() function. This function will log an error if this component is not a child of the StarRating component:
<script>
export default {
name: 'StartRatingInput',
props: {
maxRating: {
type: Number,
required: false,
default: 5,
},
},
inject: {
starRating: {
default() {
console.error('StarRatingInput need to be a child of
StartRating');
},
},
},
data: () => ({
rating: 0,
}),
methods: {
updateRating(value) {
this.rating = value;
},
emitFinalVote(value) {
this.updateRating(value);
this.$emit('final-vote', this.rating);
},
getStarName(rate) {
if (rate <= this.rating) {
return 'star';
}
if (Math.fround((rate - this.rating)) < 1) {
return 'star_half';
}
return 'star_border';
},
},
};
</script>
How it works...
At runtime, Vue will check for the injected property of starRating in the StarRatingDisplay and StarRatingInput components, and if the parent component does not provide this value, it will log an error to the console.
Using component injection is commonly used to provide and maintain a common interface between bounded components, such as a menu and an item. An item may need some function or data that is stored in the menu, or we may need to check whether it's a child of the menu.
The main downside of dependency injection is that there is no more reactivity on the shared element. Because of this, it's mostly used to share functions or check component links.
See also
You can find more information about component dependency injection at https://v3.vuejs.org/guide/component-provide-inject.html#provide-inject.