Vue.js pour les débutants ! (PARTIE 2)

Thibault Jp
11 min readJul 11, 2018

--

Bonjour à tous ! Ceci est la seconde partie de mon tutoriel sur Vue.js, si vous n’avez pas encore suivi la première partie elle se trouve ici ;)

❤️ 💛 NEW COURSE ||||| 2019 |||||| VUE.JS 3 |||||||

https://medium.com/@thibault60000/vue-js-for-beginner-pour-d%C3%A9butants-part-1-2019-en-fr-2a80b2a50e37

NEW COURSE ||||| 2019 |||||| VUE.JS 3 ||||||| 💙 💜

J’en profite pour parler de mon nouveau tutoriel Juillet 2019 : React + GraphQL + StyledComponents + Apollo + Prisma Ici

Transmission de valeur entre composants

( Enfant vers Parent )

Dans la première partie on a vu qu’il est possible d’envoyer des valeurs d’un composant parent à un composant enfant via les props ! Mais qu’en est il dans l’autre sens ?

Ce qu’il faut savoir c’est que les props ne sont pas la solution dans ce cas là ! On va devoir faire appel aux transmissions d’évènements !

Pour avoir une architecture utilisable au mieux, nous allons créer un nouveau composant qui regroupera deux composants :

CarPage.vue (qui regroupera CarList.vue et CarDetails.vue) Cela nous permettra d’avoir une page qui affiche la liste de véhicule et le détail d’un véhicule sélectionné

Commençons par créer la CarPage.vue. Pour créer une structure plus propre, créons un dossier dans notre dossier /src nommé /pages qui contiendra notre fichier.

Je précise que je continue dans ce tutoriel d’utiliser Eslint et qu’il est possible que le moindre espace, guillemet ou point virgule soit important sous peine d’erreurs dans le terminal.

Nous disposons actuellement dans le projet de nombreux fichiers qui n’ont plus forcement grande utilité mais qui sont toujours disponible sur mon GitHub si vous en avez besoin

Pour chaque nouvelle partie comme celle-ci je vais créer de nouveaux fichiers pour ne pas polluer les captures d’écran.

On créer tout simplement un Template qui appelle les deux composants. On oublie pas ensuite d’importer CarPage.vue dans App.vue comme on l’a déjà fait auparavant à la place de Cars.vue

CarPage.vue
App.vue
Arborescence

On créer ensuite notre fichier CarList.vue dans le dossier /components et on lui donne exactement le même code que Cars.vue (ou on peut tout simplement renommer le fichier, comme vous le sentez ! )

Et dans la foulée on peut créer un fichier CarList.vue (vide pour le moment)

Qu’est ce qu’on souhaite faire ici ?

Le but est d’afficher une liste de Voitures, et lorsque l’on clique sur une voiture, on actualise les informations détaillées de la voiture sélectionnée. Pour ce faire, il faut qu’en cliquant sur une ligne de la liste, un événement soit émis par le composant CarList.vue puis capter par CarDetails.vue (au même niveau, mais enfant de CarPage.vue)

Emission d’un événement

Du composant enfant (ici CarList.vue)

On va dans un premier temps vouloir savoir quelle est la voiture sélectionnée, et d’émettre un évènement la concernant.

Pour se faire, on va créer un petit <button> cliquable ainsi que la méthode (methods) associée ! On peut supprimer la méthode addCar() on en aura plus besoin !

CarList.vue

On utilise comme pour l’ajout de véhicule le @click que j’ai laissé pour que vous voyez qu’il est possible d’appeler une fonction avec ou sans arguments.

Dans la méthode vous pouvez voir $emit qui est un service. Un événement ‘selected’ est déclenché et le message associé à cet événement correspond à l’objet car de la ligne sélectionnée !

Bon c’est bien beau tout ça ! Mais on aimerai peut être le récupérer cet événement ensuite ? Et pourquoi pas le transmettre à un autre composant.. ?

Réception d’un événement et transmission à un autre composant

Réception (composant parent) / Transmission (autre composant enfant)

On va d’abord écrire du code, et je vous explique ensuite comment ça fonctionne si vous n’avez pas compris ! On se retrouve dans notre CarPage.vue !

CarPage.vue

Alors là il y a surement plusieurs questions ! (C’est quoi tout ça ? Pourquoi un underscore ? C’est quoi ces directives dans l’appel des composants??)

Premièrement, la façon dont est capter notre événement : Il est capter au niveau HTML, sur l’élément auquel il est rattaché via la directive v-on

La syntaxe étant v-on:<Nom_De_L’evenement>

Dans le cas présent le nom de l’évènement étant selected

Deuxièmement, cela entraine le déclenchement de la méthode (methods) setSelected qui prend en paramètre un event, ici event n’est rien d’autre que notre car (que nous avons eu en bouclant sur l’objet Cars dans CarList.vue). Cette méthode permet d’assigné notre voiture sélectionnée à la variable du composant CarPage selectedCar “

Et troisièmement on passe la valeur au composant enfant (CarDetails.vue) en utilisant un attribut dynamique (dit “bindé”)

Il suffit simplement ensuite d’aller remplir notre composant CarDetails.vue pour qu’il accepte de recevoir un attribut bindé comme props

CarDetails.vue

On obtient ainsi les informations de la voiture lorsque l’on clique sur le bouton ;)

Sur le localhost:8080

Bon ! Ca marche plutôt bien tout ça mais ça ne parait pas très pratique de passer à chaque fois par le parent pour communiquer avec un composant !

Communication directe entre deux composants

Donc sans passer par le parent (ici pas de passage par CarPage.vue!)

Dans cette partie on va faire beaucoup plus simple en passant directement entre les composants et non plus par le parent ! De plus on va éviter de faire transiter tout un objet, mais seulement l’identifiant de la voiture, l’autre composant se chargera d’appeler un service pour récupérer les informations détaillées de la voiture :)

Bon, Comment on émet un événement entre deux composants ?

Si vous avez quelques bonnes notions sur les événements et listener cela ne doit pas être très compliqué ! Le principe ici est que notre composant émette un événement sur un bus d’événements (partagé entre tous les composants). Et les composants voulant récupérés cet événement doivent s’abonner à cet événement pour le capter.

Commencer par créer notre bus d’évnements dans notre fichier principal main.js

main.js

On va maintenant changer notre méthode déclenchée lors du clic sur une des voitures pour ne plus émettre un événement via this.$emit mais via bus.$emit (dans CarList.vue)

Dans notre fichier CarList.vue nous allons donc changer notre méthode SelectCar() comme ceci :

CarList.vue

Nous ne passons plus un car en second paramètre de notre $emit mais bien l’idée de la voiture ! (n’oubliez pas de changer le this en bus ! )

Bon on sait comment envoyer notre nouvel événement maintenant essayons de comprendre comment le récupérer !

Pour que CarDetails.vue puisse récupérer l’ID de la voiture sélectionnée, il faut qu’au moment de la création du composant (hook created()), que le composant s’abonne à l’événement ‘selected’ que nous avons envoyé !
Pour se faire nous devons utiliser le service $on prenant simplement en arguments le nom de l’événement et une fonction (qui permet d’effectuer un traitement à la réception de celui ci)

Dans notre Json-server nous avons créé un bouchon (fake data) relativement simple avec deux objets : Cars et Details
Dans notre exemple nous ferons l’hypothèse que ID de détails est relié à l’ID d’un véhicule (Par exemple : Le véhicule ID = 1 sera lier à ses détails avec Détails ID = 1)

Pour se faire, dans notre CarDetails.vue nous allons importer VueResource et notre bus, supprimer les props pour les remplacer par notre objet Car

Nous appelons également notre objet http en définissant la route du json-server

Et dans notre hook created() nous faisons l’appel à notre service $on pour recevoir l’événement et ainsi récupérer notre objet à la bonne URL

CarDetails.vue
Sur le localhost:8080

Nous voyons ainsi que tout fonctionne correctement avec l’utilisation de bus

Vuex (Ce que Redux est à React, Vuex l’est à Vue)

Store / Gestionnaire d’état / State

On souhaite maintenant pouvoir communiquer entre différents composants pour transmettre un même objet dans chacun des composants

Ce qui est compliqué ici c’est qu’il faut penser à mettre à jour l’objet dans chaque composant qui le capte, il faut également penser au fait que l’objet peut être modifier dans différents endroits de notre application et bien d’autres soucis de ce genre. (du moins si on reste dans notre méthode actuelle consistant à communiquer entre des composants par intermédiaire d’événements et/ou bus d’événements.

Pour remédier à ces problèmes on peut utiliser ce qu’on appelle un Gestionnaire d’état comprenant un objet unique nommé Store qui centralise un état donné pour le partagé entre les différents composants de notre application (un état = un ensemble d’attributs). Le Store possède alors des méthodes pour être manipulé et toute modification d’attributs passe donc par ces méthodes. Documentation d’installation officielle ici

Comment ça s’implémente tout ça ?

C’est assez simple en fait, il faut tout d’abord définir un objet Store qui contient un attribut state (si vous venez de React ça doit vous rappelez Redux) qui regroupe les données constituant l’état de notre store. Le store contient également des méthodes pour modifier ces mêmes données.

Un petit exemple

var store = {
debug: true,
state: {
message: 'Hello World'
},
setMessageAction (newValue) {
this.debug && console.log('setMessageAction with', newValue)
this.state.message = newValue
}
}

Une fois un tel objet créé, il devient accessible par les autres composant

data: {
sharedState: store.state
}

Mais l’utilisation du Store de cette manière n’empêche pas sa modification ailleurs dans l’application, pour remédier à cela, il existe une implémentation plus stricte du Gestionnaire d’état en utilisant Vuex qui est l’implémentation officielle pour Vue.js (Comme Redux l’est pour React)

Commençons avec Vuex !

Il faut avoir en tête qu’avec cette méthode, notre flux de données sera unidirectionnel (contrairement à notre directive v-model par exemple qui est en two-way bindings (bidirectionnelle), tout se fera dans un seul sens

Pour cela nous allons utiliser 3 grands concepts que sont :

  1. Le state : attributs qui constituent l’état partagé entre les composants
  2. Les mutations : méthodes utilisées pour modifier le state (synchrone)
  3. Les actions : méthodes déclenchant une/plusieurs mutation(s) ainsi que potentiellement des appels asynchrones)

Maintenant, l’installation ;)

Pour installer, rien de plus simple, comme d’habitude un petit npm install

npm install vuex --save

Pour démarrer, nous devons créer un nouveau fichier AppStore.js comme ci-dessous :

AppStore.js

Ici, nous faisons simplement appel à Vue, Vuex et VueResource.
Nous créons 4 objets (state, getters, mutations et actions)

Et nous créons notre objet store avec la méthode Store() de Vuex qui prend en paramètre un state, des mutations, des getters et des actions

Il ne faut pas oublier d’exporter le store pour y avoir accès dans nos composants

Si vous n’avez pas oublier, dans notre application on souhaite que notre voiture sélectionnée soit transmisse à tous nos composants. C’est donc cette voiture qui constituera le state de notre application pour l’instant. On va donc modifier notre fichier de la façon suivante pour cela :

  • Créer une action permettant d’appeler notre API details (qui était au préalable dans CarDetails.vue)
  • Créer une mutation qui sera invoquée par l’action que nous venons de créé et qui affectera le résultat obtenu à notre state
  • Et créer un getter pour récupérer les informations de la voiture sélectionnée

Notre store s’alourdit et devient donc ceci (il n’y a rien de nouveau dans les méthodes, nous avons déjà tout vu auparavant)

AppStore.js

Par convention le nom d’une mutation est en MAJUSCULE séparée par un Underscore

Maintenant que notre store est créé, nous devons le déclarer dans notre chère application. Pour cela nous devons importer vuex, importer le store cela nous permettra de faire appel au store dans notre new Vue() comme ci-dessous

main.js

Bon ça fait beaucoup d’information ! Ca change de notre bus d’événements.. :)

Mais ne vous inquiétez pas ! Il ne reste plus qu’à manipuler le store dans nos composants maintenant :)

Allons voir notre composant CarList.vue !

On va surtout changer notre partie script, en appelant l’action selectCar() du store dans la méthode selectCar()de notre composant au lieu d’émettre un événement comme auparavant.
On change notre bus.$emit en this.$store.dispatch

CarList.vue

Sachez qu’il est également possible de passer par une tout autre syntaxe grâce au handler mapActions() de Vuex

CarList.vue (alternative)

Sachez que les “” sont ce que l’on appelle un opérateur de décomposition

Par exemple si vous disposez d’une fonction

function somme(x,y,z){
return x + y + z;
}

Et que vous déclarez un tableau comme suit :

const number = [ 1, 2, 3];

Alors l’appelle à

console.log(sum(...numbers));

Renverra 6 car … permettra de décomposer un tableau en plusieurs arguments dans un appel de fonction par exemple…

La dernière étape : CarDetails.vue !

Reprenons notre fichier CarDetails.vue et supprimant ce qui concerne la réception d’un événement et son traitement en faisant plutôt appel au service de détail d’un citoyen qui est désormais à la charge de notre Store !

CarDetails.vue

On se retrouve simplement avec ça maintenant.
On importe Vue et Vuex, et dans notre computed on appelle le Getter.
Mais si on essaye d’expliquer un peu plus les changements voila ce qu’on obtient :

  • On constate que le hook created a disparu. Il ne sert plus a rien
  • On constate que les data ont disparues au profit d’u getter SelectedCar du store On utilise “car” pour ne pas toucher à notre Template (car.id, car.immatriculation…) et on récupère ainsi notre aspect two-way-bindings !

Un problème persiste !

A ce stade, nous avons toujours la possibilité d’accéder directement au state pour le modifier (CE QUE NOUS NE VOULONS ABSOLUMENT PAS)

Comment? Par exemple si on ajoute dans le composant CarList.vue une méthode pour mettre à jour le name de la voiture sélectionné en mettant un @click qui lance this.$store.state.car.name = newName

Le résultat est super simple à ce niveau là.. Il suffit simplement d’ajouter dans notre création du store le paramètre strict et de le mettre à true

AppStore.js

Ce mode strict doit être a false en production (performance l’oblige)

Sur le localhost:8080

C’est tout pour cette partie !
Dans la prochaine partie nous verrons comment fonctionne le routing sur Vue.js et comment utiliser le plugin chrome ;)

LA PARTIE 3 ICI

--

--

Thibault Jp

Front-End Developer - UX-UI Designer — Amiens FRANCE