vuejs actualiza datos primarios del componente secundario


155

Estoy empezando a jugar con vuejs (2.0). Construí una página simple con un componente en ella. La página tiene una instancia de Vue con datos. En esa página me registré y agregué el componente a html. El componente tiene uno input[type=text]. Quiero que ese valor se refleje en el padre (instancia principal de Vue).

¿Cómo actualizo correctamente los datos principales del componente? Pasar un accesorio encuadernado del padre no es bueno y arroja algunas advertencias a la consola. Tienen algo en su documento pero no funciona.


1
¿Puedes agregar el código que has probado, que no funciona?
Saurabh

Respuestas:


181

El enlace bidireccional ha quedado en desuso en Vue 2.0 a favor del uso de una arquitectura más controlada por eventos. En general, un niño no debe mutar sus accesorios. Por el contrario, debe $emiteventos y dejar que el padre responda a esos eventos.

En su caso específico, puede usar un componente personalizado con v-model. Esta es una sintaxis especial que permite algo cercano al enlace bidireccional, pero en realidad es una abreviatura de la arquitectura controlada por eventos descrita anteriormente. Puede leer sobre esto aquí -> https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events .

Aquí hay un ejemplo simple:

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>


Los documentos afirman que

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

es equivalente a

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

Es por eso que el accesorio en el niño necesita ser nombrado valor, y por qué el niño necesita $ emitir un evento llamado input.


Primero gracias por la respuesta. ¿puede ampliar o señalar mejor los documentos sobre el evento 'input'? Parece un evento incorporado.
Gal Ziv

He agregado una aclaración e hice que el enlace a la documentación sea más obvio.
asemahle

1
Omití el "valor" de prop para el componente y la función creada y todavía funciona. ¿Puedes explicar por qué lo usaste?
xetra11

1
Si no agrega el accesorio, será undefinedhasta el primer cambio. Mira este violín donde he comentado props: ['value']. Observe cómo es el valor inicial undefined, en lugar de hello: jsfiddle.net/asemahle/8Lrkfxj6 . Después del primer cambio, Vue agrega dinámicamente un valor de apoyo al componente, por lo que funciona.
asemahle

Leí más allá de esto en los documentos. Gran ejemplo de trabajo. ¡+10 si pudiera!
arcilla

121

De la documentación :

En Vue.js, la relación del componente padre-hijo se puede resumir como accesorios, eventos arriba. El padre pasa los datos al niño a través de accesorios, y el niño envía mensajes al padre a través de eventos. Veamos cómo funcionan a continuación.

ingrese la descripción de la imagen aquí

Cómo pasar accesorios

El siguiente es el código para pasar accesorios a un elemento hijo:

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

Cómo 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
    }
  }
})

55
¿Qué pasa si la función 'incremento' estaba en el componente principal y quería activarla desde el componente secundario?
Hamzaouiii

el momento cortante de captar el concepto, aunque lo he estado usando varias veces por hacky copy paste ...
Yordan Georgiev

1
Imprimiré ese gráfico y lo pegaré en mi cabeza. ¡Gracias!
domih

1
En este ejemplo, ¿no deberíamos tener un detector de eventos definido en el componente raíz? Algo así como: `` `Montado () {this.on ('incremento', () => {this.incrementTotal ();}); } `` `
jmk2142

94

En componente hijo:

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

En el componente principal:

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

methods: {
    updateparent(variable) {
        this.parentvariable = variable
    }
}

3
Mi mente simplemente está impresionada por este ejemplo. No tienes idea de la cantidad de tutoriales que pasé antes de llegar aquí ...
suchislife

18

Componente hijo

Se usa this.$emit('event_name')para enviar un evento al componente principal.

ingrese la descripción de la imagen aquí

Componente principal

Para escuchar ese evento en el componente padre, hacemos v-on:event_namey se ex. handleChangeproduce un método ( ) que queremos ejecutar en ese evento

ingrese la descripción de la imagen aquí

Hecho :)


13

Estoy de acuerdo con la emisión de eventos y las respuestas del modelo v para los anteriores. Sin embargo, pensé en publicar lo que encontré sobre los componentes con múltiples elementos de formulario que desean emitir a sus padres, ya que este parece ser uno de los primeros artículos devueltos por Google.

Sé que la pregunta especifica una sola entrada, pero esta parecía la coincidencia más cercana y podría ahorrarle tiempo a las personas con componentes vue similares. Además, nadie ha mencionado el .syncmodificador todavía.

Hasta donde yo sé, la v-modelsolución solo es adecuada para una entrada que regresa a su padre. Me tomó un poco de tiempo buscarlo, pero la documentación de Vue (2.3.0) muestra cómo sincronizar múltiples accesorios enviados en el componente de regreso al padre (por medio de la emisión, por supuesto).

Se llama apropiadamente el .syncmodificador.

Esto es lo que dice la documentación :

En algunos casos, es posible que necesitemos "enlace bidireccional" para un accesorio. Desafortunadamente, el enlace bidireccional verdadero puede crear problemas de mantenimiento, porque los componentes secundarios pueden mutar al padre sin que la fuente de esa mutación sea obvia tanto en el padre como en el niño.

Es por eso que, en cambio, recomendamos emitir eventos en el patrón de update:myPropName. Por ejemplo, en un componente hipotético con un titleaccesorio, podríamos comunicar la intención de asignar un nuevo valor con:

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

Luego, el padre puede escuchar ese evento y actualizar una propiedad de datos local, si así lo desea. Por ejemplo:

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

Para mayor comodidad, ofrecemos una abreviatura para este patrón con el modificador .sync:

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

También puede sincronizar múltiples a la vez enviando a través de un objeto. Mira la documentación aquí


Esto es lo que estaba buscando. Muchas gracias.
Thomas

Esta es la mejor y más actualizada solución a partir de 2020. ¡Muchas gracias!
Marcelo

6

La forma más simple es usar this.$emit

Padre 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>

Mi ejemplo completo: https://codesandbox.io/s/update-parent-property-ufj4b


5

También es posible pasar accesorios como Object o Array. En este caso, los datos se vincularán en dos sentidos:

(Esto se observa al final del tema: 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>



0

1) Componente secundario: puede usar así en el componente secundario escribir así: this.formValue es enviar algunos datos al componente primario

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

2) Compnenet parental: y en la etiqueta del componente parental, se recibe una variable de envío como esta: y este es el código para recibir los datos de los componentes secundarios en la etiqueta del componente primario

@send="newformValue"

0

Otra forma es pasar una referencia de tu setter del padre como un accesorio para el componente hijo, de forma similar a como lo hacen en React. Por ejemplo, que tiene un método updateValueen el padre para actualizar el valor, se puede crear una instancia del componente secundario de este modo: <child :updateValue="updateValue"></child>. Luego, el niño tendrá un apoyo correspondiente: props: {updateValue: Function}y en la plantilla de llamar al método cuando cambia la entrada: <input @input="updateValue($event.target.value)">.


0

No sé por qué, pero acabo de actualizar con éxito los datos primarios con el uso de datos 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;

            },
        }

0

Este ejemplo le dirá cómo pasar el valor de entrada al padre en el botón Enviar.

Primero defina eventBus como nuevo 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
    });
}

0

Creo que esto hará el truco:

@change="$emit(variable)"

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.