Cet article traite de l'utilisation des événements avec Mitt entre plusieurs composants Riot pour partager des données.
Avant de commencer, assurez-vous d'avoir une application de base RiotJS, ou lisez mes articles précédents.
Je suppose que vous avez une compréhension fondamentale de Riot ; cependant, n'hésitez pas à vous référer à la documentation si nécessaire : https://riot.js.org/documentation/
Il existe trois méthodes pour partager des données entre composants :
- Utiliser les propriétés Riot props pour passer des valeurs à un composant enfant. Le composant enfant doit émettre des événements vers le composant parent si une action se produit, comme un clic ou un changement d'entrée. Dans ce cas, la portée de communication est limitée: du composant parent vers l'enfant et de l'enfant vers le parent.
- Utiliser un gestionnaire d'état (state manager), comme Pinia pour Vuejs ou Redux pour React, pour partager un état entre les composants/pages. La portée de communication est globale: tous les composants peuvent voir et modifier les états.
La dernière méthode utilise un émetteur d'événements comme Mitt, un modèle de messagerie appelé Pub/Sub : un ou plusieurs éditeurs émettent les données, et les abonnés les traite sans savoir quels sont les éditeurs. Cet article reproduit le modèle entre plusieurs composants RiotJS ! L'objectif est de :
- Étape 1 : Créer une application Riot de base affichant un composant Modal de Confirmation lors du clic sur un bouton. Le bouton est l'éditeur, et le Modal est l'abonné.
- Étape 2 : Lorsque le modal est visible et soit validé soit annulé, il émet un événement vers l'éditeur d'origine. Dans ce cas, le Modal est l'éditeur, et le Bouton est l'abonné.
Base de l'émetteur
Commencez par installer Mitt dans votre projet Riot/Vite :
$ npm install mitt
Créez un fichier nommé events.js, puis chargez Mitt et instanciez-le :
/** EVENT BUS **/
import mitt from 'mitt'
const _bus = mitt();
export default _bus;
Composant de Dialogue (Abonné)
Créez un composant de dialogue nommé c-dialog.riot. La base du composant est tirée de mon article précédent Créer un composant de dialogue avec RiotJS:
<c-dialog>
<dialog class="{state.active ? 'active ' : null}">
<h5>{ state.title }</h5>
<div>
{ state.message }
</div>
<nav class="right-align no-space">
<button onclick={ toggleModal } class="transparent link">{ state.cancel }</button>
<button onclick={ validateAction } class="transparent link">{ state.validate }</button>
</nav>
</dialog>
<script>
import events from './events.js'
export default {
state: {
active: false,
title: 'Confirmation',
message: 'Are you sure?',
callbackID : '',
args : true,
validate : 'Validate',
cancel: "Cancel"
},
onMounted() {
events.on('open-modal-validation', this.openModalValidation);
},
onUnmounted() {
events.off('open-modal-validation', this.openModalValidation);
},
validateAction () {
if (this.state.callbackID) {
events.emit(this.state.callbackID, this?.state?.args);
}
this.toggleModal();
},
openModalValidation ({ message, callbackID, args }) {
this.update({
message,
callbackID,
args: args ?? true
})
this.toggleModal();
},
toggleModal() {
this.update({ active: !this.state.active })
if (this.state.active === true) {
console.log("Modal opened.")
} else {
console.log("Modal closed.")
}
}
}
</script>
</c-dialog>
Code Source: https://github.com/steevepay/riot-beercss/blob/main/examples/mitt/c-dialog.riot
Détails du code :
- Le modal est visible uniquement si
state.active
esttrue
. - Le dialogue peut être personnalisé avec des données personnalisées, stockées dans l'objet State Riot.
- Le bus d'événements est chargé avec
import events from './events.js'
. - Lorsque le composant est monté, il s'abonne à l'événement
open-modal-validation
grâce àevents.on('open-modal-validation', this.openModalValidation);
. Lorsque l'événement nomméopen-modal-validation
se produit, il exécute la fonctionopenModalValidation
. - La fonction
openModalValidation
bascule la visibilité du modal en définissantstate.active
sur true. En même temps, elle recueille des données supplémentaires telles qu'un state.message personnalisé et unstate.callbackID
. - Si le bouton de validation est cliqué, il émet un événement vers
state.callback
avec des arguments personnalisésstate.args
; cela peut être un objet ou une chaîne de caractères. - Si le bouton d'annulation est cliqué, le modal est désactivé en définissant
state.active
surfalse
.
Composant Index (Éditeur)
Enfin, chargez le Dialogue et un Bouton dans un fichier index.riot:
<index-riot>
<div style="width:600px;padding:20px;">
<c-dialog />
<c-button onclick={ confirmDeletion }> Delete File </c-button>
</div>
<script>
import cButton from "../../components/c-button.riot";
import cSnackbar from "../../components/c-snackbar.riot";
import cDialog from "./c-dialog.riot";
import events from "./events.js"
export default {
components: {
cButton,
cDialog
},
onMounted() {
events.on('delete-file', this.deleteFile);
},
onUnmounted() {
events.off('delete-file', this.deleteFile);
},
state: {
active: false
},
confirmDeletion () {
events.emit('open-modal-validation', {
message: "Do you confirm you want to delete the file \"2024-04-invoice.pdf\"?",
callbackID: 'delete-file'
});
},
deleteFile () {
console.log("FILE DELETED! 🗑️");
}
}
</script>
</index-riot>
Code Source: https://github.com/steevepay/riot-beercss/blob/main/examples/mitt/index.riot
Détails du code :
- Les composants sont importés avec
import cDialog from "./components/c-dialog.riot";
puis chargés dans l'objet Riotcomponents:{}
. - Le composant dialog est instancié avec dans le HTML.
- L'émetteur d'événements, Mitt, est chargé avec import events from "./events.js".
- Lors du clic sur le bouton, la fonction confirmDeletion est exécutée et émet l'événement "open-modal-validation". Le Dialogue est maintenant visible ! Simultanément, un message personnalisé et l'ID de l'abonné "delete-file" sont passés en tant qu'arguments pour l'événement.
- Lorsque le bouton du dialogue est cliqué, un événement est émis, en utilisant
state.callbackID
comme destinataire de l'abonné.
Cette méthode peut être étendue en créant plus de boutons (éditeurs) émettant vers un seul Dialogue (abonné), par exemple :
Voici le code de index.riot pour reproduire la démo GIF :
<index-riot>
<div style="width:600px;padding:50px;">
<c-dialog />
<c-button onclick={ confirmDeletion }> Delete File </c-button>
<c-button onclick={ confirmDeletionBucket }> Delete Bucket </c-button>
<c-button error={ true } onclick={ confirmDeletionAccount }> Delete Account </c-button>
</div>
<script>
import cButton from "../../components/c-button.riot";
import cSnackbar from "../../components/c-snackbar.riot";
import cDialog from "./c-dialog.riot";
import events from "./events.js"
export default {
components: {
cButton,
cDialog
},
onMounted() {
events.on('delete-file', this.deleteFile);
events.on('delete-bucket', this.deleteBucket);
events.on('delete-account', this.deleteAccount);
},
onUnmounted() {
events.off('delete-file', this.deleteFile);
events.off('delete-bucket', this.deleteBucket);
events.off('delete-account', this.deleteAccount);
},
state: {
active: false
},
getRandomNumber() {
return Math.floor(Math.random() * Date.now())
},
confirmDeletion () {
events.emit('open-modal-validation', {
message: "Do you confirm you want to delete the File \"2024-04-invoice.pdf\"?",
callbackID: 'delete-file',
args: "file-" + this.getRandomNumber()
});
},
confirmDeletionBucket() {
events.emit('open-modal-validation', {
message: "Are you sure you want to delete this S3 bucket \"2024-assets\"? This action is irreversible.",
callbackID: 'delete-bucket',
args: "bucket-" + this.getRandomNumber()
});
},
confirmDeletionAccount() {
events.emit('open-modal-validation', {
message: "Are you sure you want to proceed with deleting your account? This action is irreversible and will permanently remove all your personal data associated with the account.",
callbackID: 'delete-account',
args: "account-" + this.getRandomNumber()
});
},
deleteFile (id) {
console.log("%c File deleted! ID: " + id, 'background: #222; color: #bada55');
},
deleteBucket (id) {
console.log("%c Bucket deleted! ID: " + id, 'background: #222; color: #ff00ff');
},
deleteAccount (id) {
console.log("%c Account deleted! ID: " + id, 'background: #222; color: #ff0000');
}
}
</script>
</index-riot>
Code Source: https://github.com/steevepay/riot-beercss/blob/main/examples/mitt/index.riot
Détails du code :
- Dans le onMounted Riot Hook, trois événements sont écoutés et redirigés vers trois fonctions :
deleteFile
,deleteBucket
oudeleteAccount
. Dans une application de production, cela pourrait être un appel API pour supprimer une ressource (fichier ou élément dans une base de données). - Trois boutons sont créés dans le HTML, et chaque clic émet l'événement
open-modal-validation
et passe un message personnalisé, un ID de rappel et un argument :- Le
callbackID
est un événement à émettre lorsque le bouton de validation est cliqué. - Les
args
sont utilisés pour passer un ID de ressource d'une API ou d'une base de données, comme un ID de fichier ou un ID de compte. Dans notre cas, un ID aléatoire est passé en tant qu'argument.
- Le
- Lorsque qu'un bouton du composant Dialogue est validé, il émet
callbackID
en tant qu'événement, et la fonction correspondante est exécutée pour supprimer le fichier, le bucket ou le compte. - Lorsque le composant est retiré de la page web, tout événement doit être écouté : Lorsque le onUnmounted](https://riot.js.org/api/#component-object) Riot hook est exécuté,
event.off
supprime l'abonnement à l'événement.
Conclusion
Au lieu de créer un Dialogue pour chaque bouton, un seul Dialogue suffit pour plusieurs boutons avec le modèle pub/sub. Maintenant, un composant peut écouter et émettre des événements pour communiquer des données basées sur des actions. Voilà 🎉
Passez une excellente journée ! Santé 🍻
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.