- Les démonstrations de ce TD se basent sur les sources disponibles [ ici ].
- Attention, seul le répertoire src est dans l'archive. Il faut donc créer un projet avec vue-cli et remplacer son répertoire src.
1°/ Mettre en place un écouteur d'événement : v-on
- En JS basique, on met en place un écouteur d'événement sur une balise grâce aux attributs du type onXXX, par exemple onclick.
- La valeur de cet attribut est généralement du code javascript simple, par exemple un appel à une fonction qui va gérer l'événement.
- Vuejs propose la directive v-on pour obtenir le même résultat.
- La différence est que v-on est paramétrable par le type d'événement que l'on veut écouter et que sa valeur est une expression javascript (comme pour les moustaches, v-bind, ...) accédant directement aux données/méthodes (cf. ci-dessous) de l'instance de vue.
- La syntaxe est : v-on:nom_événement="expression JS"
- Comme pour v-bind, il existe un raccourci d'écriture : @nom_événement="expression JS"
Remarque : si on met en place une capture d'événement et que ce dernier n'est jamais généré, cela n'a aucun impact sur l'exécution.
- Copier le fichier TD4Demo1.vue dans TD4Demo.vue
- lancer npm run serve puis visualiser le résultat dans le navigateur (par ex. http://localhost:8080)
- Montrer le code de TD4Demo1.vue. On constate que v-on capture l'événement clic sur le bouton et appelle doInc() qui modifie la valeur de value.
- Cliquer sur le bouton : la valeur affichée change automatiquement.
- Remplacer v-on: par un @ => ça fonctionne également
- Problème avec v-model : le champ de saisie est considéré comme du texte, même si on tape un nombre. Mettre A dans le champ de saisie et cliquer sur le bouton : un A est ajoutée après la valeur affichée. Normal, on a fait une concaténation de chaîne. La solution à ce problème est donnée dans la section suivante.
2°/ Récupérer l'événement
- Il est relativement fréquent que l'écouteur d'événement appelle une fonction prenant en paramètre une valeur tirée du composant ayant généré l'événement.
- Par exemple, lorsqu'un champ de saisie est validé (événement change), on veut récupérer la valeur de celui-ci pour la donner en paramètre à la fonction qui va traiter l'événement.
- Pour cela, il faut récupérer l'objet événement. Vuejs met à disposition une variable globale $event, référençant l'objet événement courant.
- Grâce à cette variable, on a notamment accès au composant cible de l'événement : $event.target.
- On peut ensuite accéder aux attributs, par exemple pour un champ de saisie : $event.target.value.
- Copier le fichier TD4Demo2.vue dans TD4Demo.vue
- lancer npm run serve puis visualiser le résultat dans le navigateur (par ex. http://localhost:8080)
- Montrer le code de TD4Demo2.vue. On constate que l'on utilise pas v-model dans la champ de saisie, mais une capture de l'événement change, qui appelle la fonction checkInc() avec en paramètre la valeur du champ de saisie.
- Cliquer sur le bouton : la valeur affichée change automatiquement.
- Taper 3 dans le champ de saisie puis sur entrée : un message de confirmation s'affiche dans la console. L'incrémentation est maintenant de 3.
- Taper aaa dans le champ de saisie puis sur entrée : un message d'alerte s'affiche dans le console et rien ne se passe.
- Cliquer de nouveau sur le bouton : le champ de saisie remet la valeur initiale et l'incrémentation s'effectue avec cette valeur.
Problème : selon les actions, le champ de saisie va être actualisé ou non en cas de saisie incorrecte.
- Recharger la page et taper 3 dans le champ de saisie => ok.
- Taper bbb dans le champ de saisie puis cliquer directement sur le bouton => le champ de saisie est actualisé et affiche 3 puis l'incrémentation se fait.
- Taper 0 dans le champ de saisie puis cliquer sur le bouton => rien en change puisque l'incrémentation ne change pas la valeur.
- Taper aaa dans el champ de saisie puis cliquer sur le bouton => le champ de saisie ne réaffiche pas 0. Normal car rien n'est mis à jour dans la page.
Conclusion : il faut parfois "forcer" le réaffichage d'une variable qui n'a pas changé de valeur.
- solution simple : utiliser 2 instructions consécutives pour changer la valeur puis la remettre à l'initiale.
- Illustration : décommenter les deux lignes qui changent incVal => le champ de saisie est forcé de se réactualiser.
3°/ Émettre un événement
- Vuejs propose une fonction $emit() qui permet de générer explicitement un événement, avec un nom et une valeur fixés par le programmeur.
- Il est donc possible de créer de événements différents de ceux définis dans la norme HTML/JS.
- On peut utiliser cette fonction aussi bien dans la partie <template> que <script> (dans ce dernier cas, ne pas oublier de prefixer avec this.)
- La syntaxe est : $emit("nom_evenement", valeur)
- Pour capturer cet événement dans le composant parent, il suffit d'utiliser @nom_evenement.
ATTENTION :
- contrairement aux événements HTML classiques qui sont remontés de fils en père, jusqu'à atteindre l'objet windows, les événements créés avec vuejs ne sont remontés qu'au père du composant qui a émis l'événement.
- si cet événement doit remonter encore d'un niveau, il faut que le père réémette explicitement l'événement en utilisant lui aussi $emit.
- Copier le fichier TD4Demo3.vue dans TD4Demo.vue
- lancer npm run serve puis visualiser le résultat dans le navigateur (par ex. http://localhost:8080)
- Montrer le code de TD4Demo3.vue. On constate que le clic sur le 2ème bouton provoque l'emission d'un événement myEvent, avec comme paramètre un objet contenant value et incVal (NB : on pourrait bien entendu renommer les champs, par exemple { myval : value, myinc: incVal} )
- Montrer le code de App.vue. On constate que l'on capture l'événement myEvent et que l'on appelle avec l'objet $event en paramètre. Dans le cas présent, c'est l'objet qui est émis grâce à $emit().
- Incrémenter plusieurs fois la valeur puis cliquer sur le 2ème bouton. Dans la console, on voit apparaître le contenu de l'objet, signe que le composant parent a bien capturé l'événement.
Raccourci d'écriture : si l'on écrit <TD4Demo3 @myEvent="doLog" />, cela semble incorrect : ce n'est pas un appel à fonction, juste un nom de fonction. Pourtant, cela fonctionne très bien. En effet, dans ce cas, vuejs va comprendre qu'il faut appeler doLog() ET par défaut passer $event en paramètre. ATTENTION, cela ne fonctionne pas en écrivant doLog().
- On a vu qu'un composant fils n'a aucun moyen de modifier directement la valeur de ses props, car leur valeur est donnée par le composant parent.
- Cependant, il est relativement fréquent qu'une interaction utilisateur dans le fils implique un changement de la valeur de la props.
- Prenons par exemple, un composant fils générique qui affiche une liste d'item sélectionnables grâce à une case à cocher.
- Ce composant a comme props un tableau d'objets représentant les items de la liste, et un tableau de booléen indiquant quels items sont sélectionnés. Ces deux tableaux sont fournis par le composant père.
- Il va de soi que si l'utilisateur coche ou décoche une case, il faut mettre à jour ce tableau de booléen.
- Malheureusement, ce n'est pas possible directement.
- En revanche, c'est possible indirectement grâce aux événements : il suffit que le fils envoie à son père un événement indiquant quelle case a changé d'état.
- Le père capture l'événement et met à jour son tableau de booléen qu'il donne à son fils comme props. De ce fait, la props du fils est mise à jour.
- Copier le fichier TD4Demo4.vue dans TD4Demo.vue
- lancer npm run serve puis visualiser le résultat dans le navigateur (par ex. http://localhost:8080)
- Montrer le code de TD4Demo4.vue. C'est le composant Selector qui permet d'afficher la liste avec cases à cocher. On remarque également que le tableau passé en props contient aussi bien le texte des items que s'ils sont sélectionnés. C'est plus simple que de passer 2 tableaux en props.
- Montrer le code de Selector.vue. Le principe pour faire remonter un changement de sélection d'un item est simple : on capture l'événement clic (comme dans les exemples précédents) et on utilise $emit pour envoyer au père l'indice de la case ou l'item cliqué. Cet indice est celui tiré du v-for utilisé pour afficher les items.
- On remarque dans TD4Demo4 que certains items sont déjà sélectionnés, ce qui correspond bien au fait que les cases sont cochées lors de l'affichage du composant.
Remarque : dans un navigateur, un clic sur une case change visuellement son état (sauf blocage explicite). Ce n'est donc pas une preuve que la props de Selector a été mise à jour. En revanche, quand on clique sur le texte d'un item, la case change, ce qui apporte la preuve voulue.
- Supposons 3 composants avec A le composant principal, B et C des composants fils.
- Comment faire pour qu'une action dans B implique une modification dans C ?
- Il serait bien pratique de pouvoir émettre des événements avec $emit d'un composant père vers un composant fils.
- Malheureusement, c'est impossible. Il est également impossible d’émettre un événement entre des composants "frères" ou "cousins".
- La seule solution viable avec les mécanismes de base de vuejs est d'utiliser le principe des props, abordés dans le TD 3.
- Pour donner l'idée principale :
- le composant A sert de point de stockage des données "communes" à B et C, ainsi que des méthodes permettant de modifier ces données.
- pour simplifier, on suppose que le composant B propose des actions sur ces données, alors que C affiche ces données.
- pour que C ait accès aux données, A va les partager sous la forme d'une props, c'est-à-dire d'une variable dans C qui va s'ajouter à celle de data et computed, mais dont la valeur est fournie par A.
- quand une action est déclenchée dans B, ce dernier émet un événement que A capture, puis il appelle une fonction modifiant les données.
- comme ces données sont partagées avec C, toute modification dans A va être répercutée dans C, donc son affichage va être réactualisé.