vuejs atualiza dados pai do componente filho

154

Estou começando a jogar com o vuejs (2.0). Criei uma página simples com um componente. A página possui uma instância do Vue com dados. Nessa página, registrei e adicionei o componente ao html. O componente tem um input[type=text]. Quero que esse valor reflita no pai (instância principal do Vue).

Como atualizo corretamente os dados pai do componente? Passar um suporte vinculado do pai não é bom e lança alguns avisos para o console. Eles têm algo no documento, mas não está funcionando.

Gal Ziv
fonte
1
Você pode adicionar o código que tentou, que não está funcionando.
Saurabh

Respostas:

181

A ligação bidirecional foi descontinuada no Vue 2.0 em favor do uso de uma arquitetura mais orientada a eventos. Em geral, uma criança não deve alterar seus adereços. Em vez disso, deve $emitocorrer eventos e deixar os pais responderem a esses eventos.

No seu caso específico, você pode usar um componente personalizado v-model. Essa é uma sintaxe especial que permite algo próximo à ligação bidirecional, mas na verdade é uma abreviação para a arquitetura orientada a eventos descrita acima. Você pode ler sobre isso aqui -> https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events .

Aqui está um exemplo simples:

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>


Os documentos afirmam que

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

é equivalente a

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

É por isso que o suporte à criança precisa ser chamado de valor e por que a criança precisa emitir um evento chamado input.

asemahle
fonte
primeiro obrigado pela resposta. você pode expandir, ou melhor, apontar para documentos sobre o evento 'input'? parece um evento incorporado.
Gal Ziv
Eu adicionei um esclarecimento e tornei o link para a documentação mais óbvio.
asemahle
1
Omiti o prop "value" para o componente e a função criada e ele ainda funciona. você pode explicar por que você usou?
precisa saber é
1
Se você não adicionar o suporte, será undefinedaté a primeira alteração. Veja este violino onde comentei props: ['value']. Observe como o valor inicial é undefined, em vez de hello: jsfiddle.net/asemahle/8Lrkfxj6 . Após a primeira alteração, o Vue adiciona dinamicamente um valor prop ao componente, para que funcione.
asemahle
Eu li isso passado nos documentos. Ótimo exemplo de trabalho. +10 se eu pudesse!
argila
121

A partir da documentação :

No Vue.js, o relacionamento do componente pai-filho pode ser resumido como props down, eventos up. O pai transmite dados para o filho via adereços e o filho envia mensagens para o pai via eventos. Vamos ver como eles funcionam a seguir.

insira a descrição da imagem aqui

Como passar adereços

A seguir está o código para passar adereços para um elemento filho:

<div>
  <input v-model="parentMsg">
  <br>
  <child v-bind:my-message="parentMsg"></child>
</div>

Como emitir evento

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
    }
  }
})
Saurabh
fonte
5
e se a função "incremento" estivesse no componente pai e eu quisesse acioná-lo a partir do componente filho?
Hamzaouiii
o momento de cisalhamento de apreender o conceito, embora tenham sido usá-lo várias vezes por copiar colar hacky ...
Yordan Georgiev
1
Vou imprimir esse gráfico e colocá-lo na minha cabeça. THX!
22720 Dom Dom
1
Neste exemplo, não devemos ter um ouvinte de evento definido no componente raiz? Algo como: `` `mounted () {this.on ('increment', () => {this.incrementTotal ();}); } `` `
jmk2142
94

No componente filho:

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

No componente pai:

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

methods: {
    updateparent(variable) {
        this.parentvariable = variable
    }
}
Sarvar Nishonboev
fonte
3
Minha mente está simplesmente impressionada com este exemplo. Você não tem idéia de quantos tutoriais eu passei antes de chegar aqui ...
suchislife 9/09/19
18

Componente filho

Use this.$emit('event_name')para enviar um evento para o componente pai.

insira a descrição da imagem aqui

Componente pai

Para escutar esse evento no componente pai, fazemos v-on:event_namee um método ( ex. handleChange) que queremos executar nesse evento ocorre

insira a descrição da imagem aqui

Feito :)

EsterlingAccime Youtuber
fonte
13

Eu concordo com as respostas de emissão de eventos e modelo v para as pessoas acima. No entanto, pensei em publicar o que encontrei sobre componentes com vários elementos de formulário que desejam emitir de volta para seus pais, pois esse parece ser um dos primeiros artigos retornados pelo google.

Sei que a pergunta especifica uma única entrada, mas essa parece a correspondência mais próxima e pode economizar tempo para as pessoas com componentes similares do vue. Além disso, ninguém mencionou o .syncmodificador ainda.

Tanto quanto sei, a v-modelsolução é adequada apenas para uma entrada retornando ao pai. Demorei um pouco para procurá-lo, mas a documentação do Vue (2.3.0) mostra como sincronizar vários adereços enviados ao componente de volta ao pai (via emissão, é claro).

É chamado apropriadamente de .syncmodificador.

Aqui está o que a documentação diz:

Em alguns casos, podemos precisar de uma ligação bidirecional para um suporte. Infelizmente, a ligação bidirecional verdadeira pode criar problemas de manutenção, porque os componentes filhos podem alterar o pai, sem que a fonte dessa mutação seja óbvia no pai e no filho.

Por isso, recomendamos a emissão de eventos no padrão de update:myPropName. Por exemplo, em um componente hipotético com um titlesuporte, poderíamos comunicar a intenção de atribuir um novo valor com:

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

Em seguida, o pai pode ouvir esse evento e atualizar uma propriedade de dados local, se desejar. Por exemplo:

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

Por conveniência, oferecemos uma abreviação para esse padrão com o modificador .sync:

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

Você também pode sincronizar vários de cada vez enviando um objeto. Confira a documentação aqui

Archernar
fonte
Era isso que eu estava procurando. Muito obrigado.
Thomas
Esta é a melhor e mais atualizada solução a partir de 2020. Muito obrigado!
Marcelo
6

A maneira mais simples é usar 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>

Meu exemplo completo: https://codesandbox.io/s/update-parent-property-ufj4b

Darlan Dieterich
fonte
5

Também é possível passar adereços como Objeto ou Matriz. Nesse caso, os dados serão vinculados nos dois sentidos:

(Isso é observado no final do tópico: https://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>

Perlovka
fonte
1

A maneira correta é para $emit()um evento no componente filho que a instância principal do Vue escuta .

// 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
    }
  }
});
vkarpov15
fonte
0

1) Componente filho: você pode usar assim no componente filho, escrever assim: this.formValue é enviar alguns dados para o componente pai

this.$emit('send',this.formValue)

2) Parrent Compnenet: e no tag do componente parrent, receba variável de envio como esta: e este é o código para recuperar os dados do componente filho no tag do componente pai

@send="newformValue"
zia muhammad
fonte
0

Outra maneira é passar uma referência do seu setter do pai como um suporte para o componente filho, semelhante à maneira como eles fazem isso no React. Diga, você tem um método updateValueno pai para atualizar o valor, você pode instanciar o componente filho assim: <child :updateValue="updateValue"></child>. Em seguida, sobre a criança terá um suporte correspondente: props: {updateValue: Function}e no modelo de chamar o método quando a entrada muda: <input @input="updateValue($event.target.value)">.

The7thMonarch
fonte
0

Não sei por que, mas atualizei com êxito os dados pai com o uso de dados como objeto, :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;

            },
        }
DWIAN BIGI Agipa
fonte
0

O exemplo dele mostrará como passar o valor de entrada para o pai no botão enviar.

Primeiro defina eventBus como novo 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
    });
}
Manish Shukla
fonte
0

Eu acho que isso vai fazer o truque:

@change="$emit(variable)"

kaleazy
fonte