wake-up-neo.net

VueJs 2.0 gibt ein Ereignis vom Enkelkind an seinen Enkel ab

Es scheint, dass Vue.js 2.0 keine Ereignisse von einem Enkelkind an seine übergeordnete Komponente sendet.

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'
})

Dieses JsFiddle löst das Problem https://jsfiddle.net/y5dvkqbd/4/ , löst jedoch zwei Ereignisse auf: 

  • Eine vom Enkel zur mittleren Komponente
  • Dann wieder von der mittleren Komponente zum Großelternteil emittieren

Das Hinzufügen dieses mittleren Ereignisses scheint sich wiederholend und unnötig zu sein. Gibt es eine Möglichkeit, direkt an den Großelternteil auszusenden, die mir nicht bewusst ist?

40
BassMHL

Die Vue Community bevorzugt im Allgemeinen die Verwendung von Vuex, um diese Art von Problem zu lösen. Änderungen werden am Vuex-Status vorgenommen, und die DOM-Darstellung ergibt sich nur daraus, sodass in vielen Fällen keine Ereignisse erforderlich sind.

Wenn Sie dies nicht beachten, ist das erneute Aussenden wahrscheinlich die nächstbeste Wahl, und schließlich können Sie einen Ereignisbus verwenden, wie in der anderen hoch bewerteten Antwort beschrieben zu dieser Frage.

Die folgende Antwort ist meine ursprüngliche Antwort auf diese Frage und ist kein Ansatz, den ich jetzt wählen würde, da ich mehr Erfahrung mit Vue habe.


Dies ist ein Fall, in dem ich mit Vues Design-Wahl nicht einverstanden sein und auf DOM zurückgreifen könnte.

Im 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.

29
Bert

Ja, die richtigen Ereignisse werden nur vom Kind zum Elternteil geleitet. Sie gehen nicht weiter, z. vom Kind bis zum Großelternteil.

Die Vue-Dokumentation behandelt diese Situation (kurz) im Abschnitt Non Parent-Child Communication .

Die allgemeine Idee ist, dass Sie in der Großelternkomponente eine leere Vue-Komponente erstellen, die von Großeltern ü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 Namensräume benötigen, damit Ereignisse nicht zwischen verschiedenen Komponenten kollidieren.

Hier ist ein Beispiel für , wie ein einfacher globaler Ereignisbus implementiert wird.

18
Sly_cardinal

In Vue 2.4 wurde eine Möglichkeit eingeführt, Ereignisse mithilfe von vm.$listeners Einfach in der Hierarchie weiterzuleiten.

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

Enthält Ereignis-Listener für übergeordnete Bereiche v-on (Ohne die Modifizierer .native). Dies kann über v-on="$listeners" An eine innere Komponente weitergegeben werden - nützlich beim Erstellen transparenter Wrapper-Komponenten.

Siehe den folgenden Ausschnitt mit v-on="$listeners" In der Komponente grand-child In der Vorlage 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>
12
Michael Rush

Eine andere Lösung wird sein:

Verwendet vm.$root.$emit in grand-child und verwendet dann vm.$root.$on beim Vorfahren (oder an einem beliebigen Ort).

Vue.component('parent', {
  template: '<div>I am the parent - {{ action }} <child @eventtriggered="performAction"></child></div>',
  data(){
    return {
      action: 'No action'
    }
  },
  created: function () {
  	this.$root.$on('eventtriggered1', () => {
    	this.performAction()
    })
  },
  methods: {
    performAction() { this.action = 'actionDone' }
  }
})

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">Do 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>

6
Sphinx

NEUE ANTWORT (Update vom November 2018)  

Ich entdeckte, dass wir dies tatsächlich tun könnten, indem wir die $parent -Eigenschaft in der Grand-Child-Komponente nutzen:

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

Viel sauberer und einfacher.

4
BassMHL

Dies ist der einzige Fall, wenn ich Ereignisbus benutze !! Zur Weitergabe von Daten von tief verschachtelten Kindern an nicht direkt übergeordnete, Kommunikation.

First: Erstellen Sie eine js-Datei (ich nenne sie eventbus.js) mit folgendem Inhalt:

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

Sekunde: In Ihrer untergeordneten Komponente wird ein Ereignis ausgegeben:

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

Third: Hören Sie sich das übergeordnete Ereignis an:

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

Hinweis: Wenn Sie dieses Ereignis nicht mehr wünschen, heben Sie die Registrierung auf:

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

INFO: Die unten stehende persönliche Meinung muss nicht gelesen werden

Ich mag es nicht, vuex für die Kommunikation von Enkelkindern zu Großeltern (oder einer ähnlichen Kommunikationsebene) zu verwenden.

In vue.js für die Weitergabe von Daten von Großeltern zu Großkind können Sie liefern/inject verwenden. Für das Gegenteil gibt es nichts Ähnliches. (Enkelkind zu Großelternteil) Ich benutze also den Event-Bus, wenn ich diese Art von Kommunikation durchführen muss.

1
roli roli

Hinzufügen zu BassMHLs Antwort , wenn Sie flexibler sein und einfach ein Ereignis rekursiv an alle Eltern und deren Eltern senden möchten, können Sie Folgendes tun:

let vm = this.$parent

while(vm) {
    vm.$emit('submit')
    vm = vm.$parent
}
1
digout

Wie das gehandhabt wird, erfahre ich wirklich, indem ich eine Klasse erstelle, die an das Fenster gebunden ist, und das Broadcast-/Listen-Setup so vereinfacht, 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 oder Großelternteil zuhören, was immer Sie möchten, indem Sie anrufen:

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