Commentaires
Bon, j'espère que la partie précédente est désormais claire pour toi !
On va passer à la communication enfant -> parent qui s'opère grâce à des évènements. Dans le cadre de notre projet cela nous permettra d'afficher une modal. Au clic sur un bouton de la page d'accueil on affichera un texte explicatif dans la modal.

J'ai rajouté un peu de CSS sur le projet alors ne sois pas étonné quand tu verras le code. Cela reste cependant très simpliste.

Arthur, l'apprenti développeurCette partie est-elle aussi longue que la précédente ?
Non beaucoup plus courte et plus simple !

L'émission d'évènements


Je te propose de comprendre le souci de la communication enfant -> parent en essayant directement de faire notre modal dans le projet. Pour commencer on va créer un composant Modal :

{"language":"text/html","content":"<template>\n <div class=\"modal\">\n <div class=\"modal__inner\">\n <h2>\n <slot name=\"title\">\n </slot>\n </h2>\n <div>\n <slot>\n </slot>\n </div>\n <button class=\"button centered\">Fermer</button>\n </div>\n </div>\n</template>\n\n<style scoped>\n .modal{\n width:100%;\n height:100vh;\n background:rgba(0,0,0,0.5);\n z-index:10;\n position:fixed;\n top:0;\n left:0;\n padding-top:30px;\n }\n\n .modal__inner{\n max-width:80%;\n min-width:20%;\n padding:1em 2em;\n height:auto;\n margin:auto;\n background:white;\n }\n</style>","filename":"Modal.vue"}


Je vais appeler ce composant dans mon parent App et créer la data "open" qui me permettra de jouer sur l'ouverture/fermeture de la modal :

{"language":"text/html","content":"<template>\n <modal v-show=\"open\">\n <template #title>Plus d'info</template>\n <template #default>\n Quelques explications à propos du site...\n </template>\n </modal>\n <h1 id=\"welcome\">Bienvenue sur notre site d'avis de photos !</h1>\n <button @click=\"open = !open\" class=\"button centered\">{{ openCloseText }}</button>\n \n <slider :slides=\"photos\" id=\"slider\">\n <template #default=\"{slide : photo, key : index}\">\n\t\t\t<h2>Photo {{ index+1 }}/{{ photos.length }}</h2>\n <photo \n v-bind=\"photo\"\n ></photo>\n </template>\n </slider>\n</template>\n\n<script>\nimport Photo from './components/Photo.vue'\nimport Slider from './components/Slider.vue'\nimport Modal from './components/Modal.vue'\n\nconst API_URL = 'https://api.nasa.gov/planetary/apod?start_date=2021-02-01&end_date=2021-02-21&api_key=DEMO_KEY'\nexport default {\n name: 'App',\n components: {\n Photo, Slider, Modal\n },\n data() {\n return {\n photos: [],\n open:false\n }\n },\n created() {\n fetch(API_URL)\n .then(result => result.json())\n .then(result => {this.photos = result})\n },\n computed: {\n openCloseText() {\n return open ? 'En savoir +' : 'Fermer la modal'\n }\n }\n}\n</script>\n\n<style>\n #app{\n font-family: 'Roboto', sans-serif;\n }\n\n #welcome {\n text-align:center;\n }\n\n .button{\n display:block;\n border:none;\n padding:10px 15px;\n font-size:1.2em;\n color:white;\n background: #818CF8;\n cursor:pointer;\n }\n\n .centered{\n margin:10px auto;\n }\n\n #slider{\n width:80%;\n margin:auto;\n }\n</style>\n","filename":"App.vue"}

Jusqu'ici tout va bien ?

Arthur, l'apprenti développeurTout va bien, je comprends tout. J'imagine que pour le style tu as simplement rajouté dans ton index.html un lien vers la font Roboto ?
Oui tout à fait.

Comme tu peux le remarquer on a le bouton "Fermer" qui est dans la modale. Cependant, la logique d'ouverture/fermeture (la data "open") est dans le composant App, pas le composant Modal ! Ainsi, au clic sur le bouton "Fermer", on doit agir sur "open" de App.vue. Pour ça, on va émettre un évènement de l'enfant vers le parent grâce à $emit.

{"language":"text/html","content":"<button class=\"button centered\" @click=\"$emit('closeModal')\">Fermer</button>","filename":"Modal.vue"}

Avec ce code on utilise en fait des évènements de composant. C'est à dire qu'on va émettre au parent un évènement qu'on appelle ici "closeModal". Le parent pourra alors écouter cet événement comme n'importe quel autre (@click, @input...) et agir en conséquence.
Cependant, pour dire à Vue que nous utilisons la logique des évènements de composants, il faut, au même titre que les props, utiliser un nouvel élément appelé "emits". Ceci n'est pas obligatoire mais largement recommandé pour mieux comprendre ce que fait notre composant. Modifions Modal.vue en conséquence :

{"language":"text/html","content":"<template>\n <div class=\"modal\">\n <div class=\"modal__inner\">\n <h2>\n <slot name=\"title\">\n </slot>\n </h2>\n <div>\n <slot>\n </slot>\n </div>\n <button class=\"button centered\" @click=\"$emit('closeModal')\">Fermer</button>\n </div>\n </div>\n</template>\n\n<script>\n export default{\n emits:['closeModal']\n }\n</script>\n\n<style scoped>\n .modal{\n width:100%;\n height:100vh;\n background:rgba(0,0,0,0.5);\n z-index:10;\n position:fixed;\n top:0;\n left:0;\n padding-top:30px;\n }\n\n .modal__inner{\n max-width:80%;\n min-width:20%;\n padding:1em 2em;\n height:auto;\n margin:auto;\n background:white;\n }\n</style>","filename":"Modal.vue"}

Et maintenant sur App.vue on peut écouter l'évènement "closeModal" sur l'élément Modal comme suit :

{"language":"text/html","content":"<modal v-show=\"open\" @close-modal=\"open = false\">\n <template #title>Plus d'info</template>\n <template #default>\n Quelques explications à propos du site...\n </template>\n</modal>","filename":"Modal.vue"}


Arthur, l'apprenti développeurAttends, pourquoi tu as écrit close-modal et pas closeModal ?
En fait lorsqu'on utilise la directive v-on, par défaut Vue attend du kebab-case. C'est d'ailleurs obligatoire si on n'utilise pas les SFC mais la syntaxe app.component... Donc lorsqu'on fait emit en CamelCase, Vue transforme en kebab-case si besoin. Dans le cadre des SFC, cela n'est pas obligatoire, mais j'aime bien cette syntaxe.

Arthur, l'apprenti développeurOk, c'est noté.
Il faut savoir que du côté de Modal.vue, on peut utiliser $emit dans une méthode comme suit :

{"language":"text/html","content":"<template>\n <div class=\"modal\">\n <div class=\"modal__inner\">\n <h2>\n <slot name=\"title\">\n </slot>\n </h2>\n <div>\n <slot>\n </slot>\n </div>\n <button class=\"button centered\" @click=\"close\">Fermer</button>\n </div>\n </div>\n</template>\n\n<script>\n export default{\n emits:['closeModal'],\n methods: {\n close() {\n this.$emit('closeModal')\n }\n }\n }\n</script>","filename":"Modal.vue"}

Arthur, l'apprenti développeurTrès bien !

Émission d'évènements avec paramètres


On peut tout à fait émettre des évènements en passant des paramètres. Pour ça, on va les rajouter dans le emit :

{"language":"text/html","content":"<script>\n export default{\n emits:['closeModal'],\n methods: {\n close() {\n this.$emit('closeModal', 'C\\'est fermé !')\n }\n }\n }\n</script>","filename":"Modal.vue"}

Pour les récupérer côté parent, on va maintenant utiliser une méthode plutôt que de passer le callback directement dans le template. Ainsi, transformons notre template :

{"language":"text/html","content":"<modal v-show=\"open\" @closeModal=\"close\">\n <template #title>Plus d'info</template>\n <template #default>\n Quelques explications à propos du site...\n </template>\n</modal>","filename":"App.vue"}

Puis ajoutons la méthode close dans la partie JS. Elle prend un argument : celui passé par l'émission de l'évènement :

{"language":"text/javascript","content":"methods: {\n close(message) {\n this.open = false\n console.log(message)\n }\n}","filename":"App.vue"}

Et voilà ! Compris ?

Arthur, l'apprenti développeurOui c'est compris !

Valider les emits


Comme pour les props, on peut imposer une certaine structure à nos évènements émis. Pour ça, l'élément "emits" devient un objet qui prend chacun des évènements émis. Pour chacun des évènements émis on peut mettre une règle sur les paramètres qu'il prend. Voici un exemple pour notre closeModal :

{"language":"text/html","content":"<script>\n export default{\n emits:{\n closeModal: (message) => {\n if(message && message.length > 3) {\n return true;\n } else {\n console.warn('Close Modal attend un message plus long')\n return false\n }\n }\n },\n methods: {\n close() {\n this.$emit('closeModal', 'C\\'est fermé !')\n }\n }\n }\n</script>","filename":"Modal.vue"}

Arthur, l'apprenti développeurOk c'est noté aussi !
Bon, et bien on a fait le tour des évènements émis.

Arthur, l'apprenti développeurCette partie était plus courte, et plus simple. Que voit-on après ?
Hé bien je te propose que l'on enrichisse nos slides en ajoutant en dessous des photos un blog avec 2 onglets. Ces 2 onglets permettront de voir respectivement les avis déjà postés et un formulaire pour en ajouter. Cela nous permettra de voir les composants dynamiques ! J'ai terminé cette partie
Demander de l'assistance