Tampaknya Vue.js 2.0 tidak memancarkan peristiwa dari anak cucu ke komponen induknya.
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'
})
JsFiddle ini memecahkan masalah https://jsfiddle.net/y5dvkqbd/4/ , tetapi dengan mengemisikan dua peristiwa:
- Satu dari cucu sampai komponen tengah
- Kemudian dipancarkan lagi dari komponen tengah ke grand parent
Menambahkan acara tengah ini tampaknya berulang dan tidak perlu. Apakah ada cara untuk memancarkan langsung ke kakek nenek yang tidak saya ketahui?
sumber
JAWABAN BARU (Pembaruan Nov-2018)
Saya menemukan bahwa kami sebenarnya dapat melakukan ini dengan memanfaatkan
$parent
properti di komponen grand child:this.$parent.$emit("submit", {somekey: somevalue})
Jauh lebih bersih dan sederhana.
sumber
transition
atau komponen pembungkus lainnya dan itu akan pecah, meninggalkan Anda dengan tanda tanya besar di kepala Anda.Ya, Anda benar kejadian hanya pergi dari anak ke orang tua. Mereka tidak melangkah lebih jauh, misalnya dari anak ke kakek-nenek.
Dokumentasi Vue (secara singkat) membahas situasi ini di bagian Komunikasi Non Parent-Child .
Ide umumnya adalah bahwa dalam komponen grandparent Anda membuat kosong
Vue
komponen yang diturunkan dari kakek-nenek ke anak-anak dan cucu melalui alat peraga. Kakek kemudian mendengarkan acara dan cucu memancarkan acara di "bus acara" itu.Beberapa aplikasi menggunakan bus peristiwa global, bukan bus peristiwa per komponen. Menggunakan bus peristiwa global berarti Anda harus memiliki nama peristiwa atau namespacing unik sehingga peristiwa tidak bentrok di antara komponen yang berbeda.
Berikut adalah contoh cara mengimplementasikan bus acara global sederhana .
sumber
Solusi lain akan on / emit di root node:
Menggunakan
vm.$root.$emit
di grand-child , lalu menggunakanvm.$root.$on
di leluhur (atau di mana pun Anda suka).Diperbarui : terkadang Anda ingin menonaktifkan pendengar di beberapa situasi tertentu, gunakan vm. $ Off (misalnya:
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>
sumber
Jika Anda ingin fleksibel dan hanya menyiarkan acara kepada semua orang tua dan orang tua mereka secara rekursif hingga ke root, Anda dapat melakukan sesuatu seperti:
let vm = this.$parent while(vm) { vm.$emit('submit') vm = vm.$parent }
sumber
Ini adalah satu-satunya kasus ketika saya menggunakan bus acara !! Untuk meneruskan data dari anak bertingkat dalam, ke tidak langsung ke induk, komunikasi.
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) })
Catatan: Jika Anda tidak menginginkan acara itu lagi, harap batalkan pendaftarannya:
this.$event.$off('event_name')
Saya tidak suka menggunakan vuex untuk komunikasi kakek-anak ke kakek-tua (Atau tingkat komunikasi serupa).
sumber
Saya telah membuat mixin pendek berdasarkan jawaban @digout. Anda ingin meletakkannya, sebelum inisialisasi instance Vue Anda (Vue baru ...) untuk menggunakannya secara global dalam proyek. Anda dapat menggunakannya seperti acara biasa.
Vue.mixin({ methods: { $propagatedEmit: function (event, payload) { let vm = this.$parent; while (vm) { vm.$emit(event, payload); vm = vm.$parent; } } } })
sumber
targetRef
yang menghentikan penyebaran pada komponen yang Anda targetkan. Thewhile
Kondisi itu akan mencakup&& vm.$refs[targetRef]
- Anda juga akan perlu untuk memasukkan bahwaref
atribut pada komponen yang ditargetkan Dalam kasus penggunaan saya saya tidak perlu terowongan sepanjang jalan ke akar, menyimpan beberapa peristiwa dari menembak dan mungkin beberapa nanodetik berharga waktuKomponen VueJS 2 memiliki a
$parent
properti yang berisi komponen induknya.Komponen induk itu juga termasuk miliknya sendiri
$parent
propertinya .Kemudian, mengakses komponen "grandparent" adalah masalah mengakses komponen "parent's parent":
this.$parent["$parent"].$emit("myevent", { data: 123 });
Bagaimanapun, ini agak rumit , dan saya merekomendasikan menggunakan manajer keadaan global seperti Vuex atau alat serupa, seperti yang dikatakan responden lain.
sumber
Mengasah jawaban @kubaklam dan @ digout, inilah yang saya gunakan untuk menghindari pancaran pada setiap komponen induk antara cucu dan kakek (mungkin jauh):
{ 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) } } }
Saat membangun komponen dengan cucu jauh di mana Anda tidak ingin banyak / komponen terikat ke penyimpanan, namun ingin komponen root bertindak sebagai penyimpan / sumber kebenaran, ini bekerja dengan cukup baik. Ini mirip dengan filosofi data down actions up dari Ember. Kelemahannya adalah jika Anda ingin mendengarkan acara itu pada setiap orang tua di antaranya, maka ini tidak akan berhasil. Tapi kemudian Anda bisa menggunakan $ propogateEmit seperti pada jawaban di atas oleh @kubaklam.
Edit: vm awal harus disetel ke komponen, dan bukan induk komponen. Yaitu
let vm = this
dan tidaklet vm = this.$parent
sumber
Saya benar-benar memahami cara ini ditangani dengan membuat kelas yang terikat ke jendela dan menyederhanakan penyiapan siaran / dengar untuk bekerja di mana pun Anda berada di aplikasi 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); } }
Sekarang Anda bisa menyalakan / menyiarkan / apapun dari mana saja dengan menelepon:
Event.fire('do-the-thing');
... dan Anda dapat mendengarkan orang tua, kakek, apa pun yang Anda inginkan dengan menelepon:
Event.listen('do-the-thing', () => { alert('Doing the thing!'); });
sumber