vue props mounted framework entre comunicacion componentes component javascript vuejs2 vue.js

javascript - props - VueJs 2.0 emite un evento del nieto a su componente abuelo



vue router (8)

¡Este es el único caso cuando uso el bus de eventos ! Para pasar datos de un niño anidado profundo, a no directamente padre, comunicación.

Primero : cree un archivo js (lo llamo eventbus.js) con este contenido:

import Vue from ''vue'' Vue.prototype.$event = new Vue()

Segundo : en su componente hijo, emita un evento:

this.$event.$emit(''event_name'', ''data to pass'')

Tercero : en el padre escuche ese evento:

this.$event.$on(''event_name'', (data) => { console.log(data) })

Nota: Si ya no desea ese evento, anule el registro:

this.$event.$off(''event_name'')

INFORMACIÓN: No es necesario leer la opinión personal a continuación.

No me gusta usar vuex para la comunicación entre nietos y abuelos (o un nivel de comunicación similar).

En vue.js para pasar datos de abuelo a nieto, puede usar provide/inject . Pero no hay algo similar para lo contrario. (nieto a abuelo) Así que uso el bus de eventos siempre que tengo que hacer ese tipo de comunicación.

Parece que Vue.js 2.0 no emite eventos de un nieto a su componente abuelo.

Vue.component(''parent'', { template: ''<div>I am the parent - {{ action }} <child @eventtriggered="performAction"></child></div>'', data(){ return { action: ''No action'' } }, methods: { performAction() { this.action = ''actionDone'' } } }) Vue.component(''child'', { template: ''<div>I am the child <grand-child></grand-child></div>'' }) Vue.component(''grand-child'', { template: ''<div>I am the grand-child <button @click="doEvent">Do Event</button></div>'', methods: { doEvent() { this.$emit(''eventtriggered'') } } }) new Vue({ el: ''#app'' })

Este JsFiddle resuelve el problema https://jsfiddle.net/y5dvkqbd/4/ , pero emitando dos eventos:

  • Uno de nieto a componente medio
  • Luego emitiendo nuevamente desde el componente medio hasta el padre principal

Agregar este evento intermedio parece repetitivo e innecesario. ¿Hay alguna forma de emitir directamente al abuelo que no conozco?


Agregando a la respuesta de BassMHL , si desea ser más flexible y simplemente transmitir un evento a todos los padres y sus padres de forma recursiva, puede hacer algo como:

let vm = this.$parent while(vm) { vm.$emit(''submit'') vm = vm.$parent }


La comunidad Vue generalmente favorece el uso de Vuex para resolver este tipo de problema. Se realizan cambios en el estado de Vuex y la representación DOM simplemente fluye de eso, eliminando la necesidad de eventos en muchos casos.

Salvo eso, la reemisión probablemente sea la siguiente mejor opción, y finalmente puede optar por usar un bus de eventos como se detalla en la otra respuesta altamente votada a esta pregunta.

La respuesta a continuación es mi respuesta original a esta pregunta y no es un enfoque que tomaría ahora, ya que tengo más experiencia con Vue.

Este es un caso en el que podría estar en desacuerdo con la elección de diseño de Vue y recurrir a DOM.

En grand-child ,

methods: { doEvent() { try { this.$el.dispatchEvent(new Event("eventtriggered")); } catch (e) { // handle IE not supporting Event constructor var evt = document.createEvent("Event"); evt.initEvent("eventtriggered", true, false); this.$el.dispatchEvent(evt); } } }

y en parent

mounted(){ this.$el.addEventListener("eventtriggered", () => this.performAction()) }

De lo contrario, sí, tiene que volver a emitir o usar un autobús.

Nota: agregué código en el método doEvent para manejar IE; ese código podría extraerse de forma reutilizable.


Otra solución será on / emit en el nodo raíz :

Utiliza vm.$root.$emit en nieto , luego usa vm.$root.$on En el antepasado (o en cualquier lugar que desee).

Actualizado : a veces le gustaría deshabilitar el escucha en algunas situaciones específicas, use vm.$off (por ejemplo: vm.$root.off(''event-name'') dentro de lifecycle hook = beforeDestroy ).

Vue.component(''parent'', { template: ''<div><button @click="toggleEventListener()">Listener is {{eventEnable ? "On" : "Off"}}</button>I am the parent - {{ action }} <child @eventtriggered="performAction"></child></div>'', data(){ return { action: 1, eventEnable: false } }, created: function () { this.addEventListener() }, beforeDestroy: function () { this.removeEventListener() }, methods: { performAction() { this.action += 1 }, toggleEventListener: function () { if (this.eventEnable) { this.removeEventListener() } else { this.addEventListener() } }, addEventListener: function () { this.$root.$on(''eventtriggered1'', () => { this.performAction() }) this.eventEnable = true }, removeEventListener: function () { this.$root.$off(''eventtriggered1'') this.eventEnable = false } } }) Vue.component(''child'', { template: ''<div>I am the child <grand-child @eventtriggered="doEvent"></grand-child></div>'', methods: { doEvent() { //this.$emit(''eventtriggered'') } } }) Vue.component(''grand-child'', { template: ''<div>I am the grand-child <button @click="doEvent">Emit Event</button></div>'', methods: { doEvent() { this.$root.$emit(''eventtriggered1'') } } }) new Vue({ el: ''#app'' })

<script src="https://unpkg.com/vue/dist/vue.js"></script> <div id="app"> <parent></parent> </div>


Realmente me gusta la forma en que se maneja esto creando una clase que está vinculada a la ventana y simplificando la configuración de transmisión / escucha para que funcione en cualquier lugar de la aplicación Vue.

window.Event = new class { constructor() { this.vue = new Vue(); } fire(event, data = null) { this.vue.$emit(event, data); } listen() { this.vue.$on(event, callback); } }

Ahora puede disparar / transmitir / lo que sea desde cualquier lugar llamando:

Event.fire(''do-the-thing'');

... y puedes escuchar en un padre, abuelo, lo que quieras llamando:

Event.listen(''do-the-thing'', () => { alert(''Doing the thing!''); });


Sí, los eventos correctos solo van de niño a padre. No van más allá, por ejemplo, de niño a abuelo.

La documentación de Vue (brevemente) aborda esta situación en la sección de Comunicación no padre-hijo .

La idea general es que en el componente de abuelo se crea un componente Vue vacío que se pasa de abuelo a hijos y nietos a través de accesorios. El abuelo luego escucha los eventos y los nietos emiten eventos en ese "bus de eventos".

Algunas aplicaciones usan un bus de eventos global en lugar de un bus de eventos por componente. El uso de un bus de eventos global significa que necesitará tener nombres de eventos únicos o espacios de nombres para que los eventos no choquen entre los diferentes componentes.

Aquí hay un ejemplo de cómo implementar un bus de eventos global simple .


Vue 2.4 introdujo una forma de pasar fácilmente eventos por la jerarquía usando vm.$listeners

De https://vuejs.org/v2/api/#vm-listeners :

Contiene escuchas de eventos v-on ámbito primario (sin modificadores .native ). Esto se puede pasar a un componente interno a través de v-on="$listeners" , útil al crear componentes de envoltura transparentes.

Vea el fragmento a continuación usando v-on="$listeners" en el componente grand-child en la plantilla child :

Vue.component(''parent'', { template: ''<div><p>I am the parent. The value is {{displayValue}}.</p> <child @toggle-value="toggleValue"></child></div>'', data(){ return { value: false } }, methods: { toggleValue() { this.value = !this.value } }, computed: { displayValue(){ return (this.value ? "ON" : "OFF") } } }) Vue.component(''child'', { template: ''<div class="child"><p>I am the child. I/'m just a wrapper providing some UI.</p><grand-child v-on="$listeners"></grand-child></div>'' }) Vue.component(''grand-child'', { template: ''<div><p>I am the grand-child: <button @click="emitToggleEvent">Toggle the value</button></p></div>'', methods: { emitToggleEvent() { this.$emit(''toggle-value'') } } }) new Vue({ el: ''#app'' })

.child { padding: 10px; border: 1px solid #ddd; background: #f0f0f0 }

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script> <div id="app"> <parent></parent> </div>


NUEVA RESPUESTA (actualización de noviembre de 2018)

Descubrí que en realidad podríamos hacer esto aprovechando la propiedad $parent en el componente grand child:

this.$parent.$emit("submit", {somekey: somevalue})

Mucho más limpio y simple.