How to call function on child component on parent events

Javascriptvue.jsEvent HandlingVuejs2

Javascript Problem Overview


Context

In Vue 2.0 the documentation and others clearly indicate that communication from parent to child happens via props.

Question

How does a parent tell its child an event has happened via props?

Should I just watch a prop called event? That doesn't feel right, nor do alternatives ($emit/$on is for child to parent, and a hub model is for distant elements).

Example

I have a parent container and it needs to tell its child container that it's okay to engage certain actions on an API. I need to be able to trigger functions.

Javascript Solutions


Solution 1 - Javascript

Give the child component a ref and use $refs to call a method on the child component directly.

html:

<div id="app">
  <child-component ref="childComponent"></child-component>
  <button @click="click">Click</button>  
</div>

javascript:

var ChildComponent = {
  template: '<div>{{value}}</div>',
  data: function () {
    return {
      value: 0
    };
  },
  methods: {
    setValue: function(value) {
        this.value = value;
    }
  }
}

new Vue({
  el: '#app',
  components: {
    'child-component': ChildComponent
  },
  methods: {
    click: function() {
        this.$refs.childComponent.setValue(2.0);
    }
  }
})

For more info, see Vue documentation on refs.

Solution 2 - Javascript

What you are describing is a change of state in the parent. You pass that to the child via a prop. As you suggested, you would watch that prop. When the child takes action, it notifies the parent via an emit, and the parent might then change the state again.

var Child = {
  template: '<div>{{counter}}</div>',
  props: ['canI'],
  data: function () {
    return {
      counter: 0
    };
  },
  watch: {
    canI: function () {
      if (this.canI) {
        ++this.counter;
        this.$emit('increment');
      }
    }
  }
}
new Vue({
  el: '#app',
  components: {
    'my-component': Child
  },
  data: {
    childState: false
  },
  methods: {
    permitChild: function () {
      this.childState = true;
    },
    lockChild: function () {
      this.childState = false;
    }
  }
})

<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js"></script>
<div id="app">
<my-component :can-I="childState" v-on:increment="lockChild"></my-component>
<button @click="permitChild">Go</button>
</div>

If you truly want to pass events to a child, you can do that by creating a bus (which is just a Vue instance) and passing it to the child as a prop.

Solution 3 - Javascript

You can use $emit and $on. Using @RoyJ code:

html:

<div id="app">
  <my-component></my-component>
  <button @click="click">Click</button>  
</div>

javascript:

var Child = {
  template: '<div>{{value}}</div>',
  data: function () {
    return {
      value: 0
    };
  },
  methods: {
  	setValue: function(value) {
    	this.value = value;
    }
  },
  created: function() {
    this.$parent.$on('update', this.setValue);
  }
}

new Vue({
  el: '#app',
  components: {
    'my-component': Child
  },
  methods: {
    click: function() {
    	this.$emit('update', 7);
    }
  }
})

Running example: https://jsfiddle.net/rjurado/m2spy60r/1/

Solution 4 - Javascript

A simple decoupled way to call methods on child components is by emitting a handler from the child and then invoking it from parent.

var Child = {
  template: '<div>{{value}}</div>',
  data: function () {
    return {
      value: 0
    };
  },
  methods: {
  	setValue(value) {
    	this.value = value;
    }
  },
  created() {
    this.$emit('handler', this.setValue);
  }
}

new Vue({
  el: '#app',
  components: {
    'my-component': Child
  },
  methods: {
  	setValueHandler(fn) {
    	this.setter = fn
    },
    click() {
    	this.setter(70)
    }
  }
})

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>

<div id="app">
  <my-component @handler="setValueHandler"></my-component>
  <button @click="click">Click</button>  
</div>

The parent keeps track of the child handler functions and calls whenever necessary.

Solution 5 - Javascript

Did not like the event-bus approach using $on bindings in the child during create. Why? Subsequent create calls (I'm using vue-router) bind the message handler more than once--leading to multiple responses per message.

The orthodox solution of passing props down from parent to child and putting a property watcher in the child worked a little better. Only problem being that the child can only act on a value transition. Passing the same message multiple times needs some kind of bookkeeping to force a transition so the child can pick up the change.

I've found that if I wrap the message in an array, it will always trigger the child watcher--even if the value remains the same.

Parent:

{
   data: function() {
      msgChild: null,
   },
   methods: {
      mMessageDoIt: function() {
         this.msgChild = ['doIt'];
      }
   }   
   ...
}

Child:

{
   props: ['msgChild'],
   watch: {
      'msgChild': function(arMsg) {
         console.log(arMsg[0]);
      }
   }
}

HTML:

<parent>
   <child v-bind="{ 'msgChild': msgChild }"></child>
</parent>

Solution 6 - Javascript

If you have time, use Vuex store for watching variables (aka state) or trigger (aka dispatch) an action directly.

Solution 7 - Javascript

The below example is self explainatory. where refs and events can be used to call function from and to parent and child.

// PARENT
<template>
  <parent>
    <child
      @onChange="childCallBack"
      ref="childRef"
      :data="moduleData"
    />
    <button @click="callChild">Call Method in child</button>
  </parent>
</template>

<script>
export default {
  methods: {
    callChild() {
      this.$refs.childRef.childMethod('Hi from parent');
    },
    childCallBack(message) {
      console.log('message from child', message);
    }
  }
};
</script>

// CHILD
<template>
  <child>
    <button @click="callParent">Call Parent</button>
  </child>
</template>

<script>
export default {
  methods: {
    callParent() {
      this.$emit('onChange', 'hi from child');
    },
    childMethod(message) {
      console.log('message from parent', message);
    }
  }
}
</script>

Solution 8 - Javascript

Calling child component in parent

<component :is="my_component" ref="my_comp"></component>
<v-btn @click="$refs.my_comp.alertme"></v-btn>

in Child component

mycomp.vue

    methods:{     
    alertme(){
            alert("alert")
            }
    }

Solution 9 - Javascript

I think we should to have a consideration about the necessity of parent to use the child’s methods.In fact,parents needn’t to concern the method of child,but can treat the child component as a FSA(finite state machine).Parents component to control the state of child component.So the solution to watch the status change or just use the compute function is enough

Solution 10 - Javascript

you can use key to reload child component using key

<component :is="child1" :filter="filter" :key="componentKey"></component>

If you want to reload component with new filter, if button click filter the child component

reloadData() {            
   this.filter = ['filter1','filter2']
   this.componentKey += 1;  
},

and use the filter to trigger the function

Solution 11 - Javascript

You can simulate sending event to child by toggling a boolean prop in parent.

Parent code :

...
<child :event="event">
...
export default {
  data() {
    event: false
  },
  methods: {
    simulateEmitEventToChild() {
      this.event = !this.event;
    },
    handleExample() {
      this.simulateEmitEventToChild();
    }
  } 
}

Child code :

export default {
  props: {
    event: {
      type: Boolean
    }
  },
  watch: {
    event: function(value) {
      console.log("parent event");
    }
  }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjbodilyView Question on Stackoverflow
Solution 1 - JavascriptjoerickView Answer on Stackoverflow
Solution 2 - JavascriptRoy JView Answer on Stackoverflow
Solution 3 - JavascriptdrinorView Answer on Stackoverflow
Solution 4 - JavascriptnilobarpView Answer on Stackoverflow
Solution 5 - JavascriptJason StewartView Answer on Stackoverflow
Solution 6 - Javascriptbrightknight08View Answer on Stackoverflow
Solution 7 - JavascriptMukundhanView Answer on Stackoverflow
Solution 8 - JavascriptßãlãjîView Answer on Stackoverflow
Solution 9 - Javascriptuser10097040View Answer on Stackoverflow
Solution 10 - JavascriptArdian ZackView Answer on Stackoverflow
Solution 11 - JavascriptabdelgribView Answer on Stackoverflow