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