5

I have a method initialized within the parent component called setMessage() and I'd like to be able to call it within the child component.

main.js

const messageBoard = new Vue({
    el: '#message-board',
    render: h => h(App),
})

App (App.vue (parent))..

export default {
    data() {
        return { messages: state }
    },
    methods: {
        setMessage(message) {
            console.log(message);
        }
    },
    template: `
        <div>
            <child-component></child-component>
        </div>
    `,
}

child

const child = Vue.extend({
    mounted() {
        // attempting to use this function from the parent
        this.$dispatch('setMessage', 'HEY THIS IS MY MESSAGE!');
    }
});
Vue.component('child-component', child);

Right now I'm getting this.$dispatch is not a function error message. What am I doing wrong? How can I make use of parent functions in various child components? I've also tried $emit, it doesn't throw an error & it doesn't hit the function.

Thank you for your help in advance!

Modelesq
  • 5,192
  • 20
  • 61
  • 88

2 Answers2

17

You have a couple options.

Option 1 - referencing $parent from child

The simplest is to use this.$parent from your child component. Something like this:

const Child = Vue.extend({
  mounted() {
    this.$parent.setMessage("child component mounted");
  }
})

Option 2 - emitting an event and handling in parent

But that strongly couples the child to its parent. To fix this, you could $emit an event in the child and have the parent handle it. Like this:

const ChildComponent = Vue.extend({
  mounted() {
    this.$emit("message", "child component mounted (emitted)");
  }
})

// in the parent component template
<child-component @message="setMessage"></child-component>

Option 3 - central event bus

One last option, if your components don't have a direct parent-child relationship, is to use a separate Vue instance as a central event bus as described in the Guide. It would look something like this:

const bus = new Vue({});

const ChildComponent = Vue.extend({
  mounted() {
    bus.$emit("message-bus", "child component mounted (on the bus)");
  }
})

const app = new Vue({
  ...
  methods: {
    setMessage(message) {
      console.log(message);
    }
  }, 
  created() {
    bus.$on('message-bus', this.setMessage)
  },
  destroyed() {
    bus.$off('message-bus', this.setMessage)
  }
});

Update (Option 2a) - passing setMessage as a prop

To follow up on your comment, here's how it might work to pass setMessage to the child component as a prop:

const ChildComponent = Vue.extend({
  props: ["messageHandler"],
  mounted() {
    this.messageHandler('from message handler'); 
  }
})

// parent template (note the naming of the prop)
<child-component :message-handler="setMessage"></child-component>
tony19
  • 125,647
  • 18
  • 229
  • 307
Peter
  • 12,541
  • 3
  • 34
  • 39
  • Hmm. This works. But it worries me that should I have child components becoming parents and reusing the same functions. Would it end up looking something like `this.$parent.$parent.setMessage()`? And therefore becoming hard to keep track of. – Modelesq Feb 24 '17 at 20:08
  • I like the 2nd option better. Is it any different than bringing in `setMessage` as a prop? This also doesn't seem to work; not does it throw any helpful errors. – Modelesq Feb 24 '17 at 20:33
  • I hadn't thought about passing the function as a prop but that would be yet another option. Just added a code sample for this ... note the hyphenated name of the prop and the fact that it is bound. – Peter Feb 24 '17 at 20:49
  • I guess I would still have to include that props within the child child elements as well. I just don't know why `$dispatch` doesn't work. Anyway, thank you for your help! – Modelesq Feb 24 '17 at 20:54
  • Happy to help! Here's the part in the migration guide where they explain why they got rid of `$dispatch` in Vue 2 and what to do instead: https://vuejs.org/v2/guide/migration.html#dispatch-and-broadcast-replaced – Peter Feb 24 '17 at 20:59
  • Awesome! I just needed a way for child components to push data back up to the parent and make use of parent functions where ever needed. I was probably over complicating things. – Modelesq Feb 24 '17 at 21:07
  • Another option is to use a mixin to attach a method to parent and child. https://stackoverflow.com/a/42618631/1804678. In this case, it would have to be a more like a helper method that is independent of the parent child relationship. – Jess Feb 09 '18 at 15:11
0
// parent component providing 'foo'
var Provider = {
  methods: {
    foo() {
      console.log('foo');
    },
  },
  provide: {
    foo: this.foo,
  },
}
    
// child component injecting 'foo'
var Child = {
  inject: ['foo'],
  created() {
    this.foo() // => "foo";
  },
}
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129