Open menu
Préambule
 
  • 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.

 
Démonstration :
  • Copier le fichier TD3Demo1.vue dans TD3Demo.vue
  • lancer npm run serve puis visualiser le résultat dans le navigateur (par ex. http://localhost:8080)
  • Montrer le code de TD3Demo1.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.

 

Démonstration :
  • Copier le fichier TD3Demo2.vue dans TD3Demo.vue
  • lancer npm run serve puis visualiser le résultat dans le navigateur (par ex. http://localhost:8080)
  • Montrer le code de TD3Demo2.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.

 

Démonstration :
  • Copier le fichier TD3Demo3.vue dans TD3Demo.vue
  • lancer npm run serve puis visualiser le résultat dans le navigateur (par ex. http://localhost:8080)
  • Montrer le code de TD3Demo3.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().

  

 

4°/ Gestion de la propagation des événements

  • Quand un événement a lieu sur une balise, il est possible que le navigateur ait un comportement par défaut qu'il applique quand bien même on appelle notre propre fonction de gestion de l'événement.
  • Par exemple, quand on clique sur une case à cocher, son visuel change en ajoutant/supprimant la croix à l'intérieur.
  • Parfois, ce comportement par défaut est une gêne et on désire l'empêcher. En JS, il suffit d'appeler la méthode preventDefault() de l'événement.

 

  • Une fois que l'événement est traité au niveau de la balise, il est propagé à la balise qui englobe la première. Si la seconde écoute l'événement, elle appellera également la fonction de gestion. Ainsi de suite en remontant jusqu'à <body>.
  • Cette propagation est parfois génante et on désire l'empêcher. En JS, il suffit d'appeler la méthode stopPropagation() de l'événement.

 

  • Vuejs permet de reproduire ces 2 mécanismes et d'appeler les 2 méthodes mentionnées grâce à des "modificateurs" d'événements :
    • pour empêcher l'appel du gestionnaire par défaut, on ajoute .prevent derrière le nom de l'événement. Par exemple @click.prevent.
    • pour stopper la propagation à la balise parente, on ajoute .stop derrière le nom de l'événement. Par exemple @click.stop.
    • A noter que l'on peut les chaîner : @click.prevent.stop.
  • Il existe d'autres modificateurs, notamment ceux qui permettent de restreindre le déclenchement de l'événement à l'appui sur une/plusieurs touches (cf. API Vuels)

 

 

  • Quand une application utilise une hiérarchie de composants à plus de 2 niveaux, il est parfois nécessaire de propager explicitement des événements pour les faire remonter sur plusieurs niveau de la hiérarchie.
  • En effet, contrairement aux événements classiques (click, mouseover, ...) qui sont par défaut propagés implicitement, les événements générés avec $emit() doivent l'être explicitement.
  • Pour cela, il suffit de capturer l'événement et d'utiliser de nouveau $emit() pour le retransmettre au composant parent suivant, qui va lui-même capturer le nouvel événement, etc.
 
Démonstration :
  • Copier le fichier TD3Demo4.vue dans TD3Demo.vue
  • lancer npm run serve puis visualiser le résultat dans le navigateur (par ex. http://localhost:8080)
  • Montrer le code de TD3Demo4bis.vue. On constate que le clic sur le 2ème bouton provoque l'emission d'un événement myEvent.
  • Montrer le code de TD3Demo4.vue. On constate que l'événement myEvent est capturé et cela provoque l'appel de la méthode doLog() qui regénère l'événement grâce à $emit().
  • Montrer le code de App.vue. On constate que l'on capture l'événement myEvent provenant de TD4Demo4, d'où le 2ème message affiché dans la console.
  • Enlever la capture de myEvent dans TD4Demo4 => App ne reçoit pas l'événement. On constate qu'il faut effectivement propager l’événement de parent en parent, sans avoir la possibilité de "sauter" des niveaux.
  • Cliquer sur la case à cocher => pas de check. Normal car le modificateur .prevent empêche d'appeler les fonctions de gestion par défaut de l'événement au niveau de la case à cocher.
  • Passer la souris sur le texte du bas => pas de changement de couleur car la balise <div> ne reçoit pas l'événement mouseover, à cause du modificateur .stop. La fonction toggleColor() ne peut donc pas être appelée. En revanche, passer sur la première ligne ne stop pas la propagation donc le texte change de couleur.
 
5°/ Quid d'une redescente des événements ?
  • 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 5.
  • 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é.
  • Un exemple sera donné dans le TD 5.