Похоже, что Vue.js 2.0 не отправляет события от великого потомка к своему основному родительскому компоненту.
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 решает проблему https://jsfiddle.net/y5dvkqbd/4/ , но с помощью двух событий:
- Один от великого ребенка до среднего компонента
- Затем снова испускаем от среднего компонента к основному родителю
Добавление этого промежуточного события кажется повторяющимся и ненужным. Есть ли способ направить сигнал напрямую великому родителю, о котором я не знаю?
источник
Сообщество Vue обычно поддерживает использование Vuex для решения такого рода проблем. Изменения вносятся в состояние Vuex, и представление DOM просто вытекает из этого, устраняя необходимость в событиях во многих случаях.
Если этого не сделать, переизлучение, вероятно, будет следующим лучшим выбором, и, наконец, вы можете выбрать использование шины событий, как подробно описано в другом ответе на этот вопрос, получившем высокую оценку.
Приведенный ниже ответ является моим первоначальным ответом на этот вопрос и не подход, который я бы использовал сейчас, имея больше опыта работы с Vue.
Это тот случай, когда я могу не согласиться с выбором дизайна Vue и прибегнуть к DOM.
В
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); } } }
и
parent
,mounted(){ this.$el.addEventListener("eventtriggered", () => this.performAction()) }
В противном случае - да, придется переиздать или воспользоваться автобусом.
Примечание. Я добавил код в метод doEvent для обработки IE; этот код можно извлечь многоразовым способом.
источник
НОВЫЙ ОТВЕТ (обновление за ноябрь 2018 г.)
Я обнаружил, что мы действительно можем сделать это, используя
$parent
свойство в большом дочернем компоненте:this.$parent.$emit("submit", {somekey: somevalue})
Намного чище и проще.
источник
transition
либо другой компонент-оболочку, и он сломается, оставив вам большой вопросительный знак в вашей голове.Да вы правы, события переходят только от ребенка к родителю. Они не идут дальше, например, от ребенка к бабушке или дедушке.
В документации по Vue (кратко) эта ситуация рассматривается в разделе « Общение между родителями и детьми ».
Общая идея состоит в том, что в компоненте дедушка и бабушка вы создаете пустой
Vue
компонент, который передается от бабушки и дедушки к детям и внукам через свойства. Затем дедушка и бабушка слушают события, а внуки излучают события на этой «шине событий».Некоторые приложения используют глобальную шину событий вместо шины событий для отдельных компонентов. Использование глобальной шины событий означает, что вам потребуются уникальные имена событий или пространство имен, чтобы события не конфликтовали между различными компонентами.
Вот пример того, как реализовать простую глобальную шину событий .
источник
Другое решение будет включено / излучать на корневом узле:
Используется
vm.$root.$emit
в наследнике , затем используетсяvm.$root.$on
в предке (или где угодно).Обновлено : иногда вы хотите отключить прослушиватель в определенных ситуациях, используйте vm. $ Off (например:
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>
источник
Если вы хотите быть гибким и просто рекурсивно транслировать событие всем родителям и их родителям вплоть до корня, вы можете сделать что-то вроде:
let vm = this.$parent while(vm) { vm.$emit('submit') vm = vm.$parent }
источник
Это единственный случай, когда я использую шину событий !! Для передачи данных от глубоко вложенного дочернего элемента к непосредственному родительскому взаимодействию.
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) })
Примечание. Если вы больше не хотите это событие, отмените его регистрацию:
this.$event.$off('event_name')
Мне не нравится использовать vuex для общения между внучками и дедушками (или на аналогичном уровне общения).
источник
Я сделал небольшой миксин на основе ответа @digout. Вы хотите поместить его перед инициализацией вашего экземпляра Vue (новый Vue ...), чтобы использовать его глобально в проекте. Вы можете использовать его как обычное событие.
Vue.mixin({ methods: { $propagatedEmit: function (event, payload) { let vm = this.$parent; while (vm) { vm.$emit(event, payload); vm = vm.$parent; } } } })
источник
targetRef
который останавливает распространение на компоненте, на который вы нацеливаетесь. Тогдаwhile
условие будет включать&& vm.$refs[targetRef]
- вам также нужно будет включить этотref
атрибут в целевой компонент. В моем случае использования мне не нужно было полностью туннелировать до root, сохраняя несколько событий от запуска и, возможно, пару драгоценных наносекунд времяКомпоненты VueJS 2 имеют
$parent
свойство, содержащее их родительский компонент.Этот родительский компонент также включает собственное
$parent
свойство.Затем, доступ к компоненту «дедушка и бабушка», это вопрос доступа к компоненту «родительский родитель»:
this.$parent["$parent"].$emit("myevent", { data: 123 });
В любом случае, это довольно сложно , и я рекомендую использовать глобальный менеджер состояний, такой как Vuex или аналогичные инструменты, как говорили другие респонденты.
источник
Отказавшись от ответов @kubaklam и @ digout, это то, что я использую, чтобы избежать излучения на каждом родительском компоненте между внуком и (возможно, далекими) дедушкой и бабушкой:
{ 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) } } }
При создании компонента с удаленными внуками, где вы не хотите, чтобы многие / какие-либо компоненты были привязаны к хранилищу, но хотите, чтобы корневой компонент действовал как хранилище / источник истины, это работает довольно хорошо. Это похоже на философию Ember по уменьшению данных. Обратной стороной является то, что если вы хотите прослушивать это событие для каждого родителя между ними, это не сработает. Но тогда вы можете использовать $ propogateEmit, как в ответе @kubaklam выше.
Изменить: начальный vm должен быть установлен для компонента, а не для его родителя. Т.е.
let vm = this
а неlet vm = this.$parent
источник
Я действительно понимаю, как это обрабатывается, создав класс, привязанный к окну, и упростил настройку трансляции / прослушивания для работы, где бы вы ни находились в приложении 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); } }
Теперь вы можете просто запустить / транслировать / что угодно из любого места, позвонив:
Event.fire('do-the-thing');
... и вы можете слушать родителей, бабушек и дедушек, что хотите, позвонив:
Event.listen('do-the-thing', () => { alert('Doing the thing!'); });
источник