Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Listen to events from parent component in child and execute child’s method in vue without hub

There seems to be a lot of discussion around this topic such as Stackoverflow answer using hub, Stackoverflow answer using refs, so I really like to ask experts to provide for once a clear concise answer to this question. If the answer is also just not possible please state that!

Here is the scenario: There are two components, a parent and a child

<Parent> // There is a button here that can be clicked to emit an event using 'this.$emit()'
   <Child></Child> // The child listens and once hears the event, it does something
</Parent>

What to be achieved?

Clicking the button in the Parent emits a certain event, the child will be constantly listening and once it hears the event it executes an action, such as calling a method of its own.

What is out there about this so far?

  1. Using a hub, in Vue Hub it is clearly stated this is for Non Parent-Child Communication, so what is the point in using it for a parent-child communication?

  2. Using Refs, which is given as an end solution when it is not possible to use props and events. So why it is not possible with events at first place?

My own thought

It seems to me the firing of an event and listening to it is only possible from child to parent, basically one way communication. The parent is able to emit an event but child component(s) are not able to capture the event. Why? I tried this and didn’t work:

In the parent component I have (triggered by clicking a button in the parent component):

methods: {
  generateCharts: function () {
    this.$emit('generate-charts')
    console.log('charts generated')
}

In the child component I have:

mounted () {
 this.parent.$on('generate-charts', function () {
   console.log('event captured') // Here nothing gets logged to the console
 })
}

Update

Just came across this answer Vue $emit. Apparently this is not possible at all with Vue.

At first instance it seems it is a deficiency because I have been in several situations where I needed to fire an event from parent and listen to it in the child.

I can imagine there must be a reason why this is not possible with Vue, it is probably a design consideration, Vue experts explaining why this is the case, and what is the better design approach to solve in general a scenario to pass events from parent to child, would be very appreciated.

like image 843
Payam Mesgari Avatar asked Sep 01 '17 12:09

Payam Mesgari


1 Answers

The answer is to use props and react to changes to those props. It is a little confusing to get used to at first because it seems like a lot of code to to do something simple but as your application gets more complex, the one way data flow enforced by the use of props really helps with debugging and reasoning about what the code is trying to accomplish.

For instance, a modal. Your parent sets showChildModal = true with a button click, this value is passed to the child as a prop. The child is watching for changes to the prop, when it sees it set to true it opens the modal. Finally, when the modal is closed, the child $emit('close') which the parent is watching for, and when it sees that it sets showChildModal = false

Vue.component('child', {
  template: '#child',
  props: ['showModal'],
  name: 'child',
  watch: {
    showModal: function(show) {
      if (show) {
        window.setTimeout(() => {
          if (window.confirm("Hi, I'm the child modal")) {
            this.$emit('close');
          } else {
            this.$emit('close');
          }
        }, 100)
      }
    }
  }
})

var vm = new Vue({
  el: '#el',
  data() {
    return {
      showChildModal: false
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="el">
  Parent
  <p>
    <button @click="showChildModal = true">Click to show child modal</button>
  </p>
  <hr>
  <p>
    <child :show-modal="showChildModal" v-on:close="showChildModal = false"> </child>
  </p>
</div>

<script type="x-template" id="child">
  <div>
    Child
    <p>
      modal open? {{ showModal }}
    </p>
  </div>
</script>
like image 77
Justin Avatar answered Oct 17 '22 09:10

Justin