web-dev-qa-db-fra.com

Emettre un événement du contenu de l'emplacement vers le parent

J'essaie de créer un contrôle de carrousel flexible qui permet aux éléments de contenu internes de forcer le changement d'une diapositive, ainsi que le carrousel se contrôle lui-même pour changer les diapositives

Un exemple de structure dans ma page ressemble

<my-carousel>
  <div class="slide">
    <button @click="$emit('next')">Next</button>
  </div>

  <div class="slide">
    <button @click="$emit('close')">Close</button>
  </div>
</my-carousel>

Le modèle de mon carrousel est comme

<div class="carousel">
  <div class="slides" ref="slides">
    <slot></slot>
  </div> 
  <footer>
   <!-- other carousel controls like arrows, indicators etc go here -->
  </footer>
</div>

Et un script comme

...
created() {
 this.$on('next', this.next)
}
...

L'accès aux diapositives, etc. n'est pas un problème, cependant en utilisant $emit ne fonctionnera pas et je n'arrive pas à trouver une solution simple à ce problème.

Je veux que le composant soit facilement réutilisable sans avoir à utiliser

  • bus d'événement central
  • diapositives codées en dur dans un carrousel
  • implémentez les méthodes de diapositive suivantes au niveau de la page et passez l'index en cours au contrôle (car je devrais le faire chaque fois que j'utilise le carrousel)
11
Frank Provost

Les emplacements sont compilés par rapport à la portée du composant parent, par conséquent, les événements que vous émettez depuis l'emplacement ne seront reçus que par le composant auquel appartient le modèle.

Si vous souhaitez une interaction entre le carrousel et les diapositives, vous pouvez utiliser un emplacement de portée qui vous permet d'exposer les données et les méthodes du carrousel à l'emplacement.

En supposant que votre composant de carrousel possède les méthodes next et close:

Modèle de carrousel:

<div class="carousel">
  <div class="slides" ref="slides">
    <slot :next="next" :close="close"></slot>
  </div> 
  <footer>
    <!-- Other carousel controls like arrows, indicators etc go here -->
  </footer>
</div>

Exemple d'utilisation du carrousel:

<my-carousel v-slot="scope">
  <div class="slide">
    <button @click="scope.next">Next</button>
  </div>

  </div class="slide">
    <button @click="scope.close">Close</button>
  </div>
</my-carousel>
19
Decade Moon

Ma solution

Créez simplement un composant écouteur d'événements (par exemple "EventListener") et tout ce qu'il fait est de rendre l'emplacement par défaut comme ceci:

EventListener.vue

export default {
    name: 'EventListener'
    render() {
        return this.$slots.default;
    }
}

Utilisez maintenant ce composant <event-listener> Et emballez-le sur votre <slot>. Les composants enfants à l'intérieur du slot doivent émettre des événements au parent comme ceci: this.$parent.$emit('myevent').

Attachez vos événements personnalisés au composant <event-listener @myevent="handleEvent">.

Modèle de carrousel:

<div class="carousel">
  <event-listener @next="handleNext" @close="handleClose">
     <div class="slides" ref="slides">
       <slot></slot>
     </div> 
  </event-listener>
  <footer>
   <!-- other carousel controls like arrows, indicators etc go here -->
  </footer>
</div>

Exemple de carrousel:

<my-carousel>
  <div class="slide">
    <button @click="$parent.$emit('next')">Next</button>
  </div>

  </div class="slide">
    <button @click="$parent.$emit('close')">Close</button>
  </div>
</my-carousel>

Remarque: Le composant <event-listener> Ne doit avoir que n vnode enfant. Ce ne peut pas être le <slot>, Nous l'avons donc simplement enveloppé sur le div à la place.

1
Daniel Ramirez

Il n'est pas possible d'écouter les événements émis depuis le contenu de l'emplacement par le composant contenu. Dans ton cas, <my-carousel> ne peut pas écouter les événements next et close. Le contenu des emplacements est compilé par rapport à la portée du composant parent.

Pour contourner ce problème, vous pouvez procéder comme suit:

<div class="carousel">
    <!-- Listen to click event here -->
    <div class="slides" @click="doSomething($event)" ref="slides">
        <slot></slot>
    </div> 
    <footer>
        <!-- other carousel controls like arrows, indicators etc go here -->
    </footer>
</div>

Et dans doSomething, vous pouvez trouver le bouton sur lequel vous avez cliqué en utilisant $event.target. En savoir plus sur ce problème sur https://github.com/vuejs/vue/issues/4332 et https://github.com/vuejs/vue/issues/4781 =

Il existe une façon plus avancée de le faire et d'écrire une fonction de rendu personnalisée. Vous encapsulez le gestionnaire de clics passé par un parent dans la fonction de rendu carousel et passez une nouvelle fonction au contenu de l'emplacement. Mais c'est quelque chose à faire extrêmement rarement et le considérerait comme un anti-modèle.

1
Harshal Patil

J'ai découvert que cela peut être fait en utilisant $ root.

<h1>Regular html document content</h1>
<parent-component>
  <h2>Some parent html that goes inside the slot</h2>
  <child-component></child-component>
</parent-component>

composant parent:

<template>
    <div>
        <slot></slot>
        <h3>extra html that is displayed</h3>
    </div>
</template>
<script>
export default {

    created() {
        this.$root.$on('child-event', this.reactOnChildEvent);
    },

    methods: {
        this.reactOnChildEvent: function(message) {
            console.log(message);
        }
    }
};
</script>

composant enfant:

<template>
    <div>
      <button @click="$root.$emit('child-event', 'hello world')">
         click here
      </button>
    </div>
</template>

Cependant, si possible, utilisez l'emplacement délimité comme mentionné ci-dessus.

0
Joery