retrograde
retrograde

Reputation: 2979

Passing data to components in vue.js

I'm struggling to understand how to pass data between components in vue.js. I have read through the docs several times and looked at many vue related questions and tutorials, but I'm still not getting it.

To wrap my head around this, I am hoping for help completing a pretty simple example

  1. display a list of users in one component (done)
  2. send the user data to a new component when a link is clicked (done) - see update at bottom.
  3. edit user data and send it back to original component (haven't gotten this far)

Here is a fiddle, which fails on step two: https://jsfiddle.net/retrogradeMT/d1a8hps0/

I understand that I need to use props to pass data to the new component, but I'm not sure how to functionally do it. How do I bind the data to the new component?

HTML:

    <div id="page-content">
       <router-view></router-view>
     </div>

 <template id="userBlock" >
   <ul>
     <li v-for="user in users">{{user.name}} - <a v-link="{ path: '/new' }"> Show new component</a>
     </li>
   </ul>
 </template>

  <template id="newtemp" :name ="{{user.name}}">
    <form>
      <label>Name: </label><input v-model="name">
      <input type="submit" value="Submit">
    </form>
  </template>

js for main component:

Vue.component('app-page', {
  template: '#userBlock',

  data: function() {
    return{

        users: []
      }
    },

ready: function () {
    this.fetchUsers();
},

methods: {
    fetchUsers: function(){
        var users = [
          {
            id: 1,
            name: 'tom'
          },
          {
            id: 2,
            name: 'brian'
          },
          {
            id: 3,
            name: 'sam'
          },
        ];

        this.$set('users', users);
     }
    }
  })

JS for second component:

Vue.component('newtemp', {
  template: '#newtemp',
  props: 'name',
  data: function() {
    return {
        name: name,
        }
   },
})

UPDATE

Ok, I've got the second step figured out. Here is a new fiddle showing the progress: https://jsfiddle.net/retrogradeMT/9pffnmjp/

Because I'm using Vue-router, I don't use props to send the data to a new component. Instead, I need set params on the v-link and then use a transition hook to accept it.

V-link changes see named routes in vue-router docs:

<a v-link="{ name: 'new', params: { name: user.name }}"> Show new component</a>

Then on the component, add data to the route options see transition hooks:

Vue.component('newtemp', {
  template: '#newtemp',
  route: {
   data: function(transition) {
        transition.next({
            // saving the id which is passed in url
            name: transition.to.params.name
        });
     }
  },
 data: function() {
    return {
        name:name,
        }
   },
})

Upvotes: 111

Views: 222344

Answers (7)

acdcjunior
acdcjunior

Reputation: 135752

The best way to send data from a parent component to a child is using props.

Passing data from parent to child via props

  • Declare props (array or object) in the child
  • Pass it to the child via <child :name="variableOnParent">

See demo below:

Vue.component('child-comp', {
  props: ['message'], // declare the props
  template: '<p>At child-comp, using props in the template: {{ message }}</p>',
  mounted: function () {
    console.log('The props are also available in JS:', this.message);
  }
})

new Vue({
  el: '#app',
  data: {
    variableAtParent: 'DATA FROM PARENT!'
  }
})
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>

<div id="app">
  <p>At Parent: {{ variableAtParent }}<br>And is reactive (edit it) <input v-model="variableAtParent"></p>
  <child-comp :message="variableAtParent"></child-comp>
</div>

Upvotes: 30

Divesh Sahu
Divesh Sahu

Reputation: 41

The above-mentioned responses work well but if you want to pass data between 2 sibling components, then the event bus can also be used. Check out this blog which would help you understand better.

supppose for 2 components : CompA & CompB having same parent and main.js for setting up main vue app. For passing data from CompA to CompB without involving parent component you can do the following.

in main.js file, declare a separate global Vue instance, that will be event bus.

export const bus = new Vue();

In CompA, where the event is generated : you have to emit the event to bus.

methods: {
      somethingHappened (){
          bus.$emit('changedSomething', 'new data');
      }
  }

Now the task is to listen the emitted event, so, in CompB, you can listen like.

created (){
    bus.$on('changedSomething', (newData) => {
      console.log(newData);
    })
  }

Advantages:

  • Less & Clean code.
  • Parent should not involve in passing down data from 1 child comp to another ( as the number of children grows, it will become hard to maintain )
  • Follows pub-sub approach.

Upvotes: 4

T.Todua
T.Todua

Reputation: 56341

I access main properties using $root.

Vue.component("example", { 
    template: `<div>$root.message</div>`
});
...
<example></example>

Upvotes: 1

notANerdDev
notANerdDev

Reputation: 1286

-------------Following is applicable only to Vue 1 --------------

Passing data can be done in multiple ways. The method depends on the type of use.


If you want to pass data from your html while you add a new component. That is done using props.

<my-component prop-name="value"></my-component>

This prop value will be available to your component only if you add the prop name prop-name to your props attribute.


When data is passed from a component to another component because of some dynamic or static event. That is done by using event dispatchers and broadcasters. So for example if you have a component structure like this:

<my-parent>
    <my-child-A></my-child-A>
    <my-child-B></my-child-B>
</my-parent>

And you want to send data from <my-child-A> to <my-child-B> then in <my-child-A> you will have to dispatch an event:

this.$dispatch('event_name', data);

This event will travel all the way up the parent chain. And from whichever parent you have a branch toward <my-child-B> you broadcast the event along with the data. So in the parent:

events:{
    'event_name' : function(data){
        this.$broadcast('event_name', data);
    },

Now this broadcast will travel down the child chain. And at whichever child you want to grab the event, in our case <my-child-B> we will add another event:

events: {
    'event_name' : function(data){
        // Your code. 
    },
},

The third way to pass data is through parameters in v-links. This method is used when components chains are completely destroyed or in cases when the URI changes. And i can see you already understand them.


Decide what type of data communication you want, and choose appropriately.

Upvotes: 52

Lucas Serena
Lucas Serena

Reputation: 21

I've found a way to pass parent data to component scope in Vue, i think it's a little a bit of a hack but maybe this will help you.

1) Reference data in Vue Instance as an external object (data : dataObj)

2) Then in the data return function in the child component just return parentScope = dataObj and voila. Now you cann do things like {{ parentScope.prop }} and will work like a charm.

Good Luck!

Upvotes: 2

Kjelle J
Kjelle J

Reputation: 197

A global JS variable (object) can be used to pass data between components. Example: Passing data from Ammlogin.vue to Options.vue. In Ammlogin.vue rspData is set to the response from the server. In Options.vue the response from the server is made available via rspData.

index.html:

<script>
    var rspData; // global - transfer data between components
</script>

Ammlogin.vue:

....    
export default {
data: function() {return vueData}, 
methods: {
    login: function(event){
        event.preventDefault(); // otherwise the page is submitted...
        vueData.errortxt = "";
        axios.post('http://vueamm...../actions.php', { action: this.$data.action, user: this.$data.user, password: this.$data.password})
            .then(function (response) {
                vueData.user = '';
                vueData.password = '';
                // activate v-link via JS click...
                // JSON.parse is not needed because it is already an object
                if (response.data.result === "ok") {
                    rspData = response.data; // set global rspData
                    document.getElementById("loginid").click();
                } else {
                    vueData.errortxt = "Felaktig avändare eller lösenord!"
                }
            })
            .catch(function (error) {
                // Wu oh! Something went wrong
                vueData.errortxt = error.message;
            });
    },
....

Options.vue:

<template>
<main-layout>
  <p>Alternativ</p>
  <p>Resultat: {{rspData.result}}</p>
  <p>Meddelande: {{rspData.data}}</p>
  <v-link href='/'>Logga ut</v-link>
</main-layout>
</template>
<script>
 import MainLayout from '../layouts/Main.vue'
 import VLink from '../components/VLink.vue'
 var optData = { rspData: rspData}; // rspData is global
 export default {
   data: function() {return optData},
   components: {
     MainLayout,
     VLink
   }
 }
</script>

Upvotes: -2

Jeff
Jeff

Reputation: 25211

I think the issue is here:

<template id="newtemp" :name ="{{user.name}}">

When you prefix the prop with : you are indicating to Vue that it is a variable, not a string. So you don't need the {{}} around user.name. Try:

<template id="newtemp" :name ="user.name">

EDIT-----

The above is true, but the bigger issue here is that when you change the URL and go to a new route, the original component disappears. In order to have the second component edit the parent data, the second component would need to be a child component of the first one, or just a part of the same component.

Upvotes: 18

Related Questions