Skip to content

Sharing data between Vue JS components

sharing-data-between-components-header

In this post, we’ll see how we can share the data from parent to child and vice-versa.

Passing data from parent to child component

Let’s say we have a user component and a display component. We’ll pass user details to the display component and the display component will display all the users data in a tabular format.

To pass the data to the child component we have to use the v-bind or we can use the : (colon) shorthand.

<ResultsPanel :user-data="this.users" />

In the above code snippet, we are passing the users array to ResultsPanel component with :user-data binding. In the ResultsPanel.vue file, we’ll define a property in the props object with the same name (userData) as we defined in the binding.

export default {
    data () {
        return {};
    },
    props: {
        userData: {
            type: Array,
            default: () => { return []; }
        }
    }
}

Vue JS is smart enough to map data from kebab-case to camel case. So, the data passed to our :user-data in the parent component will be captured in the userData object in the props object in the child component.

Here’s the working example of User.vue and ResultsPanel.vue

<template>
  <ResultsPanel :user-data="this.users" />
</template>
<script>
import ResultsPanel from "./ResultsPanel.vue";
export default {
  components: {
    ResultsPanel
  },
  data() {
    return {
      users: [
        {
          id: 1,
          name: "Kevin",
          email: "[email protected]"
        },
        {
          id: 2,
          name: "John",
          email: "[email protected]"
        },
        { id: 3, name: "Matt", email: "[email protected]" }
      ]
    };
  }
};
</script>
<!-- ResultPanel.vue -->
<template>
    <table border="1">
        <thead>
            <tr>
                <td>User ID</td>
                <td>Username</td>
            </tr>
        </thead>
        <tr v-for="user in userData" :key="user.id">
            <td>{{ user.id }}</td>
            <td>{{ user.name }}</td>
        </tr>
    </table>
</template>
<script>

export default {
    name: 'ResultsPanel',
    data () {
        return {
        }
    },
    props: {
        userData: {
            type: Array,
            default: () => { return []; }
        }
    }
}
</script>

In the above two code snippets, we passed data from the User component to the ResultsPanel with : (shorthand for v-bind). And in the ResultsPanel we’ve iterated over the users data and displayed it in the form of a table.

Here’s the output on code sandbox:

What happens if the child component does not have a proper props object?

If you did not declare a props object then vue will not show any errors in the console. Try this by removing the userData or try editing it to a different name. You will not see any errors and of course you will not see any data displayed on the screen apart from the table headers.

Passing data from child component to parent (with $emit)

Let’s add admin rights to the user details in the above example. If the user already has admin rights the checkbox will be true otherwise it will not be checked.

If the user tries to check/uncheck we’ll restrict the action by passing information from child to parent with $emit and perform necessary action.

Here’s the code for displaying admin checkbox in the ResultPanel

<table border="1">
    <thead>
      <tr>
        <td>User ID</td>
        <td>Username</td>
        <td>Is Admin</td>
      </tr>
    </thead>
    <tr v-for="user in userData" :key="user.id">
      <td>{{ user.id }}</td>
      <td>{{ user.name }}</td>
      <td>
        <input type="checkbox" :checked="user.isAdmin" @click="makeAdmin(user.id, $event)">
      </td>
    </tr>
  </table>

When user tries to check/uncheck the box, we’ll invoke makeAdmin method which will then emit the event back to the parent component (in our case its the user component).

Let’s see the makeAdmin method.

methods: {
    makeAdmin(userId, event) {
      event.preventDefault();
      this.$emit("editing-admin");
    }
  }

With event.preventDefault() we will prevent the checkbox from checking/unchecking.

With $emit we will invoke the editing-admin event on the parent component.

Now we are done with the child component setup.

To be able to fire our editing-admin event that is raised from the child component we’ll have to create a method in the parent component.

Define an even listener on the ResultPanel element like this in the parent component template.

<ResultsPanel :user-data="this.users" @editing-admin='editAdmin' />

Notice the event name here (@editing-admin). This should match with the event that is emitted from the child (this.$emit("editing-admin")).

Now, we define our method to capture the event emitted from the child component (editAdmin).

methods: {
      editAdmin() {
          alert("You don't have permission to change the admin rights.");
      }
  }

For the purpose of this demo, I’ve used a simple alert to show the warning message here. You could do whatever is appropriate for your purpose.

What happens if there’s no event to capture the raised event with $emit?

Well, nothing. You don’t see any errors in the console if you don’t wire up the event to capture in the parent component.

Try removing the @editing-admin='editAdmin' on the User.vue the component in the demo. And after removing the click event binding try clicking the checkbox in the table. You should not see any alert now.

Leave a Reply

Your email address will not be published.