Es scheint, dass Vue.js 2.0 keine Ereignisse von einem Enkelkind an seine Großelternkomponente ausgibt.
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'
})
Diese JsFiddle löst das Problem https://jsfiddle.net/y5dvkqbd/4/ , indem jedoch zwei Ereignisse ausgelöst werden:
- Eine vom Enkelkind bis zur mittleren Komponente
- Dann wieder von der mittleren Komponente zum Großelternteil emittieren
Das Hinzufügen dieses mittleren Ereignisses scheint sich zu wiederholen und ist nicht erforderlich. Gibt es eine Möglichkeit, direkt an Großeltern zu senden, die mir nicht bekannt sind?
quelle
Die Vue-Community bevorzugt im Allgemeinen die Verwendung von Vuex, um diese Art von Problem zu lösen. Der Vuex-Status wird geändert, und die DOM-Darstellung ergibt sich daraus. In vielen Fällen sind keine Ereignisse erforderlich.
Wenn dies nicht der Fall ist , ist eine erneute Ausgabe wahrscheinlich die nächstbeste Wahl, und schließlich können Sie einen Ereignisbus verwenden, wie in der anderen hoch bewerteten Antwort auf diese Frage beschrieben.
Die Antwort unten ist meine ursprüngliche Antwort auf diese Frage und kein Ansatz, den ich jetzt verfolgen würde, da ich mehr Erfahrung mit Vue habe.
Dies ist ein Fall, in dem ich mit Vues Designentscheidung nicht einverstanden sein und auf DOM zurückgreifen könnte.
In
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); } } }
und in
parent
,mounted(){ this.$el.addEventListener("eventtriggered", () => this.performAction()) }
Andernfalls müssen Sie erneut senden oder einen Bus verwenden.
Hinweis: Ich habe der doEvent-Methode Code hinzugefügt, um mit dem IE umzugehen. Dieser Code könnte auf wiederverwendbare Weise extrahiert werden.
quelle
NEUE ANTWORT (Update November 2018)
Ich entdeckte, dass wir dies tatsächlich tun können, indem wir die
$parent
Eigenschaft in der Enkelkindkomponente nutzen:this.$parent.$emit("submit", {somekey: somevalue})
Viel sauberer und einfacher.
quelle
transition
oder eine andere Wrapper-Komponente und es wird zerbrechen und Sie haben ein großes Fragezeichen im Kopf.Ja, Sie sind richtig, Ereignisse gehen nur vom Kind zum Elternteil. Sie gehen nicht weiter, zB vom Kind zum Großelternteil.
Die Vue-Dokumentation (kurz) behandelt diese Situation im Abschnitt Nicht-Eltern-Kind-Kommunikation .
Die allgemeine Idee ist, dass Sie in der Großelternkomponente eine leere
Vue
Komponente erstellen , die vom Großelternteil über Requisiten an die Kinder und Enkelkinder weitergegeben wird. Der Großelternteil hört dann auf Ereignisse und Enkelkinder senden Ereignisse auf diesem "Ereignisbus" aus.Einige Anwendungen verwenden einen globalen Ereignisbus anstelle eines Ereignisbusses pro Komponente. Die Verwendung eines globalen Ereignisbusses bedeutet, dass Sie eindeutige Ereignisnamen oder Namespaces benötigen, damit Ereignisse nicht zwischen verschiedenen Komponenten zusammenstoßen.
Hier ist ein Beispiel für die Implementierung eines einfachen globalen Ereignisbusses .
quelle
Eine andere Lösung wird in ein / Emit sein root node:
Verwendet
vm.$root.$emit
im Enkelkind , dannvm.$root.$on
beim Vorfahren (oder wo immer Sie möchten).Aktualisiert : Manchmal möchten Sie den Listener in bestimmten Situationen deaktivieren. Verwenden Sie vm. $ Off (zum Beispiel:
vm.$root.off('event-name')
inside 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>
quelle
Wenn Sie flexibel sein und einfach ein Ereignis an alle Eltern und deren Eltern rekursiv bis zur Wurzel senden möchten, können Sie Folgendes tun:
let vm = this.$parent while(vm) { vm.$emit('submit') vm = vm.$parent }
quelle
Dies ist der einzige Fall , wenn ich Eventbus !! Für die Weitergabe von Daten von einem tief verschachtelten Kind an eine nicht direkt übergeordnete Kommunikation.
import Vue from 'vue' Vue.prototype.$event = new Vue()
this.$event.$emit('event_name', 'data to pass')
this.$event.$on('event_name', (data) => { console.log(data) })
Hinweis: Wenn Sie dieses Ereignis nicht mehr möchten, heben Sie bitte die Registrierung auf:
this.$event.$off('event_name')
Ich verwende Vuex nicht gerne für die Kommunikation zwischen Enkelkindern und Großeltern (oder einer ähnlichen Kommunikationsebene).
quelle
Ich habe ein kurzes Mixin basierend auf @digout Antwort gemacht. Sie möchten es vor der Initialisierung Ihrer Vue-Instanz (neues Vue ...) einfügen, um es global im Projekt zu verwenden. Sie können es ähnlich wie ein normales Ereignis verwenden.
Vue.mixin({ methods: { $propagatedEmit: function (event, payload) { let vm = this.$parent; while (vm) { vm.$emit(event, payload); vm = vm.$parent; } } } })
quelle
targetRef
, der die Weitergabe der Komponente, auf die Sie abzielen, stoppt. Diewhile
Bedingung würde dann beinhalten&& vm.$refs[targetRef]
- Sie müssten diesesref
Attribut auch in die Zielkomponente aufnehmen. In meinem Anwendungsfall musste ich nicht bis zur Wurzel tunneln, um ein paar Ereignisse vor dem Auslösen und vielleicht ein paar wertvolle Nanosekunden zu sparen timeVueJS 2-Komponenten haben eine
$parent
Eigenschaft, die ihre übergeordnete Komponente enthält.Diese übergeordnete Komponente enthält auch eine eigene
$parent
Eigenschaft.Wenn Sie dann auf die Komponente "Großeltern" zugreifen, müssen Sie auf die Komponente "Eltern des Elternteils" zugreifen:
this.$parent["$parent"].$emit("myevent", { data: 123 });
Wie auch immer, das ist ein bisschen schwierig , und ich empfehle, einen globalen Statusmanager wie Vuex oder ähnliche Tools zu verwenden, wie andere Antwortende gesagt haben.
quelle
Ich riffle die Antworten von @kubaklam und @ digout ab und vermeide dies, um zu vermeiden, dass auf jede Elternkomponente zwischen dem Enkelkind und dem (möglicherweise entfernten) Großelternteil gesendet wird:
{ methods: { tunnelEmit (event, ...payload) { let vm = this while (vm && !vm.$listeners[event]) { vm = vm.$parent } if (!vm) return console.error(`no target listener for event "${event}"`) vm.$emit(event, ...payload) } } }
Wenn Sie eine Komponente mit entfernten Enkelkindern erstellen, bei der nicht viele / keine Komponenten an das Geschäft gebunden werden sollen, die Stammkomponente jedoch als Geschäft / Quelle der Wahrheit fungieren soll, funktioniert dies recht gut. Dies ähnelt der Philosophie von Ember. Der Nachteil ist, dass dies nicht funktioniert, wenn Sie jedes Ereignis dazwischen auf jedes Elternteil abhören möchten. Aber dann können Sie $ propogateEmit wie in der obigen Antwort von @kubaklam verwenden.
Bearbeiten: Die anfängliche VM sollte auf die Komponente und nicht auf die übergeordnete Komponente der Komponente festgelegt werden. Dh
let vm = this
und nichtlet vm = this.$parent
quelle
Ich finde es wirklich toll, wie dies gehandhabt wird, indem ich eine Klasse erstelle, die an das Fenster gebunden ist, und das Broadcast / Listen-Setup so vereinfache, dass es überall in der Vue-App funktioniert.
window.Event = new class { constructor() { this.vue = new Vue(); } fire(event, data = null) { this.vue.$emit(event, data); } listen() { this.vue.$on(event, callback); } }
Jetzt können Sie einfach von überall aus feuern / senden / was auch immer, indem Sie anrufen:
Event.fire('do-the-thing');
... und Sie können einem Elternteil, einem Großelternteil, zuhören, was immer Sie wollen, indem Sie anrufen:
Event.listen('do-the-thing', () => { alert('Doing the thing!'); });
quelle