Vuejs Update Parent Data from Child Component

vuejs update parent data from child component

Two-way binding has been deprecated in Vue 2.0 in favor of using a more event-driven architecture. In general, a child should not mutate its props. Rather, it should $emit events and let the parent respond to those events.

In your specific case, you could use a custom component with v-model. This is a special syntax which allows for something close to two-way binding, but is actually a shorthand for the event-driven architecture described above. You can read about it here -> https://v2.vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events.

Here's a simple example:

Vue.component('child', {
template: '#child',

//The child has a prop named 'value'. v-model will automatically bind to this prop
props: ['value'],
methods: {
updateValue: function (value) {
this.$emit('input', value);
}
}
});

new Vue({
el: '#app',
data: {
parentValue: 'hello'
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>

<div id="app">
<p>Parent value: {{parentValue}}</p>
<child v-model="parentValue"></child>
</div>

<template id="child">
<input type="text" v-bind:value="value" v-on:input="updateValue($event.target.value)">
</template>

Vue.js update parent data with an input from a child component

You're almost there, you can see in the code you've provided that the child component event is being emitted but the value is empty. The problem is you're not updating myDescription, if you change your :value to v-model then it will update, as v-model uses two way binding.

Also, if you want to update the file description, you can just do:

file.description = description;

Vue.component('myComponent', {
props: ["file"],
data() {
return {
myDescription: '',
}
},
mounted() {
this.myDescription = this.file.description;
},
template: `
<div>
<label>{{ file.name }}</label>
<br>
<input type="text" @input="update" v-model="myDescription"></input>
<br><br>
</div>
`,
methods: {
update() {
this.$emit("update-description", this.myDescription, this.file);
},
}
})

var app = new Vue({
el: '#app',
methods: {
updateDescription(description, file) {
console.log(description);
file.description = description;
}
},
data: {
files: [{
id: 1,
name: "Hello",
description: "",
},
{
id: 2,
name: "World",
description: "Foo",
},
{
id: 3,
name: "John",
description: "Bar",
}
]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
<div> {{ files }} </div>
<br>
<my-component v-for="file in files" :key="file.id" :file="file" @update-description="updateDescription" />
</div>

Vue 3: Unable to update parent data from child component checkbox

There's a couple of problems here:

  1. $event.target.value is a string rather than a boolean. Change this to $event.target.checked
  2. Your parent is listening to input but your child is emitting update:modelValue. Save yourself a lot of hassle and use v-model in the parent:
<CheckboxComponent
v-model="profile.doYouAgree"
label="Do you agree?"
/>

Update Parent Data from Child Modal Data using Props in vue

You emit event and pass data this.$emit('updated', this.client)
In parent component you listen to event from child and trigger method @updated="handleUpdate"

Take a look at following snippet pls:

Vue.component('client-add-form', {
template: `
<div class="modal-card" style="width: auto">
<header class="modal-card-head">
<p class="modal-card-title">Add/Modify Customer Information</p>
</header>
<section class="modal-card-body">
<b-field label="Name">
<b-input type="text" v-model="client.name" placeholder="Name"> </b-input>
</b-field>
<b-field label="Phone">
<b-input type="phone" v-model="client.phone" placeholder="Phone"> </b-input>
</b-field>
<b-field label="Email">
<b-input type="email" v-model="client.email" placeholder="Email"> </b-input>
</b-field>
<b-field label="Address">
<b-input type="textarea" v-model="client.address" placeholder="Address">
</b-input>
</b-field>
<b-field label="City">
<b-input type="text" v-model="client.city" placeholder="City"> </b-input>
</b-field>
<b-field label="Country">
<b-input type="text" v-model="client.country" placeholder="Country"> </b-input>
</b-field>
</section>
<footer class="modal-card-foot">
<b-button label="Close" @click="$parent.close()" />
<b-button label="Save" type="is-primary" @click="update" />
</footer>
</div>
`,
props: ["formData"],
data() {
return {
client: {...this.formData}
}
},
methods: {
update(){
//Database Operations etc
this.$emit('updated', this.client)
this.$parent.close()
}
},
})

new Vue({
el: '#demo',
data() {
return {
isComponentModalActive: false,
form: {
clientData: {
name: "Gaurav Kumar",
email: "imthegrv@gmail.com",
phone: "",
address: "",
city: "",
country: "",
taxCode: "",
Type: "",
},
}
}
},
methods: {
handleUpdate(client) {
this.form.clientData = client
}
}
})
<link rel="stylesheet" href="https://unpkg.com/buefy/dist/buefy.min.css">
<script src="https://unpkg.com/vue@2"></script>
<script src="https://unpkg.com/buefy/dist/buefy.min.js"></script>
<script src="https://unpkg.com/buefy/dist/components/table"></script>
<script src="https://unpkg.com/buefy/dist/components/input"></script>
<div id="demo">
<button @click="isComponentModalActive = true">
Add New Client
</button>
<b-modal
v-model="isComponentModalActive"
has-modal-card
full-screen
:can-cancel="false"
>
<client-add-form
:form-data="form.clientData"
@updated="handleUpdate"
></client-add-form>
</b-modal>
{{ form }}
</div>

How to change parent data from a child component?

I think that doesn't work because you're missing emits: ['update:count'] option in child component, But I recommend to name the prop as modelValue in child component and use v-model directive in parent instead of @update:count event :

<template>
<div>
<HelloWorld v-model="count" />
</div>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";

export default {
name: "App",
data() {
return {
count: 10
};
},
components: {
HelloWorld
}
};
</script>

HelloWorld.vue :

<template>
<div class="hello">
<input
type="number"
min="0"
:value="count"
@input="$emit('update:modelValue', $event.target.value)"
style="width:6em"
/>
</div>
</template>

<script>
export default {
name: "HelloWorld",
props: {
modelValue: Number
},
emits: ['update:modelValue'],
};
</script>

This allows you to create a custom input

how to edit the data value of the parent component from the child component in vue js

The solution is to use event emitting (Youtube Tutorial) to send updated data up to the parent component from the child component.
so the code becomes like this:
the parent component:

<template>
<div>
<button @click="show?show = false:show = true">
{{show?"Hide":"Show"}}
</button>
<item :show="show" @state="update($event)" />
</div>
</template>

<script>
import item from "item.vue"

export default {
components: {
item
},
data() {
return {
show: null,
};
},
methods: {
update(value) {
this.show = value;
}
}
};
</script>

the child component:

<template>
<div @click="action" v-if="show">
Vue Js - click here to Hide
</div>
</template>

<script>
export default {
props: {
show: Boolean,
},
methods: {
state() {
if (this.show) {
return false;
} else {
return true;
}
},
action() {
this.$emit("state", this.state())
}
}
};
</script>

Thank you @D.Schaller for helping

Update parent model from child component Vue

You can use custom events to send the data back.

To work with custom events, your data should be in the parent component, and pass down to children as props:

<step1 :someValue="value" />

and now you want to receive updated data from child, so add an event to it:

<step1 :someValue="value" @update="onStep1Update" />

your child components will emit the event and pass data as arguments:

this.$emit('update', newData)

the parent component:

methods: {
onStep1Update (newData) {
this.value = newData
}
}

Here is a simple example with custom events:

http://codepen.io/CodinCat/pen/QdKKBa?editors=1010


And if all the step1, step2 and step3 contain tons of fields and data, you can just encapsulate these data in child components (if the parent component doesn't care about these row data).

So each child has its own data and bind with <input />

<input v-model="data1" />
<input v-model="data2" />

But the same, you will send the result data back via events.

const result = this.data1 * 10 + this.data2 * 5
this.$emit('update', result)

(again, if your application becomes more and more complex, vuex will be the solution.

How to update the child component b-tabs value from parent component in vuejs

The error you're facing happens when you're trying to mutate the value of a prop, because prop values are coming from parents. If the parent changes it, your change inside the child will be overwritten.

Thus, when a child needs to change a prop, it has to emit an event to the parent, update the parent value which, in turn flows the change back into the child, via the prop.

Props down, events up!

To eliminate part of the boilerplate for this mechanism, Vue 2 provided the .sync modifier.

In Vue3, .sync was deprecated and removed (breaking change)!

someProp.sync has been replaced by v-model:someProp, because:

  • they were providing the same functionality (dual binding),
  • this increased the confusion between the two syntaxes and their particular implementation details.
  • now v-model behaviour is aligned across native form elements and Vue components.

Vue's author (Evan You), has declared over the past years that, going forward, the core team's intention is to reduce the API surface, by removing unnecessary or confusing syntax options.


Vue 2 example:

Vue.component('tab', {
template: `#tab-tpl`,
props: ['index', 'text'],
computed: {
tIndex: {
get() { return this.index },
set(val) { this.$emit('update:index', val); }
}
}
})
new Vue({
el: '#app',
data: () => ({
tabs: [{text: 'one'}, {text: 'two'}, {text: 'three'}],
tabIndex: 0
}),
computed: {
currentIndex: {
get() { return this.tabIndex },
set(val) {
this.tabIndex = (this.tabs.length + val) % this.tabs.length;
}
}
}
})
.tab-pills {
display: flex;
border-bottom: 1px solid #ccc;
cursor: pointer;
}
.active {
color: red;
}
.tab-pills > div { padding: .3rem .7rem; }
.tab-content { padding: 1rem .7rem; }
.tab-body { padding: 1rem 0 }
<script src="https://v2.vuejs.org/js/vue.min.js"></script>

<div id="app">
<div class="tab-pills">
<div v-for="(tab, key) in tabs"
v-text="tab.text"
:class="{ active: key === currentIndex }"
:key="key"
@click="currentIndex = key">
</div>
</div>
<tab :index.sync="currentIndex" :text="tabs[currentIndex].text" class="tab-content"></tab>
</div>
<template id="tab-tpl">
<div>
<div v-text="text" class="tab-body"></div>
<button @click="tIndex -= 1">Prev</button>
<button @click="tIndex += 1">Next</button>
</div>
</template>

(VueJS) Update parent data from child component

There isn't the relation that you said between the two components.

The component that you called parent is in reality the child... and the child is parent.

The parent component is always the one that calls the other, in your case:

//Parent component
<template>
...
<msm-select :options="policies" :model="isShowingUpdate" /> << the child
...
</template>

You should change the props/events between the two components.

Edit:

You can edit the:

onChange(value) {
if (this.selected !== value) {
this.flagValue = true;
} else {
this.flagValue = false;
}
}

To a new one like the following:

On the children:

onChange(value) {
if (this.selected !== value) {
this.flagValue = true;
} else {
this.flagValue = false;
}
this.$emit('flagChanged', this.flagValue)
}

On the parent use the emit event to capture and call some other method:

//HTML part:
<msm-select :options="payments" :model="isShowingUpdate" v-on:flagChanged="actionFlagChanged" />

//JS part:
methods: {
actionFlagChanged () {
//what you want
}
}

Can I give you some tips?

  • It's not a (very) good name of a function the one called: onChange
    inside a onChange event... try something like: updateFlag (more
    semantic).

  • I think that you can delete the watch part and do it in the onChange
    event

  • Try to find a good documentation/tutorial (i.e the official documentation) to learn more about parent/child communication.

Remember to add the event-bus import:

import { EventBus } from './event-bus'

Hope it helps!



Related Topics



Leave a reply



Submit