Child component to use parent function in vue js - javascript

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!

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>

// 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";
},
}

Related

(Vue.js 3 Options API) How do I access a child method?

I need to access a child component's method using Vue.js 3 with Options API. There is an answer at How to access to a child method from the parent in vue.js, but it is for Vue.js 2 and Vue.js 3 but with Composition API.
I still tried this, all in the parent component:
<dropdown-list #update="updateChildComponents"></dropdown-list>
<child-component-1 ref="childComponent1Ref" :url="url"></child-component-1>
<child-component-2 ref="childComponent2Ref" :url="url"></child-component-2>
and
methods: {
updateChildComponents() {
this.$refs.childComponent1Ref.childComponentMethod();
this.$refs.childComponent2Ref.childComponentMethod();
}
}
This actually successfully accesses the method, but I think this may not be the right way.
Secondly, I use a prop in the child component that I update in the parent and use in the child component's method, which updates only after the second event. I think these two may be related.
Child component:
props: ['url'],
methods: {
childComponentMethod() {
console.log(this.url); // can access the value from the previous event
}
}
I appreciate any help.
For communication between the parent and the children you should trasfer the value with props. In case the value is changing in the parent you must add watch.
It called 1 way binding because the parent cant see the changes in the child component.
Parent -> child = use props.
Child -> parent = use emits and on event.
<script>
import { reactive,watch, computed,onMounted } from "vue";
export default {
components: {
},
props: { metadata: String },
emits: [""],
setup(props) {
onMounted(async () => {
//first initilize
if (props.metadata) {
state.metadataName = props.metadata;
}
});
//track after the changes in the parent
watch(
() => props.metadata,
async (metadata) => {
if (metadata) {
}
}
);
return {
};
},
};
</script>

Mounted in child component executes before parent component created issue

I am working on vue application. The issue I am facing here is that the child component mounted is executed before the parent child created.
I am sending props from parent component to child component and I want to use those props in the child component's mounted but I am not getting those props in mounted as they are executed before created in parent. Please help me fix this issue so that child component sets this.userCopy to this.user which is coming as a prop.
parent componeent
<template>
<div>
<Info :user="user" />
</div>
</template>
<script>
import Info from 'views/info';
export default {
components: {
Info
},
data () {
return {
user: {
first_name: '',
last_name: '',
},
errors:[]
}
}
created() {
this.fetchUsers();
},
methods: {
fetchUsers() {
this.$axios.get('/user.json')
.then(response => {
this.user = response.data;
}).catch(error => {
this.errors = error.response.data.error;
});
},
}
}
</script>
child component
<template>
<div>
{{userCopy}}
</div>
</template>
<script>
export default {
props: ['user'],
data () {
return {
userCopy: {}
}
}
mounted: function() {
var that = this;
this.userCopy = this.user
}
}
</script>
Since user is updated asynchronously after the component is already mounted, user would be the initial value (undefined) in the mounted() hook.
Option 1: The parent could conditionally render the child component based on user, so that the component's user prop would have a value upon mounting:
<Info v-if="user" :user="user">
export default {
data() {
return {
user: null, // updated asynchronously in fetchUsers()
}
}
}
Option 2: The child could use a watcher on user that updates userCopy:
export default {
//...
watch: {
user(user) {
this.userCopy = { ...user } // shallow copy
}
}
}
Note the use of the spread operator to shallow copy user.
Actually, your created is being invoked before your children component's mounted. The problem is that the fetchUsers is asynchronous (a Promise) and needs to be awaited.
async created() {
await this.fetchUsers();
},
Try this code, awaiting the asynchronous operation.
Something I've done is have a loaded value in my data. Then on that child component that relies on that data, I'll throw a v-if="loaded".
data() {
return {
loaded: false
}
},
async created() {
try {
await this.fetchUsers();
// loaded will be set after the users have been fetched.
this.loaded = true;
}
catch(error) {
console.error('Failed to grab the users', error)
}
}
then in your template just do...
<child-component v-if="loaded" />

Vue sibling components hooks lifecycle relationships

Never used Vue.js before. I have one parent component and 2 child components. These 2 child components should interact with asynchronous actions using the native Vue event bus system (with a dummy Vue object used as a shared container for the event bus).
Having something like the following:
EventBus.js
import Vue from "vue"
export default new Vue()
Parent.vue
import Child1 from "./Child1.vue"
import Child2 from "./Child2.vue"
export default {
name: "Parent",
components: {
child1: Child1,
child2: Child2,
}
}
Child1.vue
import EventBus from "./EventBus"
export default {
name: "Child1",
beforeCreate () {
EventBus.$once("MY_EVENT_X", async () => {
EventBus.$emit("MY_EVENT_Y")
})
},
mounted () {
// something
}
}
Child2.vue
import EventBus from "./EventBus"
export default {
name: "Child2",
beforeCreate () {
EventBus.$once("MY_EVENT_Y", async () => {
// do something
})
},
mounted () {
EventBus.$emit("MY_EVENT_X")
}
}
My question: having the events handlers defined in the "beforeCreate" hook, can I be sure that the "beforeCreate" hooks of both Child1 and Child2 components will be initiliazed before any of the "mounted" hooks of Child1 or Child2 will be called by Vue?
You can leverage component hook order between parent and children. When parent mounted is called, we will be sure all child components is created and mounted.
Image source from here
To do it, you can define a boolean flag in parent, change this flag to true in mounted hook:
import Child1 from "./Child1.vue"
import Child2 from "./Child2.vue"
export default {
name: "Parent",
components: {
child1: Child1,
child2: Child2,
},
data: () => ({
isChildMounted: false
}),
mounted() {
this.isChildMounted = true
}
}
Make sure to pass this flag to children component:
<child-2 :isReady="isChildMounted" />
Finally, in child component, watching for props change. When the flag is changed to true, we know all child components is ready. It's time to emit event.
import EventBus from "./EventBus"
export default {
name: "Child2",
props: ['isReady'],
beforeCreate () {
EventBus.$once("MY_EVENT_Y", async () => {
// do something
})
},
watch: {
isReady: function (newVal) {
if (newVal) {
// all child component is ready
EventBus.$emit("MY_EVENT_X")
}
}
}
}

Vue - Passing data from child to parent - Basic emit not working

I have a basic Vue2 component and I am trying to pass data from the child to the parent component using $emit. Note, my-component contains a table where when I click on a row, onRowClick fires successfully, outputting 'foobar' to the console. For some reason, I'm not able to get the parent method to fire on $emit and success isn't being logged to console. Any idea why this is?
import Vue from 'vue';
import MyComponent from "./components/MyComponent.vue";
window.onload = function () {
var main = new Vue({
el: '#app-v2',
components: { MyComponent },
methods: {
onCouponApplied(data) {
console.log("Success, coupon applied, " + data);
this.saying = "Sunglasses are " + data; //Sunglasses are cool
}
},
data: {
contactEditPage: {
saying: ""
}
}
});
}
MyComponent.vue
export default {
methods: {
onRowClick(event){
this.$emit('applied', 'Cool');
console.log("foobar");
}
HTML
<div id="app-v2">
<my-component #applied="onCouponApplied"></my-component>
{{ saying }} //trying to output: sunglasses are cool
</div>
I faced the same problem. I fixed this issue by using $root.
For example in parent Vue component
mounted(){
this.$root.$on('event-name',function(value){
// code
});
},
And child component should be like this:
this.$root.$emit('event-name',[data]);
Hope can help you.

Unable to overwrite method in Vue constructor

I have a component with one method, which I'm firing on creation. It's using vue-select but purpose of this component shouldn't be relevant to my issue.
<template>
<v-select :on-change="onchangecallback"></v-select>
</template>
<script>
import Vue from 'vue'
import vSelect from 'vue-select'
Vue.component('v-select', vSelect);
export default {
methods: {
onchangecallback: () => {alert('default')}
},
created: function() {
this.onchangecallback();
}
}
</script>
In other file I'm importing this component and creating a new instance of it with Vue constructor and passing new onchangecallback method, which, by my understanding, should overwrite the default onchangecallback method:
import VSelect from './components/ui/VSelect.vue';
new Vue({
VSelect,
el: '#app',
components: {VSelect},
template: `<v-select />`,
methods: {
onchangecallback: () => {alert('custom')} // doesn't work
}
});
But when I start the app, instead of alert('custom') I still get alert('default').
I don't know what you are trying to achieve.
But here is my solution https://codesandbox.io/s/84qw9z13v9
You need to define a prop to pass your new callback function to that component (through the prop)
props: {
onchangecallback: {
type: Function,
default() {
return function() {
alert('default');
};
},
},
},
created: function() {
this.onchangecallback();
}
And the use it instead the default one.
Check all the code in that snippet.
For communication between components you're supposed to use events. Emit an event in a child component and make the parent component listen to it:
In the child component:
created() {
this.$emit('change')
}
In the parent component:
Template:
<child-component v-on:change="doSomething" />
Methods:
methods: {
doSomething() {
// ...
}
}
Explanation
In the child component you emit an event, in this case "change", whenever something changed. In your case this was upon creation of the component.
In the parent component you tell Vue that, whenever the child component emits a "change" event, it should run the method "doSomething" in your parent component.
This is used in many places, i.e. inputs, where the input emits an event "input" whenever its content changes and any parent can listen for that by using v-on:input="methodHere".

Categories

Resources