Passando dados para componentes em vue.js

94

Estou lutando para entender como transmitir dados entre componentes em vue.js. Eu li a documentação várias vezes e olhei muitas perguntas e tutoriais relacionados, mas ainda não estou entendendo.

Para entender isso, espero ajuda para completar um exemplo bem simples

  1. exibir uma lista de usuários em um componente (pronto)
  2. enviar os dados do usuário para um novo componente quando um link for clicado (concluído) - consulte a atualização na parte inferior.
  3. edite os dados do usuário e envie-os de volta ao componente original (não cheguei até aqui)

Aqui está um violino, que falha na etapa dois: https://jsfiddle.net/retrogradeMT/d1a8hps0/

Eu entendo que preciso usar adereços para passar dados para o novo componente, mas não tenho certeza de como fazer isso funcionalmente. Como vinculo os dados ao novo componente?

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 para o componente principal:

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 para o segundo componente:

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

ATUALIZAR

Ok, eu descobri a segunda etapa. Aqui está um novo violino mostrando o progresso: https://jsfiddle.net/retrogradeMT/9pffnmjp/

Como estou usando o Vue-router, não uso props para enviar os dados para um novo componente. Em vez disso, preciso definir parâmetros no v-link e usar um gancho de transição para aceitá-lo.

As alterações do V-link, consulte as rotas nomeadas nos documentos do vue-router :

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

Em seguida, no componente, adicione dados às opções de rota, consulte os ganchos de transição :

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,
        }
   },
})
retrógrado
fonte

Respostas:

48

------------- O que se segue é aplicável apenas ao Vue 1 --------------

A transmissão de dados pode ser feita de várias maneiras. O método depende do tipo de uso.


Se você deseja passar dados do seu html enquanto adiciona um novo componente. Isso é feito usando adereços.

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

Este valor de prop estará disponível para seu componente somente se você adicionar o nome de prop prop-nameao seu propsatributo.


Quando os dados são passados ​​de um componente para outro componente devido a algum evento dinâmico ou estático. Isso é feito usando distribuidores e distribuidores de eventos. Por exemplo, se você tiver uma estrutura de componentes como esta:

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

E você deseja enviar dados de <my-child-A>para <my-child-B>então em <my-child-A>você terá que despachar um evento:

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

Este evento percorrerá todo o caminho até a cadeia principal. E de qualquer pai que você tenha uma filial para <my-child-B>transmitir o evento junto com os dados. Então, no pai:

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

Agora, essa transmissão percorrerá a cadeia infantil. E para qualquer criança que você queira pegar no evento, no nosso caso <my-child-B>vamos adicionar outro evento:

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

A terceira maneira de passar dados é por meio de parâmetros em v-links. Este método é usado quando as cadeias de componentes são completamente destruídas ou nos casos em que o URI muda. E posso ver que você já os entende.


Decida que tipo de comunicação de dados você deseja e escolha apropriadamente.

notANerdDev
fonte
1
Obrigado, isso foi realmente útil. A única peça com a qual ainda estou lutando são as convenções de nomenclatura. ou seja: tenho user.name que passo via v-link como nome. Agora posso editar a variável de nome e passá-la de volta ao componente original como nome, mas o componente original não tem uma var chamada nome. O que estou perdendo aqui? Não parece possível passar um objeto por meio de v-link?
retrógrado em
1
Você já está passando um objeto. paramsé um objeto. A chave do nome pode ser atribuída this.user.nameem seu componente. Crie um violino se precisar de ajuda e me avise. Verifique isso: vuejs.github.io/vue-router/en/route.html
notANerdDev
Ah, ok, entendi agora. Obrigado novamente. Agradeço sua ajuda, realmente deixou as coisas muito mais claras.
retrógrado de
6
Resposta incrível, mas pelo que entendi não funcionará com o 2.0, pois os eventos foram descontinuados?
rix
2
Adorei a resposta, mas depois cheguei aos comentários e, como estou trabalhando com o Vue 2, sinto que não vou conseguir o segundo método sugerido por você. Isso é temido corretamente?
Konrad Viltersten
24

A melhor maneira de enviar dados de um componente pai para um filho é usando adereços .

Passando dados de pais para filhos via props

  • Declare props(matriz ou objeto ) no filho
  • Passe para a criança via <child :name="variableOnParent">

Veja a demonstração abaixo:

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>

acdcjunior
fonte
pequena correção: "<child-comp" em vez de "<child" e variableAtParent .. Passe para a criança via <child-comp: name = "variableAtParent">
muenalan
Só funcionará com propriedade simples, as referências do objeto não serão atualizadas. Por exemplo, se você tiver a propriedade pai ser array, e esse array foi substituído no objeto pai por outro array, o filho não o verá.
Stan Sokolov
@StanSokolov de forma alguma. Eu trabalho para arrays, consulte: jsfiddle.net/acdcjunior/q4mank05 . Se o array não for reativo, provavelmente você está enfrentando um problema de reatividade. Tente usar Vue.set, mais aqui: vuejs.org/v2/guide/reactivity.html#For-Arrays
acdcjunior
De que adianta se não for reativo? Claro que era reativo com referência constantemente substituída por um novo array. O Vue não propaga "alterações de referência" para os filhos, apenas alterações de valor em elementos simples. Portanto, se a referência à matriz foi substituída no pai, os filhos ainda verão a referência antiga. Nunca saberei que o pai agora trabalha com um objeto diferente.
Stan Sokolov
@StanSokolov Veja isto: jsfiddle.net/acdcjunior/ftmrne2h o array é substituído em pai e a criança vê / reage a ele (clique no botão para testar). Ou você quer dizer outra coisa?
Nesse
15

Acho que o problema está aqui:

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

Quando você prefixa o prop com, :está indicando ao Vue que é uma variável, não uma string. Então você não precisa da {{}}volta user.name. Experimentar:

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

EDITAR-----

O que foi dito acima é verdade, mas o maior problema aqui é que quando você altera a URL e vai para uma nova rota, o componente original desaparece. Para que o segundo componente edite os dados pai, o segundo componente precisará ser um componente filho do primeiro ou apenas uma parte do mesmo componente.

Jeff
fonte
Obrigado jeff. Tentei remover o {{}}, mas continuo recebendo os mesmos erros.
retrógrado
2

Eu encontrei uma maneira de passar dados pai para o escopo do componente no Vue, eu acho que é um pouco um hack, mas talvez isso ajude você.

1) Dados de referência na instância Vue como um objeto externo (data : dataObj)

2) Então, na função de retorno de dados no componente filho, apenas retorne parentScope = dataObje voila. Agora você não pode fazer coisas como {{ parentScope.prop }}e vai funcionar como um encanto.

Boa sorte!

Lucas serena
fonte
Isso é o acoplamento de ambos os componentes, o que pode ser bom para uma árvore e seus nós, mas normalmente não é o preferido. Eu tentaria evitar isso.
dezembro
1

As respostas mencionadas acima funcionam bem, mas se você quiser passar dados entre 2 componentes irmãos, o barramento de eventos também pode ser usado. Confira este blog que o ajudará a entender melhor.

suponha para 2 componentes: CompA e CompB tendo o mesmo pai e main.js para configurar o aplicativo principal. Para passar dados de CompA para CompB sem envolver o componente pai, você pode fazer o seguinte.

no arquivo main.js, declare uma instância global separada do Vue, que será o barramento de eventos.

export const bus = new Vue();

No CompA, onde o evento é gerado: você deve emitir o evento para o barramento.

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

Agora a tarefa é ouvir o evento emitido, então, no CompB, você pode ouvir como.

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

Vantagens:

  • Menos e código limpo.
  • Os pais não devem se envolver na transmissão de dados de um componente filho para outro (conforme o número de filhos aumenta, será difícil manter)
  • Segue a abordagem pub-sub.
Divesh Sahu
fonte
0

Eu acesso as propriedades principais usando $root.

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

Uma variável JS global (objeto) pode ser usada para passar dados entre componentes. Exemplo: Passando dados de Ammlogin.vue para Options.vue. Em Ammlogin.vue rspData é definido para a resposta do servidor. Em Options.vue, a resposta do servidor é disponibilizada 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>
Kjelle J
fonte