VueJs 2.0 émettent cas de grand enfant à sa grand-mère composant

il semble que Vue.js 2.0 n'émet pas d'événements d'un petit enfant à sa composante grand-parent.

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

Ce JsFiddle résout le problème https://jsfiddle.net/y5dvkqbd/4/ , mais par emtting deux événements:

  • Un grand enfant de milieu composant
  • puis émettant à nouveau du composant intermédiaire au grand parent

ajouter cet événement intermédiaire semble répétitif et inutile. Est-il possible d'émettre directement à grand parent que je ne connais pas?

32
demandé sur Bassem El Hachem 2017-03-06 02:45:43

3 réponses

la communauté Vue favorise généralement L'utilisation de Vuex pour résoudre ce genre de problème. Des changements sont apportés à L'état de Vuex et la représentation de DOM découle de cela, éliminant le besoin d'événements dans de nombreux cas.

Sauf que, re-émission serait probablement le meilleur choix, et enfin vous pouvez choisir d'utiliser un bus d'événements comme détaillé dans l'autre réponse fortement votée à cette question.

la réponse ci-dessous est ma réponse originale à cette question et n'est pas une approche que je prendrais maintenant, ayant plus d'expérience avec Vue.


il s'agit D'un cas où je pourrais être en désaccord avec le choix de design de Vue et recourir à 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);
        }
    }
}

et parent,

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

sinon, oui, vous devez réémettre, ou utiliser un bus.

Note: j'ai ajouté du code dans la méthode doEvent pour gérer IE; ce code pourrait être extrait de manière réutilisable.

19
répondu Bert 2018-01-08 15:32:34

Oui, vous avez raison uniquement sur les événements de passer d'un enfant à un parent. Ils ne vont pas plus loin, par exemple de l'enfant au grand-parent.

la documentation de Vue (brièvement) aborde cette situation dans le Non Communication Parent-Enfant section.

l'idée générale est que dans le composant grandparent vous créez un vide Vue composante qui est transmise des grands-parents aux enfants et petits-enfants au moyen d'accessoires. Les grands-parents écoutent alors les événements et les petits-enfants émettent des événements sur ce "bus événement".

certaines applications utilisent un bus d'événements global au lieu d'un bus d'événements par composante. Utiliser un bus d'événements global signifie que vous aurez besoin d'avoir des noms d'événements uniques ou des espaces de noms pour que les événements ne s'entrechoquent pas entre les différents composants.

Voici un exemple de comment implémenter un bus d'événements global simple.

15
répondu Sly_cardinal 2017-03-06 00:10:42

une Autre solution:

vm.$root.$emit petit-enfant, puis vm.$root.$on à l'ancêtre (ou n'importe où).

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>
5
répondu Sphinx 2018-08-02 20:24:26