[javascript] How to call function on child component on parent events

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.

This question is related to javascript vue.js event-handling vuejs2

The answer is


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


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/


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


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>

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")
            }
    }

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

_x000D_
_x000D_
var Child = {_x000D_
  template: '<div>{{value}}</div>',_x000D_
  data: function () {_x000D_
    return {_x000D_
      value: 0_x000D_
    };_x000D_
  },_x000D_
  methods: {_x000D_
   setValue(value) {_x000D_
     this.value = value;_x000D_
    }_x000D_
  },_x000D_
  created() {_x000D_
    this.$emit('handler', this.setValue);_x000D_
  }_x000D_
}_x000D_
_x000D_
new Vue({_x000D_
  el: '#app',_x000D_
  components: {_x000D_
    'my-component': Child_x000D_
  },_x000D_
  methods: {_x000D_
   setValueHandler(fn) {_x000D_
     this.setter = fn_x000D_
    },_x000D_
    click() {_x000D_
     this.setter(70)_x000D_
    }_x000D_
  }_x000D_
})
_x000D_
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>_x000D_
_x000D_
<div id="app">_x000D_
  <my-component @handler="setValueHandler"></my-component>_x000D_
  <button @click="click">Click</button>  _x000D_
</div>
_x000D_
_x000D_
_x000D_

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


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.

_x000D_
_x000D_
var Child = {_x000D_
  template: '<div>{{counter}}</div>',_x000D_
  props: ['canI'],_x000D_
  data: function () {_x000D_
    return {_x000D_
      counter: 0_x000D_
    };_x000D_
  },_x000D_
  watch: {_x000D_
    canI: function () {_x000D_
      if (this.canI) {_x000D_
        ++this.counter;_x000D_
        this.$emit('increment');_x000D_
      }_x000D_
    }_x000D_
  }_x000D_
}_x000D_
new Vue({_x000D_
  el: '#app',_x000D_
  components: {_x000D_
    'my-component': Child_x000D_
  },_x000D_
  data: {_x000D_
    childState: false_x000D_
  },_x000D_
  methods: {_x000D_
    permitChild: function () {_x000D_
      this.childState = true;_x000D_
    },_x000D_
    lockChild: function () {_x000D_
      this.childState = false;_x000D_
    }_x000D_
  }_x000D_
})
_x000D_
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js"></script>_x000D_
<div id="app">_x000D_
<my-component :can-I="childState" v-on:increment="lockChild"></my-component>_x000D_
<button @click="permitChild">Go</button>_x000D_
</div>
_x000D_
_x000D_
_x000D_

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.


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>

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


Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to vue.js

How to fix 'Unchecked runtime.lastError: The message port closed before a response was received' chrome issue? Center content vertically on Vuetify Vue.js get selected option on @change Using Environment Variables with Vue.js did you register the component correctly? For recursive components, make sure to provide the "name" option Vue 'export default' vs 'new Vue' How can I go back/route-back on vue-router? Change the default base url for axios How to reference static assets within vue javascript How to change port number in vue-cli project

Examples related to event-handling

How to call function on child component on parent events How to use onClick with divs in React.js Touch move getting stuck Ignored attempt to cancel a touchmove Calling one method from another within same class in Python How to get a right click mouse event? Changing EventArgs to MouseEventArgs causes an error in Form1Designer? How to use the DropDownList's SelectedIndexChanged event Android Overriding onBackPressed() How to pass event as argument to an inline event handler in JavaScript? Get clicked element using jQuery on event? How can I show a hidden div when a select option is selected?

Examples related to vuejs2

How can I go back/route-back on vue-router? Change the default base url for axios How to change port number in vue-cli project How to solve 'Redirect has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header'? vuetify center items into v-flex Vuejs: Event on route change Vuex - Computed property "name" was assigned to but it has no setter Vuex - passing multiple parameters to mutation How to listen to the window scroll event in a VueJS component? How to acces external json file objects in vue.js app