17

I use Vue.js 2.5.13 and have this structure:

component-one.vue:

    <template>
      <div>
        <input type="text" v-model="input_one">
        <component-two></component-two>
      </div>
    </template>

    <script>
      import ComponentTwo from 'component-two.vue'

      export default {
        name: "component-one",
        components: {
          ComponentTwo
        },
        data() {
          return {
            input_one: 'Hello from ComponentOne',
            input_two: ... // <-- I want to get value from ComponentTwo input_two (v-model) here
          }
        }
      }
    </script>

component-two.vue:

    <template>
      <div>
        <input type="text" v-model="input_two">
      </div>
    </template>

    <script>
      export default {
        name: "component-two",
        data() {
          return {
            input_one: 'Hello from ComponentTwo'
          }
        }
      }
    </script>

How to get data from ComponentTwo in component ComponentOne? This is important for me, because I've many similar components with fields (huge registration site form) and have no idea about calling data between Vue-components..

raz
  • 409
  • 6
  • 17
koddr
  • 764
  • 2
  • 9
  • 27
  • Possible duplicate of [Share data across different components in Vuejs](https://stackoverflow.com/questions/36017085/share-data-across-different-components-in-vuejs) – Roy J Jan 29 '18 at 12:06
  • 1
    @RoyJ - I don't think I'd reference that one as a possible duplicate since that answer proposes using the `.sync` modifier which has [been removed from vue](https://vuejs.org/v2/guide/migration.html#once-and-sync-Modifiers-on-v-bind-removed). But, for sure, there's plenty of other existing questions that provider answers for this situation. – PatrickSteele Jan 29 '18 at 13:51

5 Answers5

14

Vuejs uses "props" for parent/children communication and "emits" events for children/parent communication

enter image description here

You have to remember that for every prop you pass to the child component you should add that prop to the props array. The same applies to events: every events you emit you should be caught in the parent component, like so:

component-one.vue:

    <template>
      <div>
        <input type="text" v-model="input_one">
        <component-two
            @CustomEventInputChanged="doSomenthing">
        </component-two>
      </div>
    </template>

    <script>
      import ComponentTwo from 'component-two.vue'

      export default {
        name: "component-one",
        components: {
          ComponentTwo
        },
        data() {
          return {
            input_one: 'Hello from ComponentOne',
            input_two: ''
          }
        },
        methods: {
            doSomenthing ( data ) {
                this.input_two = data;
            }
        }
      }
    </script>

component-two.vue:

    <template>
      <div>
        <input type="text" v-model="input_two" @change="emitEventChanged>
      </div>
    </template>

    <script>
      export default {
        name: "component-two",
        data() {
          return {
            input_one: 'Hello from ComponentTwo'
          }
        },
        methods: {
            emitEventChanged () {
                this.$emit('CustomEventInputChanged', this.input_two);
            }
        }

      }
    </script>

This should work

danronmoon
  • 3,814
  • 5
  • 34
  • 56
Luca Giardina
  • 478
  • 4
  • 14
13

You can easily achieve this using a global event bus.

https://alligator.io/vuejs/global-event-bus/

For larger, more complex applications I would recommend using a state management tool such as vuex.

https://vuex.vuejs.org/en/

7

There are several ways of doing it, and some are mentioned in other answers,
(in no particular order, Read the detailed sections below for more info):

  1. Use a global eventbus (not recommended)
  2. Use props on components
  3. Use v-model attribute
  4. Use the sync modifier
  5. Use Pinia

Here are some details to the methods that are available:

1.) Use a global eventbus

I would strongly advise against using this approach for any kind of common communication between components, as has been discussed in many places like here

2.) Use props on components

Using props for two-way binding is not usually advised but possible, by passing an object or array you can change a property of that object and it will be observed in both child and parent without Vue printing a warning in the console.

Every time the parent component is updated, all props in the child component will be refreshed with the latest value. This means you should not attempt to mutate a prop inside a child component

Props are easy to use and are the ideal way to solve most common problems.
Because of how Vue observes changes all properties need to be available on an object or they will not be reactive. If any properties are added after Vue has finished making them observable 'set' will have to be used.

 //Normal usage
 Vue.set(aVariable, 'aNewProp', 42);
 //This is how to use it in Nuxt
 this.$set(this.historyEntry, 'date', new Date());

The object will be reactive for both component and the parent:

I you pass an object/array as a prop, it's two-way syncing automatically - change data in the child, it is changed in the parent.

If you pass simple values (strings, numbers) via props, you have to explicitly use the .sync modifier

As quoted from --> https://stackoverflow.com/a/35723888/1087372

3.) Use v-model attribute

The v-model attribute is syntactic sugar that enables easy two-way binding between parent and child. It does the same thing as the sync modifier does only it uses a specific prop and a specific event for the binding

This:

 <input v-model="searchText">

is the same as this:

 <input
   v-bind:value="searchText"
   v-on:input="searchText = $event.target.value"
 >

Where the prop must be value and the event must be input

4.) Use the sync modifier

The sync modifier is also syntactic sugar and does the same as v-model, just that the prop and event names are set by whatever is being used.

In the parent it can be used as follows:

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

From the child an event can be emitted to notify the parent of any changes:

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

5.) Use Pinia (or Vuex)

As of now Pinia is the official recommended state manager/data store

Pinia is a store library for Vue, it allows you to share a state across components/pages.

By using the Pinia store it is easier to see the flow of data mutations and they are explicitly defined. By using the vue developer tools it is easy to debug and rollback changes that were made.

This approach needs a bit more boilerplate, but if used throughout a project it becomes a much cleaner way to define how changes are made and from where.

Take a look at their getting started section


**In case of legacy projects** :

If your project already uses Vuex, you can keep on using it.

Vuex 3 and 4 will still be maintained. However, it's unlikely to add new functionalities to it. Vuex and Pinia can be installed in the same project. If you're migrating existing Vuex app to Pinia, it might be a suitable option. However, if you're planning to start a new project, we highly recommend using Pinia instead.

tony19
  • 125,647
  • 18
  • 229
  • 307
SanBen
  • 2,581
  • 26
  • 35
6

You need to implement a system that send the v-model back to the parent.

This can be done by using a computed property inside component-two that emits the change inside its set method.

Example:

Vue.component('component-two', {
  name: 'component-two',
  template: '#component-two-template',
  props: {
    value: {
      required: true,
      type: String,
    },
  },
  computed: {
    message: {
      get() {
        return this.value;
      },
      set(val) {
        this.$emit('input', val);
      },
    },
  },
});

var app = new Vue({
  el: '#app',
  data: {
    message1: 'm1',
    message2: 'm2',
  },
});
<script src="https://unpkg.com/vue@2.0.1/dist/vue.js"></script>
<script type="text/x-template" id="component-two-template">
  <input type="text" v-model="message"/>
</script>
<div id="app">
  <input type="text" v-model="message1"/>
  <component-two v-model="message2"></component-two>
  <p>Output</p>
  <pre>{{message1}}</pre>
  <pre>{{message2}}</pre>
</div>
Ferrybig
  • 18,194
  • 6
  • 57
  • 79
3

You can use .sync Modifier

<template>
  <div>
    <input type="text" v-model="input_one">
    <component-two :secondValue.sync="input_two"></component-two>
  </div>
</template>

<script>
  import ComponentTwo from 'component-two.vue'

  export default {
    name: "component-one",
    components: {
      ComponentTwo
    },
    data() {
      return {
        input_one: 'Hello from ComponentOne',
        input_two: ''
      }
    }
  }
</script>

component-two.vue:

<template>
  <div>
    <input type="text" v-model="input_two">
  </div>
</template>

<script>
  export default {
    name: "component-two",
    data() {
      return {
        input_one: 'Hello from ComponentTwo',
        input_two: ''
      },
      watch: {
        input_two : function(val){
          this.$emit('update:secondValue', val)
        }
      }
    }
  }
</script>
tony19
  • 125,647
  • 18
  • 229
  • 307
El Danielo
  • 780
  • 1
  • 8
  • 18