Dependency Injection in Vue.js will be a breeze with Provide and Inject duo
Often times is overwhelming to look at a larger component as a whole because there may be too much going on, breaking things down allows us to simplify a complex problem and focus our attention in more manageable pieces. One obvious complication of breaking down a large component is data flow and management.
Provide and Inject are used together to allow an ancestor component to serve as a dependency injector for all its descendants, regardless of how deep the component hierarchy is, as long as they are in the same parent chain.
For example, a parent component can inject validation messages in input components. Below is an example where the VueForm
the component is passing down an errors
variable down to TextInput
component.
// A basic example of provide/inject
const VueForm = {
provide:{
errors: {
name: "The name field is required",
},
},
template: `<form>
<slot></slot>
</form>`
}
const TextInput = {
inject: ['errors'],
created(){
console.log(this.errors)
},
template: `
<div>
Name:<br>
<input type="text" name="name"><br><br>
<span v-if="this.errors.hasOwnProperty('name')" class="help-text danger" v-text="this.errors.name"></span>
</div>
`
}
new Vue({
el: "#app",
components: {
'vue-form': VueForm,
'text-input': TextInput,
},
})
You can also provide and inject a function. The below example demonstrates passing getError()
and setError()
functions using provide/inject.
// provide and inject functions
const VueForm = {
provide: function() {
return {
getError: this.getError,
setError: this.setError
}
},
data(){
return {
errors: {
name: "The name field is required."
}
}
},
methods: {
getError: function(name){
return this.errors.hasOwnProperty(name) ? this.errors.name : null;
},
setError: function(name, value){
this.errors.name = value;
}
},
template: `<form>
<slot></slot>
</form>`
}
const TextInput = {
inject: ['getError', 'setError'],
template: `
<div>
Name:<br>
<input type="text" name="name" @change="setError('name', 'This is a custom error message')"><br><br>
<span class="help-text danger" v-text="getError('name')"></span>
</div>
`
}
new Vue({
el: "#app",
components: {
'vue-form': VueForm,
'text-input': TextInput,
},
})
Injecting functions using provide/inject
Note: the
provide
andinject
bindings are NOT reactive. This is intentional. However, if you pass down an observed object, properties on that object do remain reactive.
// Reactive binding in provide/inject
const VueMenu = {
inject: ['menu'],
created(){
console.log(this.menu)
},
data(){
return {
newItem:"",
}
},
methods:{
addMenuItem: function(){
console.log(this.newItem)
this.menu.menuOptions.push(this.newItem);
this.newItem="";
}
},
template: `
<div>
<ul id="example">
<li v-for="item in menu.menuOptions">
{{ item }}
</li>
</ul>
<input type="text" placeholder="Enter name" v-model="newItem"/>
<button type="submit" @click="addMenuItem">Add Menu Item</button>
</div>
`
}
new Vue({
el: "#app",
components: {
'vue-menu': VueMenu,
},
provide () {
const menu = {}
Object.defineProperty(menu, 'menuOptions', {
enumerable: true,
get: () => this.menuOptions,
})
return { menu }
},
data: () => ({ menuOptions: ['Home', 'About','Contact'] }),
})
Injections are available in props
and data
. So, you could set prop
defaults to injected data or you can use injections as initial data
.
More on provide/inject on the official documentation here.