Share data between two child components Vue js - vue.js

i am trying to send and render some data from a child component to another child component & both components are rendered using one main component , how can i pass the data between two child components (in my vue app)?
exg
I have two child components A & B , "B" has click add to cart click event and have data of cart items , & i have template for cart item in component "A"

In this situation, as both components share the same parent, it's common to move whatever shared state you need into the parent component and pass to the children as props.
Where components don't shared a direct parent, you can use an event bus (for very small apps) or Vuex. Vuex lets you share state between components in your app without having to pass props down through multiple levels.

There are a lot of ways to achieve this. I am explaining with global even bus concept. Following is an example. Here, child A and child B are communicating through event bus
const eventBus = new Vue ()
Vue.component('ChildB',{
template:`
<div id="child-b">
<h2>Child B</h2>
<pre>data {{ this.$data }}</pre>
<hr/>
</div>`,
data() {
return {
score: 0
}
},
created () {
eventBus.$on('updatingScore', this.updateScore) // 3.Listening
},
methods: {
reRender() {
this.$forceUpdate()
},
updateScore(newValue) {
this.score = newValue
}
}
})
Vue.component('ChildA',{
template:`
<div id="child-a">
<h2>Child A</h2>
<pre>data {{ this.$data }}</pre>
<hr/>
<button #click="changeScore">Change Score</button>
<span>Score: {{ score }}</span>
</div>`,
props: ["score"],
methods: {
changeScore() {
this.score +=200;
eventBus.$emit('updatingScore', this.score+ 200)
}
}
})
Vue.component('ParentA',{
template:`
<div id="parent-a">
<h2>Parent A</h2>
<pre>data {{ this.$data }}</pre>
<hr/>
<child-a :score="score"/>
<child-b/>
</div>`,
data() {
return {
score: 100
}
}
})
Vue.component('GrandParent',{
template:`
<div id="grandparent">
<h2>Grand Parent</h2>
<pre>data {{ this.$data }}</pre>
<hr/>
<parent-a/>
</div>`,
})
new Vue ({
el: '#app',
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<grand-parent/>
</div>

Ideally, you should use Vuex as state management pattern.
But if your application is very simple and you don't need to do those operations often, you can pass data in emit payload from child to parent, and then parent component should pass it to another child via props

Related

how to pass an event from a child component to a parent?

I'm studying vue js3 - I ran into a misunderstanding of linking variables in components. You need to pass the #click event from the child component, so that the value in the parent has changed.
Example:
children component
<div class="btn" id="btn" #click="addToCart(item)"> Add to cart</div>
Parent component
<p >{{cardValue}}</p>
It is necessary to increase the value of {{cardValue}} by 1 when clicking in the child component
As per my understanding, You are working on an e-commerce application where you want to add the items in a cart from a child component but cart items counter is in parent component. If Yes, Then it is necessary to emit an event to parent on click of Add to cart button, So that it will increment the counter by 1.
Live Demo :
const { createApp, ref, defineComponent } = Vue;
const app = createApp({
setup() {
const cartItemsCount = ref(0)
// expose to template and other options API hooks
return {
cartItemsCount
}
}
});
app.component('item', defineComponent({
template: '<button #click="addToCart">Add to Cart</button>',
methods: {
addToCart() {
this.$emit("addToCart");
}
}
}));
app.mount('#app')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="app">
<div>
Items added into the cart : {{ cartItemsCount }}
</div>
<item #add-to-cart="cartItemsCount += 1"></item>
</div>

How to prevent the data/method sharing of looped components in Vue.js

I have the vue component with $emit into component and let it return the data from the component. I will use the component to update current page's data. the codes below
Template:
<Testing
#update="update">
</Testing>
<AnotherComponent
:data="text"
>
</AnotherComponent>
Script:
method(){
update: function(data){
this.text = data.text
}
}
it work perfectly if only this one.
Now , i need to make a button to add one more component.
I use the for loop to perform this.
Template
<div v-for="index in this.list">
<Testing
:name="index"
#update="update">
</Testing>
<AnotherComponent
:data="text"
>
</AnotherComponent>
</div>
Script:
method(){
addList : function(){
this.list +=1;
},
deleteList : function(){
this.list -=1;
},
update: function(data){
this.text = data.text
}
}
The add and delete function run perfectly.
However , they share the "update" method and the "text" data.
so , If I change the second component , the first component will also changed.
I think this is not the good idea to copy the component.
Here are my requirements.
This component is the part of the form, so they should have different name for submit the form.
The another component" will use the data from the "testing component" to do something. the "testing" and "another component" should be grouped and the will not change any data of another group.
Any one can give me the suggestion how to improve these code? Thanks
What happends is that both are using the data form the parent, and updating that same data.
It seems that you are making some kind of custom inputs. In that case in your child component you can use 'value' prop, and 'input' event, and in the parent user v-model to keep track of that especific data data.
Child component BaseInput.vue:
<template>
<div>
<input type="text" :value="value" #keyup="inputChanged">
</div>
</template>
<script>
export default {
props: ['value'],
data () {
return {
}
},
methods: {
inputChanged (e) {
this.$emit('input', e.target.value)
}
}
}
</script>
And this is the code on the parent:
<template>
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3">
<base-input v-model="firstInputData"></base-input>
<p>{{ firstInputData }}</p>
<hr>
<base-input v-model="secondInputData"></base-input>
<p>{{ secondInputData }}</p>
</div>
</div>
</div>
<script>
import BaseInput from './BaseInput.vue'
export default {
components: {BaseInput},
data() {
return{
firstInputData: 'You can even prepopulate your custom inputs',
secondInputData: ''
}
}
}
</script>
In the parent you could really store the diferent models in an object as properties, and pass that object to that "The another component" , pass them as individual props... pass an array ....

Undefined props in Vue js child component. But only in its script

I have just started using Vue and experienced some unexpected behavior. On passing props from a parent to child component, I was able to access the prop in the child's template, but not the child's script. However, when I used the v-if directive in the parents template (master div), I was able to access the prop in both the child script and child template. I would be grateful for some explanation here, is there a better was of structuring this code? See below code. Thanks.
Parent Component:
<template>
<div v-if="message">
<p>
{{ message.body }}
</p>
<answers :message="message" ></answers>
</div>
</template>
<script>
import Answers from './Answers';
export default {
components: {
answers: Answers
},
data(){
return {
message:""
}
},
created() {
axios.get('/message/'+this.$route.params.id)
.then(response => this.message = response.data.message);
}
}
</script>
Child Component
<template>
<div class="">
<h1>{{ message.id }}</h1> // works in both cases
<ul>
<li v-for="answer in answers" :key="answer.id">
<span>{{ answer.body }}</span>
</li>
</ul>
</div>
</template>
<script>
export default{
props:['message'],
data(){
return {
answers:[]
}
},
created(){
axios.get('/answers/'+this.message.id) //only worls with v-if in parent template wrapper
.then(response => this.answers = response.data.answers);
}
}
</script>
this.message.id only works with v-if because sometimes message is not an object.
The call that you are making in your parent component that retrieves the message object is asynchronous. That means the call is not finished before your child component loads. So when your child component loads, message="". That is not an object with an id property. When message="" and you try to execute this.message.id you get an error because there is no id property of string.
You could continue to use v-if, which is probably best, or prevent the ajax call in your child component from executing when message is not an object while moving it to updated.

defining the layout of child component from parent in Vue js

I'm new to Vue and using Vue 2.2.1. I am wondering if it's possible to create a reusable component that can have its layout defined by its parent. For example, consider the following pseudo code:
// Parent template
<template>
<ul>
<li v-for="item in items">
<item-component :id="item.id">
<h1><item-title /></h1>
<p>
<item-description />
</p>
</item-component>
</li>
</ul>
</template>
// Child definition
<script>
export default {
data() {
return {
title: '',
description: ''
}
}
create() {
// do some async fetch
fetch(this.id)
.then((result) {
this.$data.title = result.title
this.$data.description = result.description
})
}
}
</script>
So, the use case is that the child component is responsible for the fetching of the data by id, but the parent is responsible for laying out the data. This way, I can keep the fetch logic in one place, but reformat the data however I want in various places.
Not sure if this is possible or not. I suppose I can extract the child's fetching functionality out into a mixin, but then I'd have to create a new component for each layout variation. What is the recommended way to handle this in Vue?
In general, when you want the parent to include content in the child, the means to do it is via a slot. Inside, a typical slot, however, the scope is the parent's scope, meaning it does not have access to data inside the child.
In your case, you would want to use a scoped slot, which is where the child is able to pass some information back to the parent to use.
// Parent template
<template>
<ul>
<li v-for="item in items">
<item-component :id="item.id">
<template scope="props">
<h1>{{props.title}}</h1>
<p>
{{props.description}}
</p>
</template>
</item-component>
</li>
</ul>
</template>
// Child definition
<script>
export default {
template:"<div><slot :title='title' :description='description'></slot></div>",
data() {
return {
title: '',
description: ''
}
}
create() {
// do some async fetch
fetch(this.id)
.then((result) {
this.$data.title = result.title
this.$data.description = result.description
})
}
}
</script>

How split vue single components template section in to smaller subtemplates

My application is being build on vuejs#2 has multiple forms most of the share same html template with add and reset button. As well as same method, resetForm nullifies the "item" property and resets the form, and create method sends the item to the backend.
<div class="row">
<div class="action">
<button class="btn btn-white" #click="create()">✎ Add</button>
<button class="btn btn-white" #click="resetForm()">❌ Reset</button>
</div>
</div>
I can share methods via mixins with each component but I can't share "template partial" same way. How to you approach such scenario?
I tried to create component create-reset-buttons, but I have no way to trigger parent method as each component encapsulates its functionality and does not allow to modify props from the child. Which need to be done in order to reset the parent form.
Components are not allowed to modify the props, but there are ways child can communicate to parent as explained here in detail.
In Vue.js, the parent-child component relationship can be summarized as props down, events up. The parent passes data down to the child via props, and the child sends messages to the parent via events. Let’s see how they work next.
How to pass props
Following is the code to pass props to chile element:
<div>
<input v-model="parentMsg">
<br>
<child v-bind:my-message="parentMsg"></child>
</div>
How to emit event
HTML:
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
JS:
Vue.component('button-counter', {
template: '<button v-on:click="increment">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
increment: function () {
this.counter += 1
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
})