When working with single page web applications (SPA), chances are you’re going to be using a router or some kind of parent and child component structure. In these scenarios, the parent components probably hold their own functions and variables that may at some point in time need to be accessed by the child component. Take for example an application that has authentication. The parent component might manage the authentication state while the child component might be a login form, secure content, or something else. When logging in, you might need to change the authentication state of the parent. This is just one of many possible examples where the child and parent need to interact.
We’re going to see how to interact with a parent component from a child component in an application created with the Vue.js JavaScript framework.
For this example, we’re going to be creating a fresh project with the Vue CLI. Assuming you’ve installed the Vue CLI, execute the following:
vue create test-project
When asked, choose to manually select features. The only feature we want for this example is the Router from the list of features. We want the router because we want to know how to access container variables. On the next prompt, it doesn’t matter how you plan to manage configurations.
At this point we should have a functional project to work with.
In the fresh project, there will be many files, but we only care about the src/App.vue and src/views/Home.vue files. No need to create something complicated or fancy out of this simple example.
Open the src/App.vue file and make it look like the following:
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<p>{{ msg }}</p>
<router-view />
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
msg: "Hello World"
}
}
}
</script>
<style></style>
Our goal will be to retrieve and alter the msg
variable in this parent component. Now open the project’s src/views/Home.vue file which is a view that is loaded in the <router-view />
line of the src/App.vue file.
<template>
<div class="home">
<img src="../assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
<button class="button" v-on:click="changeMsg()">Change Message</button>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'home',
components: {
HelloWorld
},
method: {
changeMsg() {
this.$parent.msg = "I changed the message";
}
}
}
</script>
Most of the above code was provided when generating our project, but take notice of the changeMsg
function. When the button is pressed in this child component, the changeMsg
function is called and the msg
variable is directly manipulated from the parent component. We can get or set the msg
variable using this strategy.
Now let’s look at an alternative, which many say is the better solution.
Instead of directly accessing the variable through the $parent
attribute, we’re going to be communicating with the parent component through events. In other words, we’re going to emit an event from our child and listen for it in the parent.
Open the project’s src/App.vue file and include the following:
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<p>{{ msg }}</p>
<router-view @message="setMessage" />
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
msg: ""
}
},
methods: {
setMessage(msg) {
this.msg = msg;
}
}
}
</script>
<style></style>
Notice that this time around, our <router-view>
tag has changed. Now we’re listening for a message
event and if heard, we call the setMessage
callback method. In the callback method, we set the parent msg
variable with the data that was sent.
Open the project’s src/views/Home.vue file and include the following:
<template>
<div class="home">
<img src="../assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
<button class="button" v-on:click="changeMsg()">Change Message</button>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'home',
components: {
HelloWorld
},
method: {
changeMsg() {
this.$emit("message", "I changed the message");
}
}
}
</script>
Instead of using the $parent
property, we’re now using the $emit
property. We are emitting data as the message
event which we’re currently listening for in our parent component.
Pretty awesome right?
You just saw how to interact with a parent component via a child component using two different approaches. We saw how to use $parent
and $emit
, both of which serve in usefulness for different scenarios.
What I demonstrated isn’t strictly useful when using a router. Both approaches can be used without the router as well.