Préambule

  • L'objectif principal de ce TP est de reprendre le TP n°2 en manipulant les principes de props et d'événements, au travers de la création de nouveaux composants "outils"

 

1°/ La barre de navigation

1.1°/ Objectifs et contraintes

  • Dans les TPs précédents, le changement d'affichage entre les composants VirusesViewShopLoginView et BankAccountView est fait en changeant l'URL dans le navigateur.
  • L'objectif de cet exercice est de créer une barre de navigation sous la forme d'un composant réutilisable.
  • Pour le réaliser, il y a 2 options :
    • soit c'est la barre de navigation qui permet elle-même de suivre une route,
    • soit la barre émet un événement et le composant utilise la valeur de l'événement pour décider quoi faire, en l'occurrence quelle route prendre.
  • L'avantage de la 2ème solution est qu'elle découple totalement la barre de navigation du fait que l'on utilise ou non vue-router. D'un point de vue composition de composants, c'est donc mieux.

 

1.2°/ Mise en place

  • Dans le sous-répertoire components, créer un composant NavBar.vue et copier/coller dedans le code suivant :
<template>
  <div>
  // button list, values/colors from props titles.
  </div>
</template>

<script>
export default {
  name: 'NavBar',
  props: { titles: Array }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

 

  • La props titles (dont la valeur est fournie par le composant parent) est un tableau d'objet au format suivant :
[ {text: "menu1", color: "color1} , {text: "menu2", color: "color2}, ... ]

 

  • Pour créer la liste de boutons dans la partie template, on utilise la balise <button> ... texte ... </button>
  • Il doit y avoir autant de boutons qu'il y a d'objets dans la props titles et leur texte et couleur sont donnés par les champs text et color (comme dans le TP 0)
  • Quand on clique sur le bouton n° X, on doit émettre l'événement menu-clicked avec comme valeur X. Ainsi, le composant parent recevra la valeur 0 quand on clique sur le premier bouton, 1 sur le deuxième, etc.

 

1.3°/ Test

  • Modifier App.vue  :
    • après la balise <div> du début de template, ajouter une instance du composant NavBar,
    • donner en props à cette instance un objet adéquat, afin d'afficher 3 boutons, dont le texte est "Viruses" et "Compte bancaire" et "Login", la couleur étant libre.
    • capturer l'événement menu-clicked et appeler une fonction dans methods avec en paramètre la valeur de l'événement (donc 0, 1 ou 2 si vous avez correctement codé ce qui précède)
    • cette fonction permet de suivre les 3 routes définies, afin de provoquer l'affichage soit du composant VirusesView, soit  BankAccountView, soit ShopLoginView

Remarques :

 

2°/ Une liste avec cases à cocher

2.1°/ objectifs et contraintes

  • L'objectif est de créer un composant permettant d'afficher une liste verticale d'objets JSON de même type, mais en limitant l'affichage à certains champs de ces objets.
  • Chaque item de la liste peut être (de façon optionnelle) :
    • précédé par une case à cocher,
    • suivie par un bouton.
  • Enfin, la liste est suivie par un bouton optionnel.

 

  • Pour cela, le composant doit avoir comme props :
    • data : un tableau d'objets de même type,
    • fields : un tableau donnant le nom des champs du tableau qu'il faut afficher,
    • itemChecked : un booléen qui indique s'il faut une case à cocher devant les items,
    • checked : un tableau de booléens qui indique si une case est cochée ou non,
    • itemButton : un objet au format { show: true/false, text: '....' }. Le booléen indique s'il faut un bouton après les items, et les ... sont le texte de ces boutons.
    • listButton : un objet au format { show: true/false, text: '....' }. Le booléen indique s'il faut un bouton après la liste, et les ... sont le texte de ce bouton.

 

  • Par exemple, prenons le tableau JSON suivant :
[
  {nom:"dupond", prenom:"jean",age:31},
  {nom:"durand", prenom:"pierre",age:35}
]
  • Si on donne ce tableau en entrée au composant, ainsi que le tableau [ 'prenom', 'nom' ] représentant le nom des champs à afficher, alors le composant doit afficher (+ si demandé les cases à cocher et boutons)  :
jean dupond
pierre durant

 


Rappel javascript : on peut accéder aux champs d'un objet grâce à la notation obj['nom_champ']. Par exemple, si j'ai une variable e contenant le premier objet du tableau d'exemple ci-dessus, alors e['nom'] contient 'dupond'.


2.2°/ Mise en place

  • Créer un composant CheckedList avec dedans :
<template>
  <div>
  </div>
</template>

<script>

export default {
  name: 'CheckedList',
  props: {
    data: Array, // les données sources
    fields: Array, // le tableau contenant le nom des champs
    itemCheck: Boolean, // s'il y a des case à cocher
    checked: Array, // le tableau des cases cochées
    itemButton: Object, // l'objet pour les boutons d'items
    listButton: Object, // l'objet pour le bouton de liste
  },
  data : () => {
    return {
    }
  }
}
</script>

<style scoped>
</style>

 

Modifier la partie <template> pour que :

  • le composant affiche sous forme de liste verticale (à vous de choisir l'apparence : paragraphes, liste à puce, table, ...) les objets de la props data. Pour chaque objet, seuls ses propriétés indiquées dans la props fields doivent être affichées (NB : dans l'ordre d'apparition dans fields).
  • si la props itemCheck vaut true, chaque ligne de la liste doit commencer par une case à cocher. La ième case doit être cochée si checked[i] vaut true.
  • si la props itemButton.show vaut true, chaque ligne doit se terminer par un bouton, dont le contenu est indiqué par itemButton.text
  • si la props listButton.show vaut true, un bouton doit apparaître après la liste, dont le contenu est indiqué par listButton.text

En cas d'interaction utilisateur avec les cases et les boutons :

  • chaque fois que l'état d'une case est changé, le composant doit émettre un événement checked-changed, avec comme valeur l'indice de la case à cocher (c.a.d. celui de la ligne)
  • chaque fois qu'un bouton de fin de ligne est pressé, le composant doit émettre un événement item-button-clicked, avec comme valeur l'indice du bouton (c.a.d. celui de la ligne)
  • chaque fois que le bouton après la liste est pressé, le composant doit émettre un événement list-button-clicked.

 

2.3°/ Tests

  • Modifier VirusesView pour que la liste des virus (filtrée si possible) disponibles à l'achat soit affichée grâce au composant CheckedList :
    • pour chaque virus, une ligne de CheckedList doit afficher son nom et son prix,
    • chaque ligne doit commencer par une case à cocher et se terminer par un bouton,
    • la liste des lignes doit être suivi d'une bouton.
  • Quand l'utilisateur clique sur un bouton de fin de ligne, une boîte de dialogue apparaît (cf. méthode alert() de JS) avec un message indiquant le nom de l'item, le nombre en stock et s'il est en vente.
  • Quand l'utilisateur clique sur le bouton d'après liste, une boîte de dialogue apparaît avec un message indiquant le nom de tous les virus dont la case est cochée.

Remarques :

  • La liste filtrée des virus est un déjà un tableau d'objets. On peut donc utiliser ce tableau directement comme valeur de la props data de CheckedList
  • La props checked doit avoir comme valeur un tableau de booléen, fourni par VirusesView. Le problème, c'est que le nombre de virus peut varier en fonction du filtrage. On ne peut donc pas initialiser ce tableau avec une suite de valeurs à false, qui ferait la même taille que le tableau des virus global. De plus, si on sélectionne un virus, par exemple le premier de la liste filtrée, mais qu'ensuite on change le filtrage, il est fort possible que le virus disparaisse de la liste. Dans ce cas, il ne faut surtout pas que le premier de la nouvelle liste apparaisse comme sélectionné, juste parce que c'est le premier. La solution consiste à créer dans VirusesView un tableau des indices des virus sélectionnés, par exemple nommé selected. Si le ième virus de la liste non filtrée est sélectionné, alors selected contient la valeur i. Il faut ensuite créer une fonction computed qui va renvoyer un tableau de booléens de la taille de la liste filtrée et dont les éléments sont à true si le virus correspondant est sélectionné. Quand VirusesView capture un événement checked-changed, il met à jour selected en retrouvant à partir de l'indice dans la liste filtrée quel est l'indice du virus dans la liste non filtrée. La mise à jour de selected provoque le recalcul du tableau de booléens qui sert de props à CheckedList.
  • Pour les 2 autres événements à capturer, il est conseillé d'ajouter des fonctions dans methods.