I want to check if child component is mounted and I want to move that information to he parent component. For this I am using emits.
So with example here is my parent component:
<child #is-child-mounted="childMounted" />
export default {
data() {
return {
childMounted: false,
};
},
mounted() {
if (this.childMounted) {
//do something
}
},
}
and in child component, I am changing 'is-child-mounted' to true:
mounted() {
this.$emit('isChildMounted', true);
},
But still if (this.childMounted) comes false. So how can I check in parent component if the child component is mounted?
You can add a listener on the child component fom the parent. It would look like this:
Vue3
<Component
#vnodeMounted="handleMounted"
/>
Vue2
<Component
#hook:mounted="handleMounted"
/>
You can replace the hook name by the lifecycle one you want to listen to ! I guess it should be used sparsely as it is not present in the documentation and thus be an internal API that is not destined to be used directly.
source:
https://github.com/vuejs/core/issues/4345#issuecomment-899082892
https://github.com/vuejs/vue/blob/8d3fce029f20a73d5d0b1ff10cbf6fa73c989e62/src/core/instance/lifecycle.js#L348
Looks like there is a typo in the event name in the child component while triggering the event else code should work fine.
It should be is-child-mounted instead of ischildmounted
It should be #is-child-mounted="childMounted = true" instead of #is-child-mounted="childMounted"
Live Demo :
Vue.component('child', {
props: ['childmsg'],
template: '<p>{{ childmsg }}</p>',
mounted() {
this.$emit('is-child-mounted')
}
});
var app = new Vue({
el: '#app',
data: {
childMounted: false
},
mounted() {
if (this.childMounted) {
console.log('child mounted');
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<child childmsg="This is a child component" #is-child-mounted="childMounted = true"></child>
</div>
I am trying to update a child component element through a custom event and know I am getting back to the root component as an alert gives me the current date and time, but I can not update the child component through the prop. This is a follow on question to this question and answer:
(irrelevant code omitted)
In my child component I have as follows:
<template>
<div id="bar">
<div class="row">
<div class="column">
<h3>Timestamp Page Loaded: </h3>
<h3>{{currentTime}}</h3>
</div>
<div class="column">
<button v-on:click="update()">Click to get Current Timestamp</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
barData: Array
},
data() {
return {
callNow: this.barData[0].callNow,
currentTime: this.barData[0].currentDT,
}
},
methods: {
update: function () {
const today = new Date();
const date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate();
const time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
const dateTime = date + ' ' + time;
this.$emit('newDateTime', dateTime)
}
},
}
</script>
And in my root component I have as follows:
<template>
<div>
<bbay-bar v-bind:barData="barData" v-on:newDateTime='upDateTime($event)'></bbay-bar>
</div>
</template>
<script>
import Bar from './Components/SiteWide/Bar.vue'
export default {
components: {
'bbay-bar': Bar,
},
data() {
return {
barData: [{
callNow: '0411111111',
currentDT: '',
}],
}
},
methods: {
upDateTime: function (update){
//this.$alert(update, “thus so far“);
this.currentDT = update;
}
}
}
</script>
You're using props to initialize data in the child component, then using those values in the child component's template. Changes to props afterwards won't affect the child component's data, so the template isn't updated. This scenario is brought up in the Vue docs here.
If you don't require an array for barData, I'd recommend making it an object so your data look like this:
data() {
return {
barData: {
callNow: '0411111111',
currentDT: '',
},
}},
Then in the child component you could pass the prop like so:
props: {
barData: Object
},
When you use the value in the child component, it'd then look like this:
<h3>{{ barData.currentDT }}</h3>
One thing I noticed is that in the upDateTime function you are updating this.currentDT. Previously your data property had a barData array with an object that has a field named currentDT, so this method wasn't updating the correct field. Given that barData is changed to an object, the method should be changed to update this.barData.currentDT
I need to collect data from all child components and get it in Parent component.
For example i have a form component with "Save" button.
Once i click on "Save" button i need all child component send me all data that an user put there.
<Form>
<Name />
<DatePicker />
.....
</Form>
So the main component is Form and it has several child components. Once i click on "Save" in i need to get child components data in Form.
I am thinking about giving "ref" to all child component and call their own methods in Parent once i click on "Save" inside Form. In those methods i will collect all data and fire events with this.$emit there i can send to parent the data i have collected.
Is that a good solution?
Or maybe better to use EventBus?
I prefer bind over emit.
Vue.component("InputField", {
template: `<input v-model="syncedValue" />`,
name: "InputField",
props: {
value: String
},
computed: {
syncedValue: {
get() {
return this.value;
},
set(v) {
this.$emit("input", v);
}
}
}
});
Vue.component("Form", {
template: `<div><InputField v-model="name"/><InputField v-model="surname"/><button #click="save">Save</button></div>`,
name: "Form",
data() {
return {
name: "",
surname: ""
};
},
methods: {
save() {
alert(`${this.name} ${this.surname}`);
}
}
});
new Vue({
template: `<Form></Form>`
}).$mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
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.
Vue 3 Composition API
Create a ref for the child component, assign it in the template, and use the <ref>.value to call the child component directly.
<script setup>
import {ref} from 'vue';
const childComponentRef = ref(null);
function click() {
// `childComponentRef.value` accesses the component instance
childComponentRef.value.doSomething(2.0);
}
</script>
<template>
<div>
<child-component ref="childComponentRef" />
<button #click="click">Click me</button>
</div>
</template>
Couple things to note-
If your child component is using <script setup>, you'll need to declare public methods (e.g. doSomething above) using defineExpose.
If you're using Typescript, details of how to type annotate this are here.
Vue 3 Options API / Vue 2
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 3 docs on component refs or Vue 2 documentation on refs.
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.
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/
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/vue#2.5.17/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.
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>
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>
If you have time, use Vuex store for watching variables (aka state) or trigger (aka dispatch) an action directly.
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")
}
}
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
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 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");
}
}
}
I have data in a parent Vue which is passed as a prop to a child view. If the data is changed in the parent, exactly when does the child see this change?
I see that this is not instantaneous.
In the fragment below, I pass a parent's data as a prop to the child. data is then incremented in the parent and the event is broadcast to the child via a bus. The child reads the parent data and sees the older value, not the just increment value.
<div id="wrapper">
data in parent: {{ data }}
<child :data="data" :bus="bus"></child>
<button #click="incrementParentData">
Increment parent data
</button>
</div>
var Child = {
template: '<div>data ref in child: {{ data }}</div>',
props: ['data', 'bus'],
created: function() {
var self = this;
this.bus.$on('parentDataChanged', function(data) {
alert("data ref in child $on handler:" + self.data);
});
}
}
new Vue({
el: '#wrapper',
data: function() {
return {
data: 1,
bus: new Vue()
}
},
components: {
'child': Child
},
methods: {
incrementParentData: function() {
this.data = this.data + 1;
alert("data in parent incremented to:" + this.data);
this.bus.$emit("parentDataChanged", this.data);
}
}
})
JSFiddle: here
Questions:
When will the child see the change?
What do I have to do to see the updated values in the child inside the $on handler?
PS: I'm passing the changed value as argument to the $emit, but in my real world case, the child does not need to know anything about what data in the parent changed. All the child has to do is take the parent's data and pass it to a backend api.
the problem is that you don't need to use a bus to catch when a prop has changed.
What you are looking for are watchers, it will be triggered as soon as the prop being watch changes.
You can read more about watchers on the vuejs documentation.
https://v2.vuejs.org/v2/guide/computed.html#Watchers
Hope this helps you.