Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call parent methods from child components (Vue.js)

I'm doing a project and I need to call parent methods from child components. How can this be accomplished in Vue.js?

like image 903
Ruhith Udakara Avatar asked May 25 '20 08:05

Ruhith Udakara


6 Answers

You should use this.$emit('myEvent') inside of your child component, when you want to trigger the method in the parent.

Then find your child component in the template of the parent and add an event catcher on it like this:

<template>
  <your-child-component @myEvent="myMethod"/>
</template>

If you want to add parameters to your method, you can add a second parameter to your emit like this:

this.$emit("myEvent", "My parameter")

For this to work you don't have to change anything in the event "catcher", as long as the method you call has a parameter.

like image 68
Thomas Bay Avatar answered Oct 19 '22 21:10

Thomas Bay


Props and Emit

Maybe working example will make it more clear.

https://m-vue-leaflet.netlify.app/

code- https://github.com/manojkmishra/vue-leaflet-mapping

So here if you see there are 3 vue files in components folder. Brew.vue is parent component to BrewList.vue child component.

Brew.vue- Parent Component

Parent Component

BrewList.vue - Child Component

Child Component

Child component BrewList.vue is using emit to send mouse-over-brew & mouse-leave-brew values to parent Brew.vue. Also, in case you are interested Brew.vue parent is sending brew prop to BrewList.vue child.

Child emit to Parent

As per docs- https://v2.vuejs.org/v2/guide/components.html#Listening-to-Child-Components-Events

like image 26
Manoj Avatar answered Oct 19 '22 21:10

Manoj


Dec, 2021 Update:

It works with $emit. The name of @callTest in parent component must be same as the name of $emit('callTest') in child component.

Parent Component:

<template>
  <Child
    @callTest="test" // Assign 'test' method to @callTest
  />
</template>

<script>
import Child from "../components/Child.vue";
import { defineComponent } from "vue";

export default defineComponent({
  name: "Parent",

  components: {
    Child,
  },

  methods: {
    test() {
      alert("Test");
    },
  }
});
</script>

Child Component:

<template>
  <button @click="$emit('callTest')">Click Me</button>
</template>

<script>
import { defineComponent } from "vue";
    
export default defineComponent({
  name: "Child",
});
</script>

Again, the name of @callTest in parent component must be same as the name of $emit('callTest') in child component.


If you use $emit in script section, this is needed different from template section.

Child Component:

<template>
  <button @click="message">Click Me</button>
</template>

<script>
import { defineComponent } from "vue";
    
export default defineComponent({
  name: "Child",
  methods: {
    message() {
      this.$emit('callTest') // 'this' is needed.
    }
  }
});
</script>

If test method has 2 parameters, you need to call test method with 2 arguments in child component like below.

Parent Component:

<template>
  <Child
    @callTest="test" // Assign 'test' method to @callTest
  />
</template>

<script>
import Child from "../components/Child.vue";
import { defineComponent } from "vue";

export default defineComponent({
  name: "Parent",

  omponents: {
    Child,
  },
        
  methods: {
    test(num1, num2) { // 'test' method has 2 parameters.
      alert(num1 + num2);
    },
  }
});
</script>

Child Component:

<template>       // Call 'test' method with 2 arguments.                          
  <button @click="$emit('callTest', 3, 5)">Click Me</button>
</template>

<script>
import { defineComponent } from "vue";
        
export default defineComponent({
  name: "Child",
});
</script>
like image 34
Kai - Kazuya Ito Avatar answered Oct 19 '22 21:10

Kai - Kazuya Ito


Ideally, this is the right way to do so: https://v2.vuejs.org/v2/guide/components.html#Listening-to-Child-Components-Events

On the other hand, I believe in your scenario (which I'm trying to assume cause it's not really clear), you can use this.$parent.methodName.

Keep in mind that the second suggestion is less clean. It should be used just in case of need.

like image 35
Cristiano Soleti Avatar answered Oct 19 '22 21:10

Cristiano Soleti


So basically, there are 2 ways to answer your question

  1. Using $emit, with syntax is @

  2. Passing function as props, with syntax is : The same as your example

If you based on Vue docs and a lot of other Vue tutorials, you will see that they encourage people to use $emit event rather than passing function as props (the way you are using). The docs you can read here.

https://v2.vuejs.org/v2/guide/components-custom-events.html https://v2.vuejs.org/v2/guide/components.html#Emitting-a-Value-With-an-Event https://code.tutsplus.com/tutorials/design-patterns-for-communication-between-vuejs-component--cms-32354 vue, emitting vs passing function as props

The reason is Vue philosophy is passing props down, emitting events up. Using $emit will help to mark the function triggered as a Vue event, and therefore you can use global event listener. This will also may help you to separate between data flow logic and event flow logic.

However, using function as props is not wrong, and in fact, it can be used to achieve the same result. In my preference, I use the 2nd way when I write a component that has a default function, and the function is only overridden when parents pass another one. This will help me avoid rewriting default functions many times.

For the rest of the other cases, I will use the 1st way $emit.

like image 3
Jake Lam Avatar answered Oct 19 '22 20:10

Jake Lam


Parent

<complited v-on:passData="fromChild" />

  methods: {
      fromChild(data) {
        if (data.methodCall) return this[data.methodCall]();
      }

      aFunction() {
        alert('function: a');
      }

      bFunction() {
        alert('function: b');
      }
  }

Child

<template>
   <div>
    <button @click="parentCall()">Call Parent Function
    </button>
   </div>
  </template>
  methods: {
      parentCall() {
        this.$emit("passData", {methodCall: 'aFunction' });
      }
  }
like image 1
LSChanaka Avatar answered Oct 19 '22 21:10

LSChanaka