99

I have a component and want to add a click listener that runs a method in the parent template in Vue. Is this possible?

<template>
    <custom-element @click="someMethod"></custom-element>
</template>

<script>
    export default {
        name: 'template',
        methods: {
            someMethod: function() {
                console.log(true);
        }
    }
</script>
user2036108
  • 1,227
  • 1
  • 10
  • 12
  • 1
    In the code you have posted, `someMethod` will be run when `custom-element` is clicked. Is that what you want or is there some other scenario? – Bert Sep 14 '17 at 00:20
  • 1
    Possible duplicate of [Vue.js inheritance call parent method](https://stackoverflow.com/questions/36764151/vue-js-inheritance-call-parent-method) – thanksd Sep 14 '17 at 01:13

7 Answers7

177

Yes!

It's possible to call a parent method from a child and it's very easy.

Each Vue component define the property $parent. From this property you can then call any method that exist in the parent.

Here is a JSFiddle that does it : https://jsfiddle.net/50qt9ce3/1/

<script src="https://unpkg.com/vue"></script>

<template id="child-template">
    <span @click="someMethod">Click me!</span>
</template>

<div id="app">
  <child></child>
</div>

<script>
Vue.component('child', {
  template: '#child-template',
  methods: {
    someMethod(){
        this.$parent.someMethod();
    }
    }
});

var app = new Vue({
    el: '#app',
  methods: {
    someMethod(){
        alert('parent');
    }
    }
});
</script>

Note: While it's not recommended to do this kind of thing when you are building disconnected reusable components, sometimes we are building related non-reusable component and in this case it's very handy.

Gudradain
  • 4,653
  • 2
  • 31
  • 40
  • 10
    this is no longer working for me, getting this.$parent.myMethod is not a function – raklos Jan 23 '20 at 21:21
  • 2
    @raklos It still work with the latest version of vue.js which is 2.6.11 at the moment. If it's not working for you, the problem is elsewhere. I would suggest to ask another question with the code you are using for help. – Gudradain Jan 23 '20 at 21:27
  • @raklos chances are you are calling it in the "response section" of a function. In that case, the following question likely offers what you are looking for: https://stackoverflow.com/questions/51382072/vue2-call-parent-method-from-child-in-looped-component – Daniel Methner Jun 02 '20 at 05:39
  • 10
    For those who get the "this.$parent.myMethod is not a function" error try `this.$parent.$parent.myMethod` or `this.$parent.$parent.$parent.myMethod` and so on. It may happen that your method is not in the direct parent but somewhere higher in the components hierarchy. – Ilyich Mar 01 '21 at 16:50
  • @IIyich this solved the issue for me, thank you! – Webalation Mar 10 '21 at 17:46
  • 2
    This solution is highly unreliable because it relies on the components hierarchy. It'll not work if you call the method from different components inside a template: `` (sorry I can't manage to format the code properly here) – migli May 16 '21 at 06:44
  • Using `$parent` is only for small static web sites, it isn't right solution for dynamic complex web sites as it is anti-pattern and will cause headache in the future. More chances to break the web site if e.g. a method in component moved to another component, you will have to update `$parent` everywhere and test the site again. Solution for this would be passing a method as a prop or using scoped slots. https://michaelnthiessen.com/pass-function-as-prop/ – abzalassembekov Aug 21 '21 at 11:20
  • 1
    Provide/inject and props/emit are great tools for userland code, but when developing a library, you might not want to pollute your API by providing data to arbitrary descendants nor put the burdon on consumers to perform boilerplate event binding. A component communicating directly with its parent is only an antipattern when deployed in place of a more appropriate declarative idiom – sometimes code that smells bad will taste just fine. That said, $parent.$parent is a sinful temptation one must contemplate gravely before indulging. – Isabelle Wedin Aug 22 '21 at 19:05
58

Directly from the Vue.js documentation:

In Vue, the parent-child component relationship can be summarized as props down, events up. The parent passes data down to the child via props, and the child sends messages to the parent via events...

So you need to emit a click event from your child component when something happens, which can then be used to call a method in your parent template.

If you don't want to explicitly emit an event from the child (using this.$emit('click') from your child component), you can also try to use a native click event, @click.native="someMethod".

tony19
  • 125,647
  • 18
  • 229
  • 307
Lance Whatley
  • 2,395
  • 1
  • 13
  • 16
38

Relying on calling this.$parent hides the dependency and will break when you use component libraries which create a longer child hierarchy

The prefered methods are to either:

  1. Explicitly pass methods as properties to child components (the same as passing data props)
  2. Or declare global methods as mixins

From nils's answer on Vue.js inheritance call parent method:

  1. Passing props (parent-child)

    var SomeComponentA = Vue.extend({
        methods: {
            someFunction: function () {
                // ClassA some stuff
            }
        }
    });
    
    var SomeComponentB = Vue.extend({
       props: [ 'someFunctionParent' ],
       methods: {
           someFunction: function () {
               // Do your stuff
               this.someFunctionParent();
           }
       }
    });
    

    and in the template of SomeComponentA:

    <some-component-b :someFunctionParent="someFunction"></some-component-b>
    
  2. Use Mixins

    If this is common functionality that you want to use in other places, using a mixin might be more idiomatic:

    var mixin = {
        methods: {
            someFunction: function() {
                // ...
            }
        }
    };
    var SomeComponentA = Vue.extend({
        mixins: [ mixin ],
        methods: {
        }
    });
    
    var SomeComponentB = Vue.extend({
       methods: {
           someFunctionExtended: function () {
               // Do your stuff
               this.someFunction();
           }
       }
    });
    

Further Reading

tony19
  • 125,647
  • 18
  • 229
  • 307
KyleMit
  • 30,350
  • 66
  • 462
  • 664
  • Number 1 is a great approach IMO. Best I've seen. I would have used Vuex for this but it would have been a lot of work to enable a modal. – errata Jan 06 '21 at 02:40
8

You can either pass the parent method down to the child component via props or you can get the child component to emit either a custom or native event.

Here's a Plunker to demonstrate both approaches.

miqh
  • 3,624
  • 2
  • 28
  • 38
6

In current vue version, this solution:
Passing props (parent-child)

var SomeComponentA = Vue.extend({
    methods: {
        someFunction: function () {
            // ClassA some stuff
        }
    }
});

var SomeComponentB = Vue.extend({
   props: [ 'someFunctionParent' ],
   methods: {
       someFunction: function () {
           // Do your stuff
           this.someFunctionParent();
       }
   }
});

The HTML part:

<some-component-b someFunctionParent="someFunction"></some-component-b>

Base on this post, should be modified in this way:

<some-component-b v-bind:someFunctionParent="someFunction"></some-component-b>
cswu
  • 341
  • 3
  • 4
6

You can use $root like this with vanilla Vue, but, If you use nuxt with vue that response won't work. Why? because $root is nuxt itself. Let me show you an example:

this.$root.$children[1].myRootMethod()
  • $root: As I said before, this is nuxt.

  • $children[0]: this is nuxtloading.

  • $children[1]: this is your main component, in my case, it was a basic layout with a few global components and a global mixin.

  • $children[n]: other components on your app.

Hope it helps.

mrroot5
  • 1,811
  • 3
  • 28
  • 33
  • This works but kind of anti-pattern. It decreases readability. For similar case(anti-pattern) and looking better one is just to accessing the $parent to call the method. `this.$parent.parentMethod()` – Chase Choi Jan 20 '20 at 08:46
  • @ChaseChoi No, this doesn't work because my code is like an absolute path, it not depends on the children component because the result is always the same. – mrroot5 Jan 20 '20 at 08:56
  • this.$parent.$options.methods.methodName(); Is the only thing that worked for me... – 3xCh1_23 Dec 23 '20 at 21:09
  • Yes, this could work if you only have a parent component not a great grandfather or an unknown ammount of parent components. – mrroot5 Dec 28 '20 at 13:56
3

Solution for Vue 3:

Another way to provide a function to a child component is to use provide/inject. The advantage is that you can avoid passing on props through multiple components, as inject can be used at any depth of child components.

Parent component:

<script setup>

import { provide } from 'vue'

myFunction(){
console.log('message from parent');
}

provide('message', myFunction);

Some child component:

<script setup>
import { inject } from 'vue'

const msg = inject('message')

//calling the injected function
msg()

</script>

Link to the docs.

Stefan
  • 123
  • 1
  • 10
  • How would this be implemented to use provide() and pass "someMethod" in the following example: export default { methods:{someMethod}} – Derek Jul 22 '23 at 15:23
  • On This link( https://vuejs.org/guide/components/provide-inject.html ) you can click on Options in the upper left corner to see examples for the Options API. – Stefan Jul 27 '23 at 11:05