Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access to a child method from the parent in vue.js

People also ask

How do you access child components at Vue?

We can access the root Vue instance with $root , and the parent component with $parent . To access a child component from a parent, we can assign a ref with a name to the child component and then use this. $refs to access it. These properties shouldn't be used too often because they create a mess fast.

How do we call child component in parent?

One way to call a child component's function from its parent is with the help of the useRef hook. Here's the gist of it: We pass a Ref (e.g. childFunc ) to a child 🧒 component and assign the desired function to the Ref's current key. This then gives you access to that very function in the parent component.


You can use ref.

import ChildForm from './components/ChildForm'

new Vue({
  el: '#app',
  data: {
    item: {}
  },
  template: `
  <div>
     <ChildForm :item="item" ref="form" />
     <button type="submit" @click.prevent="submit">Post</button>
  </div>
  `,
  methods: {
    submit() {
      this.$refs.form.submit()
    }
  },
  components: { ChildForm },
})

If you dislike tight coupling, you can use Event Bus as shown by @Yosvel Quintero. Below is another example of using event bus by passing in the bus as props.

import ChildForm from './components/ChildForm'

new Vue({
  el: '#app',
  data: {
    item: {},
    bus: new Vue(),
  },
  template: `
  <div>
     <ChildForm :item="item" :bus="bus" ref="form" />
     <button type="submit" @click.prevent="submit">Post</button>
  </div>
  `,
  methods: {
    submit() {
      this.bus.$emit('submit')
    }
  },
  components: { ChildForm },
})

Code of component.

<template>
 ...
</template>

<script>
export default {
  name: 'NowForm',
  props: ['item', 'bus'],
  methods: {
    submit() {
        ...
    }
  },
  mounted() {
    this.bus.$on('submit', this.submit)
  },  
}
</script>

https://code.luasoftware.com/tutorials/vuejs/parent-call-child-component-method/


Parent-Child communication in VueJS

Given a root Vue instance is accessible by all descendants via this.$root, a parent component can access child components via the this.$children array, and a child component can access it's parent via this.$parent, your first instinct might be to access these components directly.

The VueJS documentation warns against this specifically for two very good reasons:

  • It tightly couples the parent to the child (and vice versa)
  • You can't rely on the parent's state, given that it can be modified by a child component.

The solution is to use Vue's custom event interface

The event interface implemented by Vue allows you to communicate up and down the component tree. Leveraging the custom event interface gives you access to four methods:

  1. $on() - allows you to declare a listener on your Vue instance with which to listen to events
  2. $emit() - allows you to trigger events on the same instance (self)

Example using $on() and $emit():

const events = new Vue({}),
    parentComponent = new Vue({
      el: '#parent',
      ready() {
        events.$on('eventGreet', () => {
          this.parentMsg = `I heard the greeting event from Child component ${++this.counter} times..`;
        });
      },
      data: {
        parentMsg: 'I am listening for an event..',
        counter: 0
      }
    }),
    childComponent = new Vue({
      el: '#child',
      methods: {
      greet: function () {
        events.$emit('eventGreet');
        this.childMsg = `I am firing greeting event ${++this.counter} times..`;
      }
    },
    data: {
      childMsg: 'I am getting ready to fire an event.',
      counter: 0
    }
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.28/vue.min.js"></script>

<div id="parent">
  <h2>Parent Component</h2>
  <p>{{parentMsg}}</p>
</div>

<div id="child">
  <h2>Child Component</h2>
  <p>{{childMsg}}</p>
  <button v-on:click="greet">Greet</button>
</div>

Answer taken from the original post: Communicating between components in VueJS


The suggested solution is for Vue 2, but if you end up here looking for a Vue 3 Composition API solution, you can do the following when migrating :

A child component in a template, which has method "doSomething" :

 <div class="form">                                                                                                                                                        
      <child-component ref="childComponentRef" />                                                                      
</div>  

With Vue 2 :

this.$refs.childComponentRef.doSomething( );
       

With Vue 3 Composition Api :

    setup( )
    {
        const childComponentRef = ref( );

        childComponentRef.value.doSomething( )

        return {
           childComponentRef
        }
     }  

If you end up here looking for Vue 3 script setup

<!-- Parent -->
<template>
    <ChildComponent ref="childComponentRef" />
</template>

<script setup>
import { ref, onMounted } from 'vue'
import ChildComponent from './components/ChildComponent.vue'

const childComponentRef = ref()

onMounted(() => {
    childComponentRef.value.doSomething()
})
</script>
<!-- Child -->
<script setup>
const doSomething = () => {
    console.log('Im batman')
}

// Only available in Vue >= 3.1.3
// No need to import
defineExpose({
    doSomething
})
</script>

If your Vue version is < 3.1.3, you will have to use setup function and return the doSomething function to access it in the parent component.

<!-- Child -->
<script>
import { defineComponent } from 'vue'

export default defineComponent({
    setup() {
        const doSomething = () => {
            console.log('Im batman')
        }

        return { doSomething }
    }
})
</script>

Ref and event bus both has issues when your control render is affected by v-if. So, I decided to go with a simpler method.

The idea is using an array as a queue to send methods that needs to be called to the child component. Once the component got mounted, it will process this queue. It watches the queue to execute new methods.

(Borrowing some code from Desmond Lua's answer)

Parent component code:

import ChildComponent from './components/ChildComponent'

new Vue({
  el: '#app',
  data: {
    item: {},
    childMethodsQueue: [],
  },
  template: `
  <div>
     <ChildComponent :item="item" :methods-queue="childMethodsQueue" />
     <button type="submit" @click.prevent="submit">Post</button>
  </div>
  `,
  methods: {
    submit() {
      this.childMethodsQueue.push({name: ChildComponent.methods.save.name, params: {}})
    }
  },
  components: { ChildComponent },
})

This is code for ChildComponent

<template>
 ...
</template>

<script>
export default {
  name: 'ChildComponent',
  props: {
    methodsQueue: { type: Array },
  },
  watch: {
    methodsQueue: function () {
      this.processMethodsQueue()
    },
  },
  mounted() {
    this.processMethodsQueue()
  },
  methods: {
    save() {
        console.log("Child saved...")
    },
    processMethodsQueue() {
      if (!this.methodsQueue) return
      let len = this.methodsQueue.length
      for (let i = 0; i < len; i++) {
        let method = this.methodsQueue.shift()
        this[method.name](method.params)
      }
    },
  },
}
</script>

And there is a lot of room for improvement like moving processMethodsQueue to a mixin...