vuejs update parent data from child component
Asked Answered
M

18

243

I'm starting to play with vuejs (2.0). I built a simple page with one component in it. The page has one Vue instance with data. On that page I registered and added the component to html. The component has one input[type=text]. I want that value to reflect on the parent (main Vue instance).

How do I correctly update the component's parent data? Passing a bound prop from the parent is not good and throws some warnings to the console. They have something in their doc but it is not working.

Mcelroy answered 1/12, 2016 at 16:16 Comment(3)
Can you add the code you have tried, which is not working.Shaker
Possible duplicate of How can I share data between non parent-child components in VueHagfish
This communication can be established by: emit event or vuex.Themistocles
W
251

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>

The docs state that

<custom-input v-bind:value="something" v-on:input="something = arguments[0]"></custom-input>

is equivalent to

<custom-input v-model="something"></custom-input>

That is why the prop on the child needs to be named value, and why the child needs to $emit an event named input.

Wickiup answered 1/12, 2016 at 16:36 Comment(14)
first thanks for the response. can you please expand, or better point to documents about the 'input' event? it seems like a built in event.Mcelroy
I have added a clarification, and made the link to the documentation more obvious.Wickiup
I omitted the prop "value" for the component and the created function and it still works. can you explain why you used it?Brominate
If you don't add the prop, then it will be undefined until the first change. See this fiddle where I've commented out props: ['value']. Notice how the initial value is undefined, instead of hello: jsfiddle.net/asemahle/8Lrkfxj6. After the first change, Vue dynamically adds a value prop to the component, so it works.Wickiup
I read right past this in the docs. Great working example. +10 if I could!Tabshey
Looking at the example in the documentation regarding this very issue - vuejs.org/v2/guide/… - I see that the component's template (the input element in the template) itself binds directly to the very data that was bound to the value property of the component: <input v-bind:value="value"> (so when the input is modified by a user, it would modify the parent's price data directly, it seems). The design there does not seem to use a separate, component-specific variable for its input, as your example does... am I right?Lithograph
@DanNissenbaum You are correct! I had implemented v-model in a less elegant way than the Vue docs. I've updated the code sample to be more in-line with the Vue documentation.Wickiup
I was actually thinking that YOUR way is better... that's why I came to this posting to begin with. Doesn't binding the child to the parent's data violate the 'pass props, emit events' mantra? It was that question that caused me to land on this question to begin with and I'm confused by the official documentation's example - whereas your example (before your changes just now) seemed to do it properly, by separating the child's data into a different variable that is a member of the child only, just initialized by the parent and properly calling an event to update the parent. (?)Lithograph
There is no violation. The parent binds value to the child. This means the value prop on the child will match what is on the parent. The child only reads this value, never updates it directly. When the user writes text, the child emits an event with the newest value (method updateValue). In response to that event the parent updates value. In turn, this causes the value prop on the child to be updated.Wickiup
great explanation it was very helpful. One thing I'm still cannot figure out though is how to handle when you need to pass multiple props since you can define just one v-model. Any help?Visser
I still don't understand where you're listening for the emitted event. According to this example: forum.vuejs.org/t/passing-data-back-to-parent/1201/2 we have to define a method in the parent to receive the value. Not sure where this is being handled here... In the forum, they've mentioned parent receives click event using v-on. But here, v-on is defined in the child template itself!Derogatory
It works, thank you bro. I'm tried as you mention on the documentation. I use <custom-input v-bind:value="something" v-on:input="something = arguments[0]"></custom-input> and it works.Homager
This is pretty cool, here is the documentation link for $emit. vuejs.org/v2/guide/…Chagrin
How to propagate changes made in main app to 'parentValue' to component as well? eg 'this.parentValue = "hi again"' still leaves the value as 'hello' in component. Changing in component does reflect in parent app.Hetrick
E
222

In child component:

this.$emit('eventname', this.variable)

In parent component:

<component @eventname="updateparent"></component>

methods: {
    updateparent(variable) {
        this.parentvariable = variable
    }
}
Earwitness answered 9/9, 2018 at 10:15 Comment(6)
@Sarvar Nishonboev - when updateparent method updates the parentvariable, does it "automatically" pass it back to child as prop?Ative
@Ative - no, not in this case because the child component does not receive parentvariable as a prop. If it did, then it would.Jowl
In my case I got an error until I removed this keyword. Works like a charmDurwood
This will require you to create additional functions right.Interpretation
I got it working by using v-on:eventname instead of @eventnameRepeater
This pattern still works in Vue3. :)Recife
S
158

From the documentation:

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.

enter image description here

How to pass props

Following is the code to pass props to a child 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
    }
  }
})
Shaker answered 1/12, 2016 at 16:38 Comment(2)
what if the function ´increment´was in the parent component and I wanted to trigger it from the child component?Ventricular
In this example, shouldn't we have a event listener defined in the root component? Something like: ``` mounted() { this.on('increment', () => { this.incrementTotal(); }); } ```Peppie
R
40

Child Component

Use this.$emit('event_name') to send an event to the parent component.

enter image description here

Parent Component

In order to listen to that event in the parent component, we do v-on:event_name and a method (ex. handleChange) that we want to execute on that event occurs

enter image description here

Done :)

Responsum answered 7/4, 2019 at 2:27 Comment(0)
P
25

I agree with the event emitting and v-model answers for those above. However, I thought I would post what I found about components with multiple form elements that want to emit back to their parent since this seems one of the first articles returned by google.

I know the question specifies a single input, but this seemed the closest match and might save people some time with similar vue components. Also, no one has mentioned the .sync modifier yet.

As far as I know, the v-model solution is only suited to one input returning to their parent. I took a bit of time looking for it but Vue (2.3.0) documentation does show how to sync multiple props sent into the component back to the parent (via emit of course).

It is appropriately called the .sync modifier.

Here is what the documentation says:

In some cases, we may need “two-way binding” for a prop. Unfortunately, true two-way binding can create maintenance issues, because child components can mutate the parent without the source of that mutation being obvious in both the parent and the child.

That’s why instead, we recommend emitting events in the pattern of update:myPropName. For example, in a hypothetical component with a title prop, we could communicate the intent of assigning a new value with:

this.$emit('update:title', newTitle)

Then the parent can listen to that event and update a local data property, if it wants to. For example:

<text-document   
 v-bind:title="doc.title"  
 v-on:update:title="doc.title = $event"
></text-document>

For convenience, we offer a shorthand for this pattern with the .sync modifier:

<text-document v-bind:title.sync="doc.title"></text-document>

You can also sync multiple at a time by sending through an object. Check out the documentation here

Prostration answered 11/11, 2018 at 20:48 Comment(2)
This is what I was looking for. Thanks a lot.Camarillo
This is the best and most up to date solution as of 2020. Thanks a lot!Wonacott
F
16

The way more simple is use this.$emit

Father.vue

<template>
  <div>
    <h1>{{ message }}</h1>
    <child v-on:listenerChild="listenerChild"/>
  </div>
</template>

<script>
import Child from "./Child";
export default {
  name: "Father",
  data() {
    return {
      message: "Where are you, my Child?"
    };
  },
  components: {
    Child
  },
  methods: {
    listenerChild(reply) {
      this.message = reply;
    }
  }
};
</script>

Child.vue

<template>
  <div>
    <button @click="replyDaddy">Reply Daddy</button>
  </div>
</template>

<script>
export default {
  name: "Child",
  methods: {
    replyDaddy() {
      this.$emit("listenerChild", "I'm here my Daddy!");
    }
  }
};
</script>

My full example: https://codesandbox.io/s/update-parent-property-ufj4b

Frenzied answered 12/6, 2019 at 17:23 Comment(1)
this is best and simplest one , you save my time thank you so muchPirnot
M
7

It is also possible to pass props as Object or Array. In this case data will be two-way binded:

(This is noted at the end of topic: https://v2.vuejs.org/v2/guide/components.html#One-Way-Data-Flow )

Vue.component('child', {
  template: '#child',
  props: {post: Object},
  methods: {
    updateValue: function () {
      this.$emit('changed');
    }
  }
});

new Vue({
  el: '#app',
  data: {
    post: {msg: 'hello'},
    changed: false
  },
  methods: {
    saveChanges() {
        this.changed = true;
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>

<div id="app">
  <p>Parent value: {{post.msg}}</p>
  <p v-if="changed == true">Parent msg: Data been changed - received signal from child!</p>
  <child :post="post" v-on:changed="saveChanges"></child>
</div>

<template id="child">
   <input type="text" v-model="post.msg" v-on:input="updateValue()">
</template>
Malinger answered 23/3, 2018 at 13:46 Comment(0)
S
7

In Parent Conponent -->

data : function(){
            return {
                siteEntered : false, 
            };
        },

In Child Component -->

this.$parent.$data.siteEntered = true;

Slide answered 7/11, 2020 at 23:40 Comment(0)
O
5

2021 ANSWER - Vue 2.3+

SHORT ANSWER: Just add .sync modifier in the parent and pass the data as props to the children:

    // PARENT:
    data () {
    return {
      formData: {
        members: [] //<- we wanna pass this one down to children and add/remove from the child component
      }
    }

   // PARENT TEMPLATE:
   <!-- ADD MEMBERS -->
  <add-members :members.sync="formData.members" />

Nested child component: AddMembers.vue

export default {
  name: 'AddMembers',
  props: ['members'],
  methods: {
    addMember () {
      this.members.push(new Member()) // <-- you can play and reactivity will work (in the parent)  
    },
    removeMember (index) {
      console.log('remove', index, this.members.length < 1)
      this.members.splice(index, 1)
    }
  }
}

Long story: changes from the child component in reallity are being $emitted and updating formData.members[] of the parent.

source: Mauro Perez at medium

Oder answered 7/3, 2021 at 22:53 Comment(0)
S
3

In the child

 <input
            type="number"
            class="form-control"
            id="phoneNumber"
            placeholder
            v-model="contact_number"
            v-on:input="(event) => this.$emit('phoneNumber', event.target.value)"
    />

data(){
    return {
      contact_number : this.contact_number_props
    }
  },
  props : ['contact_number_props']

In parent

<contact-component v-on:phoneNumber="eventPhoneNumber" :contact_number_props="contact_number"></contact-component>


 methods : {
     eventPhoneNumber (value) {
      this.contact_number = value
    }
Shamble answered 10/9, 2020 at 1:21 Comment(0)
A
1

The correct way is to $emit() an event in the child component that the main Vue instance listens for.

// Child.js
Vue.component('child', {
  methods: {
    notifyParent: function() {
      this.$emit('my-event', 42);
    }
  }
});

// Parent.js
Vue.component('parent', {
  template: '<child v-on:my-event="onEvent($event)"></child>',
  methods: {
    onEvent: function(ev) {
      v; // 42
    }
  }
});
Aguirre answered 26/12, 2019 at 16:29 Comment(0)
J
1

Intro

I was looking for sending data from parent to child (and back) in vue3 (I know the question was about vue2, but there are no references for vue3 on SO at the time).

Below is the working boilerplate result, pure "html + js", no packagers, modules, etc with few caveats I had, explained.

Notes:

  1. Tnserting the child - line
    <component-a :foo="bar" @newfooevent="bar = $event"></component-a>`
    
  • I bind parent.bar to child.foo using short-hand :foo="bar", same as v-bind:foo="bar". It passes data from parent to child through props.

  • Caveat: Event listener should be placed in the child component tag only!

    That is the @newfooevent="bar = $event" part.

    You cannot catch the signal in the <div id="app"> or anywhere else inside the parent.

    Still, this is the parent's side of the universe, and here you can access all parent's data and extract the data from the child's signal to deal with it.

  1. You can create app, and define component after it (the app.component("component-a", ...) part.

    Caveat: there are no need in forward declaration of components, e.g. functions in C/C++. You can create app which uses the component, and define the component afterwards. I lost a lot of time looking for the way to declare it somehow - no need.

  2. Here you can find a nice example of the v-model usage, and the code I used to sort things out: https://javascript.plainenglish.io/vue-3-custom-events-d2f310fe34c9

The example

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <meta charset="utf-8" />
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <component-a :foo="bar" @newfooevent="bar = $event"></component-a>
      <p>Parent copy of `bar`: {{ bar }}</p>
      <button @click="bar=''">Clear</button>
    </div>

    <script>
      const app = Vue.createApp({
        data() {
          return {
            bar: "bar start value"
          };
        }
      });      

      app.component("component-a", {
        props: {
          foo: String
        },
        template: `
          <input 
            type="text"
            :value="foo"
            @input="$emit('newfooevent', $event.target.value)">
        `
      });      

      app.mount("#app");
    </script>
  </body>
</html>
Justitia answered 25/6, 2021 at 14:9 Comment(0)
E
1

When we want to pass the data to the parent component as well as another nested child component of the current child component, using a data property would be useful as shown in the following example.

Example: Calling your child component from the parent component like this.

Parent component:

<template>
  <TodoItem :todoParent="todo" />
</template>

<script>
export default {
  data() {
    return {
      todo: {
        id:1,
        task:'todo 1',
        completed:false
      }
    };
  }
}
</script>

Child component:

<template>
  <div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
    <p>
      <input type="checkbox" @change="markCompleted" />
      {{todo.task}}
      <button class="del">x</button>
    </p>
  </div>
</template>

<script>
export default {
  name: "TodoItem",
  props: ["todoParent"],
  data() {
    return {
      todo: this.todoParent,
    };
  },
  methods: {
    markCompleted() {
      this.todo.completed = true
    },
  },
};
</script>

Even you can pass this property to the nested child component and it won't give this error/warning.

Other use cases when you only need this property sync between parent and child component. It can be achieved using the sync modifier from Vue. v-model can also be useful. Many other examples are available in this question thread.

Example2: using component events. We can emit the event from the child component as below.

Parent component:

<template>
  <TodoItem :todo="todo" @markCompletedParent="markCompleted" />
</template>

<script>
export default {
  data() {
    return {
      todo: {
        id:1,
        task:'todo 1',
        completed:false
      }
    };
  },
  methods: {
    markCompleted() {
      this.todo.completed = true
    },
  }
}
</script>

Child component:

<template>
  <div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
    <p>
      <input type="checkbox" @change="markCompleted" />
      {{todo.task}}
      <button class="del">x</button>
    </p>
  </div>
</template>

<script>
export default {
  name: "TodoItem",
  props: ["todo"],
  methods: {
    markCompleted() {
      this.$emit('markCompletedParent', true)
    },
  }
};
</script>
Engadine answered 6/6, 2022 at 21:20 Comment(0)
K
0

Another way is to pass a reference of your setter from the parent as a prop to the child component, similar to how they do it in React. Say, you have a method updateValue on the parent to update the value, you could instantiate the child component like so: <child :updateValue="updateValue"></child>. Then on the child you will have a corresponding prop: props: {updateValue: Function}, and in the template call the method when the input changes: <input @input="updateValue($event.target.value)">.

Kibbutz answered 26/1, 2020 at 10:2 Comment(0)
O
0

I don't know why, but I just successfully updated parent data with using data as object, :set & computed

Parent.vue

<!-- check inventory status - component -->
    <CheckInventory :inventory="inventory"></CheckInventory>

data() {
            return {
                inventory: {
                    status: null
                },
            }
        },

Child.vue

<div :set="checkInventory">

props: ['inventory'],

computed: {
            checkInventory() {

                this.inventory.status = "Out of stock";
                return this.inventory.status;

            },
        }
Onstad answered 30/4, 2020 at 6:51 Comment(0)
I
0

his example will tell you how to pass input value to parent on submit button.

First define eventBus as new Vue.

//main.js
import Vue from 'vue';
export const eventBus = new Vue();

Pass your input value via Emit.
//Sender Page
import { eventBus } from "../main";
methods: {
//passing data via eventbus
    resetSegmentbtn: function(InputValue) {
        eventBus.$emit("resetAllSegment", InputValue);
    }
}

//Receiver Page
import { eventBus } from "../main";

created() {
     eventBus.$on("resetAllSegment", data => {
         console.log(data);//fetching data
    });
}
It answered 6/7, 2020 at 13:38 Comment(0)
C
0

I think this will do the trick:

@change="$emit(variable)"

Chalice answered 15/7, 2020 at 20:19 Comment(0)
C
0

There is another way of communicating data change from child to parent which uses provide-inject method. Parent component "provides" data or method for the child component, and this data or method is then "injected" into child component - but it can also be used for triggering a method in parent and passing it a parameter.
This approach can be especially useful when having a child component which happens to be embedded in multiple other components. Also, in a large project care must be taken not to lose overview of provide and inject usage.

Example of parent (top level) component App.vue using provide to give access to it's method updateParentValue (if method is provided and not data, provide is in form of a method):

<template>
  <h2>App.vue, parentValue is: <em>{{ parentValue }}</em></h2>
  <ChildComponent1 />
</template>

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

export default {
  data() {
    return {
      parentValue: "",
    };
  },
  components: {
    ChildComponent1,
  },
  provide() {
    return {
      updateParent: this.updateParentValue,
    };
  },
  methods: {
    updateParentValue($value) {
      this.parentValue = $value;
    },
  },
};
</script>

In this example component Component4.vue is in the "bottom", that is, App.vue contains Component1, Component1 contains Component2... until Component4 which actually utilizes inject to get access to parent method which is then invoked and a parameter $value is passed (just a random number here):

<template>
  <div>
    <h2>ChildComponent4.vue</h2>
    <button @click="updateParent(Math.random())">
      Update parent value in App.vue
    </button>
  </div>
</template>

<script>
export default {
  inject: ["updateParent"],
};
</script>

Entire example is available here.
Vue.js documentation

enter image description here

Cordiecordier answered 22/9, 2021 at 2:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.