Commentaires
On va donc maintenant attaquer les méthodes qu'on peut utiliser dans Vue.JS. Cette partie est assez simple car les méthodes ne sont ni plus ni moins que des fonctions qu'on écrit d'habitude en Javascript Vanille.

Ces fonctions se mettent dans un objet "methods" comme suit :

{"language":"text/javascript","content":"const Photos = {\n\t\t\tdata() {\n\t\t\t return {\n\t\t\t \t\n\t\t\t }\n\t\t\t},\n \t\t\tmethods: {\n \t\t\t\tmaFonction() {\n \t\t\t\t\t// Mon code\n \t\t\t\t}, \n\t\t\t\tmaDeuxiemeFonction(parametre1) {\n\t\t\t\t\t// Mon code\n\t\t\t\t}\n \t\t\t}\n\t\t}\n\t\tconst app = Vue.createApp(Photos).mount('#app')","filename":""}

Tu remarqueras ici que j'ai utilisé une nouvelle syntaxe pour utiliser Vue. Ça fonctionne toujours aussi bien, c'est simplement une autre façon d'instancier Vue ;).

Dans ces méthodes, on utilise du Javascript classique. Voyons un exemple simple.

{"language":"text/html","content":"<main id=\"app\" v-cloak>\n <button @click=\"playMusic('pop')\">Jouer de la musique pop</button>\n <button @click=\"playMusic('jazz')\">Jouer du jazz</button>\n</main>\n\n<script type=\"module\">\n import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'\n createApp({\n methods:{\n playMusic(style) {\n const audio = new Audio(style+'.mp3');\n audio.play()\n }\n }\n }).mount('#app')\n\n</script>","filename":"index.html"}

Arthur, l'apprenti développeurC'est très simple à comprendre, ça ressemble vraiment aux fonctions classiques avec Javascript
Tout à fait ! Sache qu'à l'intérieur de nos méthodes, tu peux aussi sélectionner des éléments du DOM. Par exemple :

{"language":"text/html","content":"<main id=\"app\" v-cloak>\n <label for=\"music\">Quelle type de musique voulez-vous jouer ?</label>\n <input type=\"text\" id=\"music\">\n <button @click=\"playMusic\">Jouer de la musique</button>\n <span id=\"state\"></span>\n</main>\n\n<script type=\"module\">\n import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'\n createApp({\n mounted() {\n document.getElementById('music').focus()\n },\n methods:{\n playMusic() {\n const style = document.getElementById('music').value;\n const audio = new Audio(style+'.mp3');\n audio.play()\n }\n }\n }).mount('#app')\n\n</script>","filename":"index.html"}

Ici, je récupère la valeur de l'input avec un document.getElementById classique, et je lui mets le focus au chargement de ma page.

Arthur, l'apprenti développeurMais tu ne m'as pas dit que les frameworks permettaient de développer plus rapidement ? On a vu qu'on pouvait se passer de document.getElementById... jusqu'à maintenant. Est-ce qu'il y a des cas où je vais devoir continuer d'utiliser cette syntaxe ?
Ce que je veux te montrer, c'est que la manipulation du DOM avec les méthodes natives reste tout à fait possible avec Vue.JS. Cependant, il est vrai que mon exemple peut être amélioré. Au niveau du formulaire, on verra qu'on a une directive bien pratique pour nous aider. Mais avant, sache qu'on peut sélectionner les éléments du DOM grâce à des références. On en a rarement besoin, mais parfois cela peut être utile.
On peut appliquer un attribut "ref" sur n'importe quel élément de notre container Vue et le sélectionner avec this.$refs... . Voici un exemple avec notre input précédent :

{"language":"text/html","content":"<main id=\"app\" v-cloak>\n <label for=\"music\">Quelle type de musique voulez-vous jouer ?</label>\n <input type=\"text\" id=\"music\" ref=\"musicInput\">\n <button @click=\"playMusic\">Jouer de la musique</button>\n <span id=\"state\"></span>\n</main>\n\n<script type=\"module\">\n import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'\n createApp({\n mounted() {\n this.$refs.musicInput.focus()\n this.$refs.musicInput.value = \"Pop\" // Juste pour l'exemple\n },\n methods:{\n playMusic() {\n const style = document.getElementById('music').value;\n const audio = new Audio(style+'.mp3');\n audio.play()\n }\n }\n }).mount('#app')\n\n</script>","filename":"index.html"}


Arthur, l'apprenti développeurOn a juste à faire this.$refs.ELEMENT et c'est l'équivalent grossier de document.getElementById('ID_ELEMENT') ?
C'est cela même ;). Il n'y a pas de bonne ou de mauvaise façon de faire, ce sont juste deux façons complémentaires, tu choisis celle que tu préfères.


Avant d'attaquer la méthode qui permettra d'enlever des articles de notre sélection, je te laisse un nouvel exemple de l'utilisation des méthodes avec un slider (ou carrousel). Ça manque un peu de CSS, j'en conviens, mais l'idée c'est d'avoir un nouvel exemple.

{"language":"text/html","content":"<main id=\"app\" v-cloak>\n <button @click=\"slide(-1)\">Aller à gauche</button>\n <button @click=\"slide(1)\">Aller à droite</button>\n <template v-for=\"(slide, key) in slides\" :key=\"key\">\n <div v-if=\"index == key\">\n {{ slide }}\n </div>\n </template>\n</main>\n\n<script type=\"module\">\n import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'\n createApp({\n data() {\n return {\n slides: [\n 'Flower',\n 'Animal',\n 'Space'\n ],\n index: 0\n }\n },\n methods: {\n slide(operation) {\n // Vu que % n'est pas le modulo mathématique en JS\n // on doit faire un code un peu compliqué\n // pour gérer les nombres négatifs\n this.index = (((this.index + operation) % this.slides.length) + this.slides.length) % this.slides.length\n }\n }\n }).mount('#app')\n\n</script>","filename":"index.html"}

Arthur, l'apprenti développeurAh merci !
Y'a pas de quoi. Alors, pour reprendre notre mini-projet, on va faire quelque chose de très simple. Je pense que je n'ai pas besoin de t'expliquer le code qui suit.

{"language":"text/javascript","content":"toggleSelection(post) {\n if (this.selection.includes(post)) {\n this.selection.splice(this.selection.indexOf(post), 1)\n } else {\n this.selection.push(post)\n }\n}","filename":"vue.js"}

Arthur, l'apprenti développeurEuh... Je veux bien 2-3 explications
Je vérifie s'il y a le post dans ma sélection, si oui je l supprime, sinon je l'ajoute ;)

Et du coup notre bouton devient :
{"language":"text/html","content":"<button @click=\"toggleSelection(post)\" class=\"text-sm absolute bottom-2\">\n <span v-if=\"selection.includes(post)\">Enlever de ma liste</span>\n <span v-else>Ajouter à ma liste</span>\n</button>","filename":"index.html"}

Comme tu le vois, je change le texte du bouton selon si l'article est dans notre sélection ou pas. Cependant, je ne trouve pas ça très pratique d'avoir un span dans le bouton. J'aimerais changer le texte du bouton dynamiquement, directement dans ma fonction toggleSelection.
Tu as une idée pour faire ça ?

Arthur, l'apprenti développeurAppliquer une référence sur le bouton et modifier son textContent ?
C'est possible en effet, mais attention tu serais obligé de faire des références dynamiques, c'est-à-dire une référence qui change pour chaque article. On aurait quelque chose de ce style, et c'est pas vraiment recommandé et un peu lourd :

{"language":"text/html","content":"<button @click=\"toggleSelection(post)\" class=\"text-sm absolute bottom-2\" :ref=\"'button'+post.id\">\n Ajouter à ma liste\n</button>\n<script type=\"module\">\n import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'\n createApp({\n data() {\n return {\n loading: true,\n posts: [],\n selection: [],\n modal: 'close',\n }\n },\n methods: {\n toggleSelection(post) {\n const ref = 'button'+post.id\n if (this.selection.includes(post)) {\n this.selection.splice(this.selection.indexOf(post), 1)\n this.$refs[ref][0].textContent = 'Ajouter à ma liste'\n } else {\n this.selection.push(post)\n this.$refs[ref][0].textContent = 'Enlever de ma liste'\n }\n }\n },\n })\n</script>","filename":"index.html"}

Ce n'est pas le plus simple. Le plus simple serait de savoir sur quel bouton on a cliqué, c'est à dire sur la cible de l'évènement.

Arthur, l'apprenti développeurComme en Vanilla JS avec event.target ?
C'est exactement ça. Mais pour avoir accès à cet "event", on doit le passer en paramètre dans notre bouton comme suit :

{"language":"text/html","content":"<button @click=\"toggleSelection(post, $event.target)\" class=\"text-sm absolute bottom-2\">\n Ajouter à ma liste\n</button>","filename":"index.html"}

Au final, notre code ressemble donc à ça :

{"language":"text/html","content":"<!DOCTYPE html>\n<html lang=\"fr\">\n\n<head>\n <meta charset=\"utf-8\">\n <title>Mes premiers pas avec Vue 3</title>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n <style>\n [v-cloak] {\n display: none;\n }\n </style>\n</head>\n\n<body>\n <main id=\"app\" v-cloak>\n <div v-show=\"modal == 'open'\"\n class=\"w-1/2 h-1/2 bg-white px-2 py-2 z-10 overflow-y-auto shadow-md rounded fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2\">\n <h2 class=\"text-xl py-4\">Mes articles en attente</h2>\n <a :href=\"`http://monsite.com/${post.id}`\" v-for=\"post in selection\" :key=\"post.id\" class=\"block\">{{\n post.title\n }}</a>\n </div>\n <div v-if=\"loading\">Chargement en cours...</div>\n\n <p v-if=\"posts.length < 1\"\n class=\"max-w-7xl mx-auto border-l-4 border-yellow-400 bg-yellow-50 p-4 text-sm text-yellow-700\">\n Aucun article à afficher.\n </p>\n <div v-else class=\"max-w-7xl mx-auto grid gap-4 grid-cols-3\">\n <article v-for=\"post in posts\" class=\"shadow px-4 pb-8 pt-2 rounded relative\" :key=\"post.id\">\n <a :href=\"`http://monsite.com/${post.id}`\" class=\"mt-4 block\">\n <h2 class=\"text-xl font-semibold text-gray-900\">{{ post.title }}</h2>\n <p class=\"mt-3 text-base text-gray-500\">{{ post.body }}</p>\n </a>\n <button @click=\"toggleSelection(post, $event.target)\" class=\"text-sm absolute bottom-2\">\n Ajouter à ma liste\n </button>\n </article>\n </div>\n <footer v-if=\"selection.length > 0\" class=\"fixed bottom-0 right-2 px-2 py-4 rounded bg-gray-300\">\n <button @click=\"modal='open'\">Voir {{ selection.length > 1 ? 'les' : '' }} {{ selection.length }}\n article{{selection.length > 1 ? 's' : '' }} à lire plus tard</button>\n </footer>\n </main>\n\n <script type=\"module\">\n import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'\n createApp({\n data() {\n return {\n loading: true,\n posts: [],\n selection: [],\n modal: 'close',\n }\n },\n methods: {\n toggleSelection(post, button) {\n if (this.selection.includes(post)) {\n this.selection.splice(this.selection.indexOf(post), 1)\n button.textContent = 'Ajouter à ma liste'\n } else {\n this.selection.push(post)\n button.textContent = 'Enlever de ma liste'\n }\n }\n },\n beforeCreate() {\n this.loading = true;\n },\n created() {\n fetch('https://jsonplaceholder.typicode.com/posts')\n .then((response) => response.json())\n .then((json) => { this.posts = json });\n },\n mounted() {\n this.loading = false\n }\n }).mount('#app')\n\n </script>\n</body>\n\n</html>","filename":"index.html"}

Ce qui serait bien aussi c'est de mettre un style différent à notre bouton selon si la photo est sélectionnée ou pas. C'est ce que nous allons voir juste après avec la gestion du style inline ;) J'ai terminé cette partie
Demander de l'assistance