Dev Notes

Software Development Resources by David Egan.

Passing Data Between Vue Components


JavaScript, Vue
David Egan

Components are re-usable Vue instances that better allow you to keep code separated and modular. They are very flexible - child components can be defined and used within parents. This can be a really useful way of preventing repetitive markup.

Single-file components take things to the next level. These keep all component data within a single .vue file - comprised of a HTML like structure with <template> (markup), <script> (JavaScript) and <style> (CSS) components. This can be really useful for larger projects or projects in which the entire frontend is built in JavaScript.

If you use vue-cli to spin up a Vue/Webpack project, the project comes with single-file components pre-installed.

This article outlines how to pass data between components in the context of single file components.

Passing Data From Parent Component to Child Component

Import Child in Parent Component

Import the child component so that it can be used in the parent, and declare the component in the <script> section:

<script>
// @ is an alias to /src (in a vue-cli generated project)
import Child from '@/components/Child.vue'

// You also need to declare that the imported child component will be used:
export default {
  components: {
    Child
  }
}
</script>

Invoke the Child Component in the Parent

Within the <template> section of the parent component, call the child component. Prop data is passed as an attribute.

You can pass data inline, or define it within the data() method of the component.

<template>
  <div>
    <!-- simplest prop - pass a string -->
    <Child title="This is my title"></Child>

    <!-- Pass an object, defined inline -->
    <Child :parentData="{msg: 'xxx'}"></Child>

    <!-- Pass an object, defined in the `data()` method -->
    <Child :parentData="myData"></Child>

    <!-- Pass a string variable, defined in `data()`. Note colon. -->
    <Child :stringProp="stringMessage"></Child>
  </div>
</template>

Note that prepending a colon to the prop attribute indicates that a variable is passed, not a string.

Receiving the Prop Data in the Child

You must declare the prop in the child component <script> section - you can then use it as a normal Vue-defined variable:

<script>
export default {
  name: 'Child',
  // To use props, they must be declared
  props: {
    parentData: Object,
    stringProp: String,
    title: String
  }
}
</script>

Pass Data from Child to Parent

You can send data from a child to a parent component by means of Vue’s built-in $emit() method. The first parameter of $emit is the event that should be listened for in the parent component. The second (optional) parameter is the data value to pass.

Parent:

<!-- Parent.vue -->
<template>
  <div>
    <!--
    Listen for `childToParent`: the first parameter of the `$emit` method
    in the child component. We're also listening and reacting to an
    `increment` event - in this case, we increment a counter inline.
    -->
    <Child :parentData="myData" v-on:childToParent="onChildClick" v-on:increment="counter++"></PassProps>
  </div>
</template>
<script>
import Child from '@/components/Child.vue'
export default {
  data () {
    return {
      counter: 0,
      fromChild: '', // This value is set to the value emitted by the child
    }
  },
  name: 'about',
  components: {
    Child
  },
  methods: {
    // Triggered when `childToParent` event is emitted by the child.
    onChildClick (value) {
      this.fromChild = value
    }
  }
}
</script>

In this example, the parent listens for the childToParent event (defined by the $emit method) and triggers the onChildClick() method when the event is received. This method in turn sets the fromChild variable. It also emits an increment event in a simpler way when the button is clicked, by placing $emit inline in the template.

In the child component:

<!-- Child.vue -->
<template>
  <div class="child">
    <!-- Simplest - call `$emit()` inline-->
    <button type="button" name="button" v-on:click="$emit('increment')">Click me to increment!</button>

    <!-- set a variable then trigger a method which calls `$emit()` -->
    <label for="child-input">Child input: </label>
    <input id="child-input" type="text" name="msg" v-model="childMessage" v-on:keyup="emitToParent">
  </div>
</template>
<script>
export default {
  data() {
    return {
      childMessage: ''
    }
  },
  name: 'Child',
  methods: {
    // Define the method that emits data to the parent as the first parameter to `$emit()`.
    // This is referenced in the <template> call in the parent. The second parameter is the payload.
    emitToParent (event) {
      this.$emit('childToParent', this.childMessage)
    }
  }
}
</script>

This example emits a user-entered string back to the parent component.


comments powered by Disqus