Développer une application web avec Vue.js et Vue CLI, mise en œuvre des concepts via un exemple (partie 2)
- Présentation de l’exemple : PollDLE
- Création d’un projet Vue.js
- Modèle et vue
- Templating avec Vue.js
- Composant avec Vue.js
- Invocation de service REST
- Routage avec Vue.js
- Conclusion et remerciements
- Ressources
L’objectif de cet article en plusieurs parties est de vous présenter le framework web JavaScript Vue.js en se focalisant sur les principaux concepts au travers d’un exemple unique.
Les différentes parties de cet article sont détaillées ci-dessous :
- généralités sur les frameworks web JavaScript et présentation de Vue.js ;
- mise en œuvre des concepts de Vue.js ;
- déploiement d’une application web développée avec Vue.js.
Lors de l’écriture de l’article, nous avons utilisé la version 2 de Vue.js et la version 4 de Vue CLI.
Cette deuxième partie présente les principaux concepts de Vue.js au travers d’un exemple.
Le plan est le suivant :
- présentation de l’exemple qui servira de fil rouge ;
- création du squelette du projet PollDLE avec Vue.js et Vue CLI ;
- initialisation des modèles et des vues pour les différents composants ;
- mise en place des bindings entre les modèles et les vues via l’utilisation du templating (interpolation et les directives) ;
- utilisation des propriétés calculées (computed) et des observateurs (watch) ;
- détail du fonctionnement d’un composant pour savoir développer, instancier et dialoguer avec un composant ;
- invocation d’un service web Rest pour modifier ou retourner la valeur d’un modèle ;
- paramétrage du système de routage pour les changements de valeurs d’URL.
Les codes source pour les exercices sont disponibles sur le dépôt Git suivant : https://github.com/mickaelbaron/vuejs-polldle-tutorial-src (pour récupérer le code, faire : git clone <https://github.com/mickaelbaron/vuejs-polldle-tutorial-src>
).
Présentation de l’exemple : PollDLE
L’exemple qui nous servira de fil rouge est appelé PollDLE pour Poll (Sondage) et la dernière partie de Doodle (un outil de planification très simple d’emploi). Il s’agira donc d’une application pour créer un sondage (un titre et des options), voter à un sondage (un choix possible) et afficher les résultats d’un sondage.
La couche client (front-end) sera réalisée avec Vue.js et Bootstrap pour le CSS tandis que la couche serveur (back-end) est écrite en Java. Pour cette dernière partie, nous ne détaillerons pas sa mise en place, elle est déjà codée. Elle s’appuie sur la spécification MicroProfile en utilisant les composants JAX-RS et CDI et en s’appuyant sur l’implémentation fournie par KumuluzEE.
Concernant la partie graphique, il y aura trois écrans pour la création, le vote et la consultation d’un sondage.
Ci-dessous est présenté l’écran pour la création d’un sondage. Un premier champ de texte permet de saisir le titre du PollDLE. Un second champ de texte permet de saisir une nouvelle option d’un PollDLE. La modification d’une option n’est pas autorisée. Pour cela il faudra la supprimer via les boutons situés sur la droite de chaque option d’un PollDLE. Un bouton intitulé « Clear All PollDLE Options » permet de supprimer l’intégralité des options d’un PollDLE. Le titre et le nombre d’options d’un PollDLE sont résumés sur le panneau intitulé « Summary of your PollDLE ». Enfin pour valider la création d’un PollDLE, il suffira de cliquer sur le bouton « Create PollDLE » qui redirigera automatiquement l’utilisateur vers le deuxième écran.
Ci-dessous est présenté l’écran pour voter à un sondage. Cet écran reprend le titre du PollDLE et transforme les différentes réponses sous la forme de bouton radio à choix unique. Un bouton « Vote » permet de valider son vote. Veuillez remarquer dans la barre d’adresse, la valeur 1
qui correspond à l’identifiant fonctionnel donné à ce PollDLE. Pour accéder au vote d’un PollDLE, il suffira donc de spécifier dans la barre d’adresse son identifiant.
Ci-dessous est présenté l’écran pour l’affichage des résultats d’un sondage. Le résultat est présenté sous deux formes. La première sous forme d’un graphique circulaire et la seconde via une liste avec les différentes options et le nombre de résultats obtenus. Pour accéder à cette page, il suffira de spécifier dans la barre d’adresse l’identifiant du PollDLE suivi de result
.
Dans la suite, nous donnerons les codes HTML et JavaScript des différents composants au fur et à mesure. L’intérêt de cet article n’est pas d’apprendre à construire une interface graphique, mais de comprendre comment rendre dynamique une interface avec Vue.js.
Avant chaque concept présenté, nous fournirons un état du code (via des répertoires de la forme polldle-vue-x) afin que vous puissiez directement tester par vous-même.
Création d’un projet Vue.js
Nous allons montrer dans cette section comment créer un projet Vue.js en utilisant Vue CLI et nous examinerons le squelette du projet obtenu après création.
Création d’un projet Vue.js avec l’outil Vue CLI
- Pour découvrir les possibilités de Vue CLI, ouvrir un terminal et saisir la commande suivante :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ vue -h
Usage: vue <command> [options]
Options:
-V, --version output the version number
-h, --help output usage information
Commands:
create [options] <app-name> create a new project powered by vue-cli-service
add [options] <plugin> [pluginOptions] install a plugin and invoke its generator in an already created project
invoke [options] <plugin> [pluginOptions] invoke the generator of a plugin in an already created project
inspect [options] [paths...] inspect the webpack config in a project with vue-cli-service
serve [options] [entry] serve a .js or .vue file in development mode with zero config
build [options] [entry] build a .js or .vue file in production mode with zero config
ui [options] start and open the vue-cli ui
init [options] <template> <app-name> generate a project from a remote template (legacy API, requires @vue/cli-init)
config [options] [value] inspect and modify the config
upgrade [semverLevel] upgrade vue cli service / plugins (default semverLevel: minor)
info print debugging information about your environment
Run vue <command> --help for detailed usage of given command.
- Pour obtenir des informations sur votre environnement de développement et s’assurer que tout est correctement installé (npm, et Vue CLI), saisir la ligne commande suivante :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ vue info
Environment Info:
System:
OS: macOS 10.15.5
CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Binaries:
Node: 14.2.0 - /usr/local/bin/node
Yarn: 1.22.4 - /usr/local/bin/yarn
npm: 6.14.4 - /usr/local/bin/npm
Browsers:
Chrome: 83.0.4103.116
Edge: Not Found
Firefox: 77.0.1
Safari: 13.1.1
npmGlobalPackages:
@vue/cli: 4.4.5
- Nous allons créer notre premier projet avec Vue CLI en mode console. Depuis la racine du dossier vuejs-polldle-tutorial-src (obtenu en récupérant les codes source depuis le dépôt https://github.com/mickaelbaron/vuejs-polldle-tutorial-src), saisir la ligne de commande suivante, une série de questions vous seront posées.
1
$ vue create polldle-vue-00
- Sélectionner le second élément afin de choisir manuellement le paramétrage.
1
2
3
4
Vue CLI v4.4.5
? Please pick a preset:
default (babel, eslint)
❯ Manually select features
- Sélectionner les plugins Babel (un compilateur JavaScript permettant d’utiliser des syntaxes récentes du langage qui seront traduites en JavaScript compréhensible par la plupart des versions des navigateurs) et Linter / Formatter (un outil d’analyse statique du code JavaScript permettant de détecter des erreurs avant l’exécution et des problèmes de style).
1
2
3
4
5
6
7
8
9
10
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◉ Babel
◯ TypeScript
◯ Progressive Web App (PWA) Support
◯ Router
◯ Vuex
◯ CSS Pre-processors
◉ Linter / Formatter
◯ Unit Testing
◯ E2E Testing
- Pour l’option Linter / Formatter, choisir le premier élément afin d’afficher la moindre erreur détectée. C’est assez contraignant au début (moindre espace en trop est une erreur), mais quel plaisir d’avoir un code propre qui respecte les conventions de codage. Le Linter utilisé par défaut sera ESLint.
1
2
3
4
5
? Pick a linter / formatter config:
❯ ESLint with error prevention only
ESLint + Airbnb config
ESLint + Standard config
ESLint + Prettier
- Choisir le premier élément pour lancer le Linter à chaque sauvegarde d’un fichier JavaScript.
1
2
3
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◉ Lint on save
◯ Lint and fix on commit
- Choisir le second élément pour stocker les informations spécifiques de Babel et ESLint dans le fichier package.json. À noter que même avec ce choix, un fichier babel.config.js sera quand même créé.
1
2
3
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.?
In dedicated config files
❯ In package.json
- Pour savoir si vous souhaitez que vos précédents choix soient considérés par défaut pour les prochaines créations de projets.
1
? Save this as a preset for future projects? (y/N)
- Choisir le gestionnaire de packages (Yarn ou NPM) à utiliser par défaut pour télécharger les dépendances. Cette option n’est disponible que si vous disposez de plus d’un gestionnaire de packages installé sur votre système. Ayant utilisé par la suite NPM, je vous demanderai de sélectionner le second choix.
1
2
Use Yarn
❯ Use NPM
- La création de votre projet va se dérouler.
1
2
3
4
5
Vue CLI v4.4.5
✨ Creating project in /Users/baronm/vuejs-polldle-tutorial-src/polldle-vue-00.
⚙️ Installing CLI plugins. This might take a while...
⸨ ░░░░░░░░░░░⸩ ⠇ diffTrees: sill install generateActionsToTake
La création de ce projet aurait pu être effectuée via Vue UI au travers d’une interface web. Les options sont les mêmes, seule l’interface est plus agréable.
Inventaire des fichiers générés
Intéressons-nous à détailler les différents fichiers et répertoires qui ont été générés lors de la précédente section.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
polldle-vue-00
├── README.md
├── babel.config.js
├── node_modules/
├── package-lock.json
├── package.json
├── public/
│ ├── favicon.ico
│ └── index.html
└── src/
├── App.vue
├── assets
│ └── logo.png
├── components
│ └── HelloWorld.vue
└── main.js
Pour les développeurs qui utilisaient Vue CLI 2, vous remarquerez la disparition du répertoire config. En effet, depuis Vue CLI 3, il n’y a plus de fichier de configuration. Pas d’inquiétude, vous pourrez toujours ajouter des informations de configuration. Nous en parlerons dans la partie 3 qui traite du déploiement.
Le fichier README.md décrit les différentes commandes à utiliser avec npm. Nous y reviendrons dans la section suivante quand nous expliquerons comment tester le projet.
Le fichier babel.config.js est un fichier de configuration pour le transpileur Babel. Ce dernier permet de générer du code JavaScript exécutable sur n’importe quel navigateur web. L’avantage est de pouvoir utiliser des versions récentes de JavaScript comme par exemple ES2015+.
Le fichier package.json est donné en exemple ci-dessous. Des métadonnées sont utilisées pour décrire le projet : name
et version
. Des scripts npm sont définis pour tester, construire la version finale et vérifier la qualité du code. La clé dependencies
sert à préciser les bibliothèques utilisées par le projet alors que la clé devDependencies
sert à préciser les bibliothèques utilisées pour le développement. Au niveau des numéros de version, le caractère ^
est utilisé. Il indique que npm est autorisé à mettre à jour le numéro de version de la bibliothèque par des versions mineures sans changer de version majeure (4.4.0 vers <4.4.0). On peut également trouver le caractère ~
dans certains cas. Il indique que npm est autorisé à mettre à jour le numéro de version sans changer de version mineure (4.4.0 <4.5.0).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"name": "polldle-vue-00",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
...
"vue": "^2.6.11"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.4.0",
"@vue/cli-plugin-eslint": "~4.4.0",
"@vue/cli-service": "~4.4.0",
"vue-template-compiler": "^2.6.11"
}
... // Informations de configurations des plugins ESLint, PostCSS...
}
Le répertoire node_modules contient l’ensemble des modules nécessaires pour la construction du projet. Ce répertoire est obtenu automatiquement en exécutant le script $ npm install
. L’outil npm se base alors sur le fichier package.json pour télécharger les modules directs et transitifs. Par comparaison, c’est très ressemblant à Maven de l’univers Java où pom.xml correspond au fichier package.json.
Le répertoire public est utilisé pour stocker les fichiers statiques HTML. Le fichier index.html est le point d’entrée de votre application (voir ci-dessous). Tout le code que vous allez développer sera injecté dans <div id="app"></div>
.
- De façon à intégrer la bibliothèque CSS Bootstrap à toute l’application, ajouter le lien CDN après la balise de titre.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Polldle UI Vue.JS</title>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"
integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
Le répertoire src contient le code Vue.js à proprement parler.
Fichier main.js
Le fichier main.js sert à configurer notre projet. Il précise les composants à utiliser (import App from './App.vue'
), initialiser des variables globales (Vue.config.productionTip = false
) et précise où le rendu doit être effectué ($mount('#app'))
).
Ce fichier est en quelque sorte le point central de l’application.
1
2
3
4
5
6
7
8
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
Fichier App.vue
Le fichier App.vue est le premier composant de votre application qui va contenir tous les autres composants. Il est en quelque sorte le composant racine d’une application de type Single-Page application. Tout est localisé à l’intérieur de ce composant. Comme il s’agit d’un composant, nous détaillerons son contenu plus tard.
Répertoire assets
C’est dans ce répertoire que vous déposerez vos ressources (images, vidéos et fichiers à télécharger).
Répertoire components
Ce répertoire contiendra tous les composants que vous allez développer. Tous les fichiers porteront l’extension *.vue
.
Tester le projet généré
- Pour tester ce nouveau projet, se déplacer à la racine du dossier polldle-vue-00 et exécuter la ligne de commande suivante :
La commande
$ npm run serve
est un alias défini dans package.json qui lancevue-cli-service serve
.
1
2
3
4
5
6
7
8
9
10
11
12
13
$ npm run serve
...
DONE Compiled successfully in 2977ms
App running at:
- Local: http://localhost:8080/
- Network: http://WWW.XXX.YYY.ZZZ:8080/
Note that the development build is not optimized.
To create a production build, run npm run build.
- Ouvrir un navigateur est saisir l’URL suivante : http://localhost:8080/.
Tout au long de cet article et à chaque fois qu’il vous sera demandé de compléter des fichiers dans un répertoire de la forme polldle-vue-x, pensez à faire $ npm install
pour installer les modules et $ npm run serve
pour démarrer l’exécution en mode développement.
Si un problème de ce genre se produit : npm WARN Local package.json exists, but node_modules missing, did you mean to install?, assurez-vous d’avoir fait un
$ npm install
pour télécharger l’ensemble des modules nécessaires. Les fichiers téléchargés seront déposés dans le répertoire node_modules.
Modèle et vue
Nous vous invitons à vous positionner dans le répertoire polldle-vue-01 pour profiter des codes qui vont illustrer cette section. Pensez à faire
$ npm install
pour installer les modules et$ npm run serve
pour démarrer l’exécution en mode développement.
Nous allons initialiser les différents modèles et vues des composants CreatePolldle et FooterPolldle de l’application PollDLE sans effectuer aucun binding, c’est-à-dire sans relier les vues aux modèles et inversement. En effet, nous voulons montrer qu’un composant est constitué d’une partie vue (HTML classique) et d’une partie modèle (JavaScript) et que l’intérêt de Vue.js est de fournir un ensemble d’outillages (les différentes sections qui vont suivre) pour rendre dynamique l’interface graphique. Par ailleurs, nous ne nous attarderons pas sur les spécificités d’un composant (communication entre des composants ou son cycle de vie) puisque nous y reviendrons plus tard dans une section dédiée.
À cette étape voici le contenu du répertoire src/components.
1
2
3
4
5
6
polldle-vue-01
...
├── components
│ ├── CreatePolldle.vue
│ └── FooterPolldle.vue
...
Le fichier CreatePolldle.vue concerne le composant décrivant l’écran de création d’un PollDLE et le fichier FooterPolldle.vue désigne le composant pour le bas de page de l’application. Les autres composants seront étudiés quand nous aborderons le concept de composant.
Le fichier CreatePolldle.vue contient le strict minimum et fait apparaitre la partie vue (localisée dans le contenu de la balise <template>
) de la partie modèle (localisée dans le contenu de la balise <script>
).
1
2
3
4
5
6
7
<template />
<script />
<style>
...
</style>
- Compléter la partie vue
<template>
, en ajoutant le code HTML suivant :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<template>
<div class="container">
<!-- Titre + description -->
<h1>PollDLE</h1>
<h2>Voting done simply in real-time</h2>
<!-- PollDLE name -->
<div class="row">
<div class="col">
<input type="text" class="large-input mx-auto d-block" placeholder="Add your question here">
</div>
</div>
<h3>Add your PollDLE options</h3>
<div class="row">
<div class="col">
<input type="text" placeholder="Polldle Option" class="large-input mx-auto d-block">
</div>
</div>
<div class="row">
<div class="col">
<button type="button" class="clear-button btn-lg btn-danger mx-auto d-block" >Clear all PollDLE Options</button>
</div>
</div>
<!-- PollDLE option -->
<div class="row justify-content-center"></div>
<!-- Button Action -->
<div class="row">
<div class="col">
<button type="button" class="validate-button btn-lg btn-primary mx-auto d-block">Create PollDLE</button>
</div>
</div>
<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Summary of your PollDLE</h4>
<hr>
<p>
The question is: <strong>TODO</strong>
</p>
<p>Number of PollDLE options: TODO</p>
</div>
<div class="error-message alert alert-danger" role="alert">TODO</div>
</div>
</template>
<script>
...
</script>
<style>
...
</style>
Vous remarquerez pour ceux qui utilisent la bibliothèque Bootstrap les styles spécifiques tels row
et col
. À ce niveau, il s’agit d’une interface graphique développée en HTML des plus classiques.
- Compléter le début de la partie modèle
script
, en ajoutant le code JavaScript suivant correspondant aux propriétés du modèle.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
... // code précédent
</template>
<script>
export default {
name: "CreatePolldle",
data() {
return {
question: "",
newPolldleOptionText: "",
polldleOptions: [],
errorMessage: "",
buttonShown: false
};
},
... // code présenté ci-après
}
</script>
...
La partie modèle est définie dans la fonction data()
qui retourne un ensemble de propriétés. Dans le cas présenté ci-dessus, nous retrouvons les propriétés suivantes :
question
: pour le titre du PollDLE ;newPolldleOptionText
: pour saisir la valeur d’une option de PollDLE avec sa création ;polldleOptions
: pour l’ensemble des options d’un PollDLE. Il s’agit d’un objet avec une propriététext
qui contient la valeur de l’option ;errorMessage
: pour le message d’erreur ;buttonShown
: pour aider à afficher ou pas le bouton de suppression des options d’un PollDLE.
Des fonctions JavaScript peuvent être utilisées pour regrouper des fonctionnalités ou effectuer des traitements plus complexes.
- Compléter la fin de la partie modèle
script
, en ajoutant le code JavaScript correspondant aux méthodes du modèle.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
...
<script>
export default {
name: "CreatePolldle",
... // code de la fonction data()
methods: {
removedPolldleOption(polldleOption) {
let index = this.polldleOptions.indexOf(polldleOption);
this.polldleOptions.splice(index, 1);
this.errorMessage = "";
},
addPolldleOption() {
this.polldleOptions.push({
text: this.newPolldleOptionText
});
this.newPolldleOptionText = "";
},
clearAllPolldleOptions() {
this.polldleOptions = [];
this.errorMessage = "";
},
createPolldle() {
var polldleObject = {
question: this.question,
polldleOptions: []
};
this.polldleOptions.forEach(element => {
var newPollOptionElement = { name: element.text };
if (element.text !== "") {
polldleObject.polldleOptions.push(newPollOptionElement);
}
});
// Call REST web service with fetch API
},
isCreatePolldleDisabled() {
return (
this.polldleOptions === null ||
this.polldleOptions.length < 2 ||
this.question === ""
);
}
}
};
</script>
...
Nous détaillons ci-dessous l’objectif de ces fonctions JavaScript. Veuillez noter que nous avons utilisé l’écriture Fonctions Fléchées (Arrow Functions en anglais) pour simplifier l’écriture et faciliter l’usage de la portée pour this
.
removedPolldleOption(polldleOption)
: pour supprimer une option au PollDLE, l’élément à supprimer est passé en paramètre ;addPolldleOption()
: pour ajouter une nouvelle option au PollDLE ;clearAllPolldleOptions()
: pour supprimer toutes les options du PollDLE ;createPolldle()
: pour créer le PollDLE et appeler le service web côté serveur ;isCreatePolldleDisabled()
: pour savoir si un PollDLE peut être créé (qu’il existe des options de PollDLE, au moins deux et qu’un titre soit présent).
Dans le corps des fonctions fléchées, nous avons utilisé le mot clé this
. Celui-ci a une portée globale et fait référence au modèle du composant.
N’hésitez par à consulter les autres fichiers .vue pour découvrir comment les modèles et les vues ont été construits.
Templating avec Vue.js
Nous vous invitons à vous positionner dans le répertoire polldle-vue-02 pour profiter des codes qui vont illustrer cette section. Pensez à faire
$ npm install
pour installer les modules et$ npm run serve
pour démarrer l’exécution en mode développement.
Dans cette section, nous allons apprendre à réaliser le binding entre la vue et le modèle via le templating. Pour cela et comme précisé en première partie de cet article, deux outillages sont disponibles : l’interpolation via la notation moustache et les directives qui sont des attributs enrichissant les balises HTML.
Interpolation
La forme la plus basique pour réaliser un binding est une interpolation de texte en utilisant une moustache qui est une double accolade entourant l’expression à évaluer : {{ ... }}
. Le rendu va consister à utiliser la moustache pour injecter la valeur de l’expression.
À titre d’exemple, dans le composant CreatePolldle, la zone située en bas de la page donne un résumé des données saisies (titre, nombre d’options et message d’erreur si existant).
- Compléter le code du fichier CreatePolldle.vue en remplaçant la balise de commentaire
<!-- Mustache with question -->
par une notation moustache associée à la propriétéquestion
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
...
<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Summary of your PollDLE</h4>
<hr>
<p>
The question is:
<strong>
<!-- Mustache with question -->
{{ question }}
</strong>
</p>
<p>Number of PollDLE options: TODO</p>
</div>
...
</template>
...
À chaque modification de la valeur de la propriété question
, la moustache injectera la valeur de la propriété dans le DOM.
Le résultat obtenu après le rendu de cette moustache est donné sur le code HTML suivant où question
a pour valeur initiale Aimez-vous les frites ?
.
1
2
3
4
5
6
7
8
9
10
11
...
<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Summary of your PollDLE</h4>
<hr>
<p>
The question is:
<strong>Aimez-vous les frites ?</strong>
</p>
<p>Number of PollDLE options: TODO</p>
</div>
...
Directives
Les moustaches ne peuvent pas être utilisées à l’intérieur d’une balise HTML. Il faut donc passer par l’utilisation de directives qui utilisent le préfixe v-
. Dès que l’expression adossée à la directive est modifiée, cette directive va effectuer les changements sur le DOM.
Dans cette section, nous allons étudier les directives suivantes : v-text
, v-on
, v-bind
, v-model
, v-once
, les directives conditionnelles v-show
, v-if
, v-else
, v-else-if
et la directive v-for
pour le rendu de liste.
Directive v-text
La directive v-text
sert à mettre à jour le contenu textuel d’un élément. Elle joue le même rôle qu’une interpolation moustache.
- Compléter le code du fichier CreatePolldle.vue en ajoutant la directive
v-text
pour afficher la propriétéerrorMessage
(commentaire<!-- Directive v-text with errorMessage -->
).
1
2
3
4
5
6
7
<template>
...
<!-- Directive v-text with errorMessage -->
<div class="error-message alert alert-danger" role="alert" v-text="errorMessage"></div>
...
</template>
...
Le résultat obtenu après le rendu de cette directive v-text
est donné sur le code HTML suivant où v-text="errorMessage"
permet d’injecter dans le corps de la balise <div>
la valeur initiale de la propriété errorMessage
qui est Problem to create a new Polldle
.
1
2
3
...
<div class="error-message alert alert-danger" role="alert">Problem to create a new Polldle.</div>
...
Quand utiliser la directive v-text
ou l’interpolation moustache ? Si le contenu textuel d’une balise doit être changé dans son intégralité, utiliser la directive v-text
, si par contre le contenu textuel d’une balise doit être changé en partie, utiliser une interpolation moustache.
Directive v-bind
La directive v-bind
permet de lier un attribut d’une balise à une expression. L’attribut est donné comme argument séparé par un deux-points après la directive v-bind
. Les attributs class
et style
sont considérés naturellement.
- Compléter le code du fichier CreatePolldle.vue en ajoutant la directive
v-bind
pour lier l’attributdisabled
avec la méthodeisCreatePolldleDisabled()
(commentaire<!-- Directive v-bind with isCreatePolldleDisabled() -->
).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
...
<!-- Button Action -->
<!-- Directive v-bind with isCreatePolldleDisabled() -->
<div class="row">
<div class="col">
<button
type="button"
class="validate-button btn-lg btn-primary mx-auto d-block"
v-bind:disabled="isCreatePolldleDisabled()"
>Create PollDLE</button>
</div>
</div>
...
</template>
...
La directive v-bind
a comme paramètre disabled
(séparée par un deux-points) et est associée à la fonction isCreatePolldleDisabled()
. Dès lors que la fonction isCreatePolldleDisabled()
retourne vrai, l’attribut disabled
sera évalué à vrai et ainsi l’état du bouton sera désactivé.
Dans le cas où la fonction isCreatePolldleDisabled()
retourne faux, le code HTML obtenu après le rendu de cette directive v-bind:disabled
sera le suivant :
1
2
3
4
5
6
7
8
9
10
11
...
<div class="row">
<div class="col">
<button
type="button"
class="validate-button btn-lg btn-primary mx-auto d-block"
disabled="disabled"
>Create PollDLE</button>
</div>
</div>
...
Vue.js fournit une écriture simplifiée de la directive v-bind
. Comme cette directive est largement utilisée, elle peut être remplacée par :
.
L’exemple précédent pourra être écrit de cette façon.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
...
<!-- Button Action -->
<div class="row">
<div class="col">
<button
type="button"
class="validate-button btn-lg btn-primary mx-auto d-block"
:disabled="isCreatePolldleDisabled()"
>Create PollDLE</button>
</div>
</div>
...
</template>
...
Directive v-model
La directive v-model
crée une liaison bidirectionnelle entre un composant de saisie (<input>
, <select>
, <textarea>
) est une propriété du modèle.
- Compléter le code du fichier CreatePolldle.vue en ajoutant la directive
v-model
pour lier les propriétésquestion
etnewPolldleOptionText
au composant de saisie<input>
(commentaires<!-- Directive v-model with question -->
et<!-- Directive v-model with newPolldleOptionText -->
).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<template>
...
<!-- PollDLE name -->
<div class="row">
<div class="col">
<!-- Directive v-model with question -->
<input
type="text"
class="large-input mx-auto d-block"
placeholder="Add your question here"
v-model="question"
>
</div>
</div>
<h3>Add your PollDLE options</h3>
<div class="row">
<div class="col">
<!-- Directive v-model with newPolldleOptionText -->
<input
type="text"
placeholder="Polldle Option"
v-model="newPolldleOptionText"
class="large-input mx-auto d-block"
>
</div>
</div>
...
</template>
...
Le code HTML obtenu après le rendu est le suivant. Nous constatons que la mécanique de la liaison bidirectionnelle n’est pas réalisée côté HTML, mais du côté JavaScript.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
<div class="row">
<div class="col">
<input
type="text"
placeholder="Add your question here"
class="large-input mx-auto d-block"
>
</div>
</div>
<h3>Add your PollDLE options</h3>
<div class="row">
<div class="col">
<input
type="text"
placeholder="Polldle Option"
class="large-input mx-auto d-block">
</div>
</div>
Directive v-on
La directive v-on
permet d’attacher un écouteur d’événements à un élément et de faire appel à une méthode dès lors qu’un événement est émis. Le type d’événement est donné comme argument séparé par un deux-points après la directive v-on
.
- Compléter le code du fichier CreatePolldle.vue en ajoutant la directive
v-on
pour lier les méthodesaddPolldleOption
,clearAllPolldleOptions
etcreatePolldle
aux écouteurs d’événements liés à la souris et au clavier (commentaires<!-- Directive v-on with addPolldleOption -->
,<!-- Directive v-on with clearAllPolldleOptions -->
et<!-- Directive v-on with createPolldle -->
).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<template>
...
<h3>Add your PollDLE options</h3>
<div class="row">
<div class="col">
<!-- Directive v-on with addPolldleOption -->
<input
type="text"
placeholder="Polldle Option"
v-model="newPolldleOptionText"
class="large-input mx-auto d-block"
v-on:keypress.enter="addPolldleOption"
>
</div>
</div>
<div class="row" v-show="buttonShown">
<div class="col">
<!-- Directive v-on with clearAllPolldleOptions -->
<button
type="button"
class="clear-button btn-lg btn-danger mx-auto d-block"
v-on:click="clearAllPolldleOptions"
>Clear all PollDLE Options</button>
</div>
</div>
...
<!-- Button Action -->
<div class="row">
<div class="col">
<!-- Directive v-on with createPolldle -->
<button
type="button"
class="validate-button btn-lg btn-primary mx-auto d-block"
v-on:click="createPolldle"
v-bind:disabled="isCreatePolldleDisabled()"
>Create PollDLE</button>
</div>
</div>
...
</template>
...
L’abonnement aux événements n’est pas visible depuis le code HTML puisque ce traitement est effectué côté JavaScript. Du côté du rendu HTML, les changements ne seront pas encore visibles. Il faudra attendre l’étude de la directive v-for
. Cependant, nous pouvons en profiter pour montrer les états des valeurs des propriétés du composant CreatePolldle
qui changent lors des interactions de l’utilisateur en utilisant l’extension pour les navigateurs web Vue-DevTools.
- Depuis votre navigateur, afficher la page web du projet, ouvrir l’outil du développeur web puis sélectionner l’onglet Vue. Depuis l’arbre des composants, sélectionner ensuite le composant
CreatePolldle
. Vous verrez sur la partie droite les différentes valeurs des propriétés du composant. Dans l’exemple montré sur la figure ci-dessous, nous avons créé trois options.
Il est également possible de filtrer le type d’événement en indiquant des touches spécifiques de clavier ou des boutons de la souris. C’est le cas par exemple pour la validation d’une option de PollDLE qui doit être faite en pressant la touche Enter (v-on:keypress.enter="addPolldleOption"
).
Vue.js fournit également une écriture simplifiée de la directive v-on
. Comme cette directive est largement utilisée, elle peut être remplacé par @
.
Une des parties de l’exemple précédent pourra être écrite de cette façon en utilisant l’écriture simplifiée.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
...
<h3>Add your PollDLE options</h3>
<div class="row">
<div class="col">
<input
type="text"
placeholder="Polldle Option"
v-model="newPolldleOptionText"
class="large-input mx-auto d-block"
@keypress.enter="addPolldleOption"
>
</div>
</div>
...
</template>
...
Directive v-once
Il est possible de n’effectuer le rendu d’une balise ou d’un composant qu’une seule fois. Pour cela il faudra utiliser la directive v-once
depuis la balise englobante. Un exemple est disponible dans le composant FooterPolldle.
- Compléter le code du fichier FooterPolldle.vue en ajoutant la directive
v-once
à la balise<p>
(commentaire de type<!-- Directive v-once -->
).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div class="container">
<!-- Directive v-once -->
<p class="footer" v-once>{{ description }}</p>
</div>
</template>
<script>
export default {
name: "FooterPolldle",
data() {
return {
description:
"PollDLE ~= Poll + (last part of famous DooDLE app). PollDLE is an open source project developped by Mickael BARON and Mahamadou DIABATE - Powered by Vue.js and Java."
};
}
};
</script>
...
Dans ce cas, la directive v-once
prend tout son sens, car le rendu ne sera réalisé qu’une seule fois, même si la description change.
Rendu conditionnel
Nous vous invitons à vous positionner dans le répertoire polldle-vue-03 pour profiter des codes qui vont illustrer cette section. Pensez à faire
$ npm install
pour installer les modules et$ npm run serve
pour démarrer l’exécution en mode développement.
Nous allons dans cette section détailler les directives v-show
et v-if
. Ces directives permettent d’afficher ou de masquer du contenu HTML soit par une simple permutation basée sur du CSS (v-show
) soit par un contrôle du rend (v-if
).
Directive v-if
La directive v-if
permet d’effectuer le rendu ou pas du bloc si l’expression associée à cette directive est vraie. Par ailleurs comme tout bloc de condition, v-if
peut s’utiliser avec v-else-if
(où une autre expression peut être évaluée) v-else
pour le bloc par défaut si aucune expression n’est satisfaite.
Nous donnons un exemple d’utilisation de v-if
et de v-else
dans le composant ResultPolldle.
- Compléter la partie vue du fichier ResultPolldle.vue en remplaçant les balises de commentaire
<!-- Directive v-if ... -->
et<!-- Directive v-else -->
par l’utilisation des directivesv-if
etv-else
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<template>
<div class="container">
<!-- Directive v-if with !isErrorState() -->
<div v-if="!isErrorState()">
<h1>{{ question }}</h1>
<div class="row">
<div class="col-8">
</div>
<div class="col-4">
<div></div>
</div>
</div>
<!-- Directive v-if with isEmptyState() -->
<div v-if="isEmptyState()">
<h2>No vote at this moment, keep in touch. Results update in real-time.</h2>
</div>
</div>
<!-- Directive v-else -->
<div v-else class="error-message alert alert-danger" role="alert">{{ errorMessage }}</div>
</div>
</template>
<script>
...
export default {
...
methods: {
isEmptyState() {
return this.state === stateResult.EMPTY;
},
isErrorState() {
return this.state === stateResult.ERROR;
}
}
}
</script>
Avant-propos, vu que c’est la première fois que nous présentons ce composant : les fonctions
isErrorState()
etisEmptyState()
utilisées comme expression dans les directives conditionnellesv-if
etv-else
permettent d’accéder à la propriétéstate
. La valeur de cette propriété est modifiée lors de l’appel au service web (voir plus tard). Sistate === stateResult.EMPTY
le service web a été invoqué, mais aucune donné n’est transmise. Sistate === stateResult.ERROR
l’invocation au service web a généré une erreur. Si le service web a été invoqué et contient des données alorsstate === stateResult.RESULT
.
Le premier rendu conditionnel est affiché s’il n’y a pas eu de problème lors du chargement des résultats du vote. Le deuxième rendu conditionnel est affiché s’il n’y a pas eu de problème et si le contenu retourné par le service web est vide. Enfin, le troisième rendu conditionnel (v-else
) est lié au premier rendu conditionnel et affichera les erreurs causées par l’invocation du service web.
Nous donnons ci-dessous, le rendu HTML dans le cas où l’invocation au service web a provoqué une erreur.
1
2
3
4
5
...
<div class="container">
<div role="alert" class="error-message alert alert-danger">Problem to retrieve Polldle result.</div>
</div>
...
Comme on peut le remarquer, le premier bloc lié à l’affichage des résultats ne sera pas créé. Seul le dernier bloc sera créé.
Directive v-show
La directive v-show
a le même effet que la directive v-if
(et sœurs) du point de vue visuel. Toutefois au niveau du code, le rendu d’un bloc avec v-show
sera réalisé, mais l’affichage sera contrôlé par la propriété CSS display
.
- Compléter la partie vue du fichier CreatePolldle.vue en remplaçant les balises de commentaire
<!-- Directive v-show with buttonShown -->
et<!-- Directive v-show with errorMessage -->
par une directivev-show
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
...
<!-- Directive v-show with buttonShown -->
<div class="row" v-show="buttonShown">
<div class="col">
<button
type="button"
class="clear-button btn-lg btn-danger mx-auto d-block"
@click="clearAllPolldleOptions"
>Clear all PollDLE Options</button>
</div>
</div>
...
<!-- Directive v-show with errorMessage -->
<div
v-show="errorMessage !== ''"
class="error-message alert alert-danger"
role="alert"
v-text="errorMessage"
></div>
...
</template>
Le premier bloc correspond au bouton « Clear All PollDLE Options » qui ne sera visible que s’il existe des options de Polldle (propriété buttonShown
). Pour le second bloc où la directive v-show
est utilisée, nous utilisons une expression JavaScript qui vérifie si la propriété errorMessage
est vide ou pas.
Nous donnons ci-dessous le rendu HTML correspondant au premier bloc permettant de rendre visible ou pas le bouton « Clear All PollDLE Options ». Pour tester, changer la valeur de la propriété buttonShown
de false
à true
et inversement.
1
2
3
4
5
6
7
...
<div class="row" style="display: none;">
<div class="col">
<button type="button" class="clear-button btn-lg btn-danger mx-auto d-block">Clear all PollDLE Options</button>
</div>
</div>
...
Dans cet état de valeur de propriétés où butonShown
vaut faux, la directive v-show
injectera la valeur de style style="display: none;"
.
Quand utiliser v-if ou v-show ?
Les directives v-if
et v-show
permettent d’obtenir le même résultat visuellement, mais les rendus HTML sont différents.
Si vous avez besoin d’effectuer des permutations fréquemment (visibles ou pas visibles) au cours de l’utilisation de votre composant, il est préférable d’utiliser v-show
. La directive v-if
est à utiliser de préférence lors de l’initialisation de votre composant et quand il y a peu de changements. Un changement de CSS (via la directive v-show
) est toujours moins coûteux que de devoir créer de nouveaux blocs (via la directive v-if
).
Il faut aussi noter que la directive v-show
réalise le rendu HTML quoi qu’il arrive, ce qui n’est pas le cas pour la directive v-if
. Il faut dont prêter attention aux propriétés manipulées dans le rendu qui pourraient ne pas être initialisées. Par conséquent, si vous devez traiter des propriétés qui ne sont pas encore initialisées et qui le seront quand l’affichage devra être visible, il est préférable d’utiliser la directive v-if
, dans le cas contraire utiliser la directive v-show
.
Rendu de liste
Nous vous invitons à vous positionner dans le répertoire polldle-vue-04 pour profiter des codes qui vont illustrer cette section. Pensez à faire
$ npm install
pour installer les modules et$ npm run serve
pour démarrer l’exécution en mode développement.
Nous étudions dans cette section la directive v-for
qui permet de réaliser plusieurs fois le rendu d’un élément (où s’applique la directive).
La valeur de la directive v-for
doit suivre la syntaxe alias in expression
ou expression peut-être issue d’une donnée source de type tableau ou d’objet (via les propriétés de l’objet). alias
permettra d’accéder à l’élément courant.
Nous présentons ci-dessous les différentes syntaxes que vous pourrez retrouver en utilisant v-for
en fonction du type de source (tableau ou objet).
1
2
3
4
5
6
7
<!-- La source items est un tableau. items: [ { text: 'yes' }, { text: 'no'} ... ] -->
<div v-for="item in items">{{ item.text }}</div> --> item est l'élément courant (yes)
<div v-for="(item, index) in items"></div> --> item est l'élément courant (yes) et index l'indice de l'élément (0)
<!-- La source object est un objet. object: { firstname: 'mickael', familyname: 'baron' ... } -->
<div v-for="(val, key) in object"></div> --> val est la valeur de la propriété (mickael) et key le nom de la propriété (firstname)
<div v-for="(val, key, index) in object"></div> --> val est la valeur de la propriété (mickael), key le nom de la propriété (firstname) et index l'indice de la propriété (0)
- Compléter la partie vue du fichier CreatePolldle.vue en remplaçant les balises de commentaire
<!-- Directive v-for with polldleOptions -->
par l’utilisation de la directivev-for
.
1
2
3
4
5
6
7
8
9
10
11
...
<!-- PollDLE option -->
<!-- Directive v-for with polldleOptions -->
<div
class="row justify-content-center"
v-for="currentPolldleOption in polldleOptions"
:key="currentPolldleOption.text"
>
{{ currentPolldleOption.text }}
</div>
...
Dans l’exemple complet, l’élément construit plusieurs fois sera relatif au composant CreatePolldleOption. Comme nous n’avons pas encore présenté la notion de composant de Vue.js, nous nous limiterons à l’affichage du texte de l’option PollDLE.
Nous donnons ci-dessous le rendu HTML lorsque polldleOptions
contient deux options Oui
et Non
.
1
2
3
4
5
6
7
...
<div class="row justify-content-center">
Oui
</div>
<div class="row justify-content-center">
Non
</div>
L’élément répété est celui où la directive v-for
est appliquée, dans ce cas <div class="row justify-content-center">
.
Composant avec Vue.js
Cette section s’intéresse à la notion de composant. Nous verrons comment développer et instancier un composant. Nous étudierons les aspects liés au cycle de vie d’un composant. Nous terminerons par les différentes façons pour communiquer entre des composants.
Savoir développer un composant
Nous vous invitons à vous positionner dans le répertoire polldle-vue-05 pour profiter des codes qui vont illustrer cette section. Pensez à faire
$ npm install
pour installer les modules et$ npm run serve
pour démarrer l’exécution en mode développement.
Pour développer un composant avec Vue.js, il existe plusieurs façons qui sont parfaitement résumées dans cet article : https://vuejsdevelopers.com/2017/03/24/vue-js-component-templates/. Dans le périmètre de notre article, nous nous limiterons au développement du composant via l’utilisation d’un fichier portant l’extension .vue. Cette manière de développer est appelée composants monofichiers ou composants à fichier unique (Single File Components en anglais). Nous avions déjà évoqué dans la partie introductive de Vue.js la description d’un composant sous cette forme. Pour rappel, ce fichier avec l’extension .vue est décomposé en trois parties qui définissent :
- un template constitué de balises HTML qui définit la structure du composant (balise
<template>
) ; - un code JavaScript qui détermine le comportement du composant (balise
<script>
) ; - des styles CSS qui définissent l’apparence du composant (balise
<style>
). - Créer un fichier CreatePolldleOption.vue relatif au composant CreatePolldleOption et recopier le code ci-dessous.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<template >
<div class="polldle-option-input row justify-content-center no-gutters">
<div class="col col-auto">
<input
type="text"
class="form-control"
readonly
>
</div>
<div class="col col-auto">
<!-- Directive v-on with removePolldleOption -->
<button
class="btn btn-outline-secondary"
type="button"
@click="removePolldleOption()"
>X</button>
</div>
</div>
</template>
<script>
export default {
name: "CreatePolldleOption",
data() {
return {
errorMessage: ""
};
},
methods: {
removePolldleOption() {
}
}
};
</script>
<style>
.polldle-option-input {
margin-bottom: 5px;
}
</style>
La propriété name: "CreatePolldleOption"
permet de définir le nom du composant. Ce nom doit être généralement identique au nom donné au fichier. La convention de nommage recommandée peut-être kebab-case ou PascalCase. C’est cette dernière convention que nous utilisons. La convention de nommage PascalCase consiste à mettre en majuscule la première lettre de chaque mot. Dans le cas de cet exemple, le composant est identifié par CreatePolldleOption
et le fichier contenant le code sera nommé CreatePolldleOption.vue.
Quand un composant est développé via un monofichier, c’est à la charge de Vue CLI et des outils annexes (Webpack par exemple avec Vue Loader) de transformer le code contenu dans ce fichier unique pour générer un code JavaScript compréhensible par le navigateur. Sans cet outillage, l’utilisation de fichiers portant l’extension .vue et avec une décomposition en trois parties n’aurait pas d’utilité.
Savoir instancier un composant
Nous vous invitons à vous positionner dans le répertoire polldle-vue-06 pour profiter des codes qui vont illustrer cette section. Pensez à faire
$ npm install
pour installer les modules et$ npm run serve
pour démarrer l’exécution en mode développement.
Précédemment, nous avons vu comment développer un composant, nous allons maintenant voir comment l’instancier au sein d’autres composants Vue.js. À ce propos, deux types de composants sont à distinguer : les composants que vous avez développés (c’est le cas du composant CreatePolldleOption) et les composants externes (c’est le cas de la bibliothèque Highcharts via le composant vue-highcharts). Quelle que soit l’origine des composants, la manière de les utiliser au sein d’un composant reste identique.
Pour instancier un composant, trois choses doivent être prises en compte :
- l’importation du monofichier de description du composant ;
- la déclaration locale ou globale ;
- l’instanciation du composant.
Nous prendrons comme exemple le composant CreatePolldle défini dans le fichier CreatePolldle.vue.
Importation du monofichier de description du composant
- Dans le code donné ci-dessous du fichier CreatePolldle.vue, remplacer le commentaire
// Import CreatePolldleOption component
en ajoutant la variableCreatePolldleOption
qui permet de pointer sur le composant CreatePolldleOption défini dans le fichier CreatePolldleOption.vue.
1
2
3
4
5
6
7
8
9
<template>
...
</template>
<script>
// Import CreatePolldleOption component
import CreatePolldleOption from "@/components/CreatePolldleOption.vue";
...
</script>
...
Le caractère
@
est utilisé par les outillages pour désigner le répertoire courant. Webpack remplacera ce caractère par le chemin courant du projet.
Déclaration locale ou globale
La déclaration locale précise qu’un composant importé n’est visible que par le composant qui en fait la demande. Au contraire, la déclaration globale précise qu’un composant importé est visible par tous les composants du projet. Il y a un risque de rendre globale la déclaration d’un composant. Cela alourdit le code produit et cela empêche d’avoir une visibilité explicite des dépendances entre les composants (qui utilise quoi).
- Dans le code donné ci-dessous du fichier CreatePolldle.vue, remplacer le commentaire
// Add dependencies on CreatePolldleOption component
en ajoutant la propriétécomponents
pour exprimer que le composant CreatePolldleOption doit être déclaré.
1
2
3
4
5
6
7
8
9
10
11
...
<script>
import CreatePolldleOption from "@/components/CreatePolldleOption.vue";
...
export default {
name: "CreatePolldle",
// Add dependencies on CreatePolldleOption component
components: { CreatePolldleOption },
...
</script>
...
Pour déclarer globalement un composant, il est préférable de le faire depuis le fichier main.js. Malgré le fait que nous n’utilisons pas la déclaration globale dans notre projet, nous montrons à titre d’exemple la manière de faire. Dans le code ci-dessous, il faut d’une part importer le monofichier CreatePolldleOption.vue et d’autre part l’ajouter globalement à l’objet
Vue
comme ceci :Vue.component('CreatePolldleOption', CreatePolldleOption)
.
1
2
3
4
5
6
7
8
9
10
11
12
13
import Vue from 'vue'
import App from './App.vue'
require('./assets/polldle.css')
import CreatePolldleOption from "@/components/CreatePolldleOption.vue";
Vue.config.productionTip = false
Vue.component('CreatePolldleOption', CreatePolldleOption)
new Vue({
render: h => h(App)
}).$mount('#app')
Instanciation du composant
La dernière étape consiste à utiliser le composant dans la partie template du monofichier. Le composant est vu comme une nouvelle balise dont le nom est identique à la variable utilisée lors de l’importation.
Il est à noter qu’un composant peut être instancié autant de fois que souhaité. Il n’y a pas de limite, excepté la mémoire utilisée par votre navigateur.
- Compléter la partie template du fichier CreatePolldle.vue en remplaçant la balise de commentaire
<!-- Instance CreatePolldleOption component -->
par notre nouvelle balise<CreatePolldleOption>
.
1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
...
<div
class="row justify-content-center"
v-for="currentPolldleOption in polldleOptions"
:key="currentPolldleOption.text"
>
<!-- Instance CreatePolldleOption component -->
<CreatePolldleOption/>
</div>
...
</template>
...
Le composant CreatePolldleOption sera créé autant de fois que précisé dans la boucle.
Ci-dessous est présenté le rendu HTML du composant lorsque deux instances du composant CreatePolldleOption ont été créées.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<div class="row justify-content-center">
<!-- Début du composant CreatePolldleOption -->
<div class="polldle-option-input row justify-content-center no-gutters">
<div class="col col-auto">
<input type="text" readonly="readonly" class="form-control">
</div>
<div class="col col-auto">
<button type="button" class="btn btn-outline-secondary">X</button>
</div>
</div>
<!-- Fin du composant CreatePolldleOption -->
</div>
<div class="row justify-content-center">
<!-- Début du composant CreatePolldleOption -->
<div class="polldle-option-input row justify-content-center no-gutters">
<div class="col col-auto">
<input type="text" readonly="readonly" class="form-control">
</div>
<div class="col col-auto">
<button type="button" class="btn btn-outline-secondary">X</button>
</div>
</div>
<!-- Fin du composant CreatePolldleOption -->
</div>
Le contenu généré est conforme au composant CreatePolldleOption. Ce code n’est pas complet puisque les champs input
ne sont pas renseignés. Nous aborderons cet aspect dans la section suivante dédiée à la communication entre des composants.
Composant externe ou plugin
Nous vous invitons à vous positionner dans le répertoire polldle-vue-07 pour profiter des codes qui vont illustrer cette section. Pensez à faire
$ npm install
pour installer les modules et$ npm run serve
pour démarrer l’exécution en mode développement.
Au sens de composant externe, nous considérons une bibliothèque développée par un tiers et que l’on souhaite intégrer à notre projet. Au niveau de Vue.js, ce type de composant est aussi appelé plugin. C’est le cas pour la bibliothèque JavaScript Highcharts et de sa version packagée vue-highcharts pour le rendu des résultats d’un Polldle.
Pour transformer un composant en un plugin ou composant externe, il faut exposer une méthode
install
. Cela n’étant pas l’objectif de cet article, une indication est donnée dans la documentation officielle de Vue.js.
Quand vous souhaitez ajouter une bibliothèque dans votre projet, vous devez généralement ajouter des dépendances dans le fichier package.json puis instancier le composant souhaité dans votre code. Examinons ensemble l’ajout de la bibliothèque Highcharts et de sa version packagée vue-highcharts.
- Saisir la ligne commande suivante permettant d’ajouter la bibliothèque JavaScript Highcharts et de sa version packagée vue-highcharts avec l’outil de dépendances npm.
1
2
3
4
5
6
$ npm install highcharts vue-highcharts
...
+ vue-highcharts@0.1.0
+ highcharts@8.1.2
added 2 packages from 2 contributors and audited 1266 packages in 5.272s
...
Pour trouver précisément le nom des bibliothèques à ajouter, il faut généralement se rendre sur le site web de la bibliothèque qui expliquera comment l’installer. Il n’existe qu’une bibliothèque JavaScript correspondant à Highcharts, mais plusieurs variantes packagées développées pour Vue.js.
La commande précédente va également ajouter dans le fichier package.json deux entrées puis télécharger les bibliothèques dans le répertoire node_modules. Ci-dessous est présenté une partie du contenu du fichier package.json.
1
2
3
4
5
6
7
8
9
{
...
"dependencies": {
"highcharts": "^8.1.2",
"vue": "^2.6.10",
"vue-highcharts": "^0.1.0"
},
...
}
Il faut ensuite ajouter le plugin dans le composant ResultPolldle décrit par le fichier ResultPolldle.vue.
- Éditer le fichier ResultPolldle.vue en remplaçant les commentaires
// Import the VueHighcharts plugin
et// Use the VueHighcharts plugin
par le code présenté ci-dessous.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
</template>
<script>
// Import the VueHighcharts plugin
import VueHighcharts from "vue-highcharts";
import Vue from "vue";
// Use the VueHighcharts plugin
Vue.use(VueHighcharts);
...
</script>
...
<style>
</style>
Le code Vue.use(VueHighcharts)
permet d’utiliser le plugin de manière globale. Dans ce cas, tous les composants décrits par la bibliothèque Highcharts : Highcharts, Highstock, Highmaps et HighchartsGantt sont directement instanciables.
- Éditer le fichier ResultPolldle.vue en remplaçant la balise de commentaire
<!-- Instance of highcharts component -->
par la balise<highcharts>
.
1
2
3
4
5
6
7
8
9
10
<template>
...
<div class="row">
<div class="col-8">
<!-- Instance of highcharts component -->
<highcharts/>
</div>
</div>
...
</template>
Le code ajouté n’est pas complet, car il implique une communication entre le composant ResultPolldle et highcharts. Tout comme la communication entre les composants CreatePolldle et CreatePolldleOption, nous aborderons cet aspect dans la section suivante dédiée à la communication entre des composants.
Savoir écouter un composant : propriétés calculées et observateurs
Nous vous invitons à vous positionner dans le répertoire polldle-vue-08 pour profiter des codes qui vont illustrer cette section. Pensez à faire
$ npm install
pour installer les modules et$ npm run serve
pour démarrer l’exécution en mode développement.
Lors de changement des propriétés du modèle, on peut vouloir calculer de nouvelles propriétés (les propriétés calculées) ou déclencher des opérations coûteuses (les observateurs).
Propriétés calculées (computed)
Il peut arriver que dans une interpolation ou une directive, l’expression transmise soit complexe ce qui peut alourdir la lisibilité de la partie vue. Par ailleurs, si cette expression est répétée, il devient nécessaire de regrouper le code (code dupliqué == bogue répété).
Prenons l’exemple du résumé du nombre d’options de PollDLE du composant CreatePolldle (voir fichier CreatePolldle.vue).
1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
...
<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Summary of your PollDLE</h4>
<hr>
<p>
The question is:
<strong>{{ question }}</strong>
</p>
<p>Number of PollDLE options: {{ this.polldleOptions.length }}</p>
</div>
...
</template>
Pour remplacer le code this.polldleOptions.length
, nous allons utiliser une propriété calculée qui sera mise à jour à chaque changement de la propriété polldleOptions
.
- Compléter la partie vue du fichier CreatePolldle.vue en remplaçant la balise de commentaire
<!-- Mustache with computed property: listSize -->
par une notation moustache associée à la propriétélistSize
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
...
<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Summary of your PollDLE</h4>
<hr>
<p>
The question is:
<strong>{{ question }}</strong>
</p>
<!-- Mustache with computed property: listSize -->
<p>Number of PollDLE options: {{ listSize }}</p>
</div>
...
</template>
- Compléter la partie JavaScript du fichier CreatePolldle.vue en remplaçant la balise de commentaire
// Computed property listSize when polldleOptions changes
par une propriété calculée comme présenté ci-dessous. Toutes les propriétés calculées de ce composant devront être placées comme éléments de l’attributcomputed
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default {
...
data() {
},
// Computed property listSize when polldleOptions changes
computed: {
listSize() {
return this.polldleOptions.length;
}
},
methods: {
...
}
}
La propriété calculée listSize est mise en cache et tant que les propriétés dont elle dépend ne changent pas (ici, il s’agit de polldleOptions), l’expression ne sera pas réévaluée. Ainsi, si un nouveau rendu est effectué côté vue, la valeur de la propriété calculée listSize sera prise depuis le cache.
Le résultat aurait été identique si nous avions utilisé une fonction déclarée dans la zone methods. Toutefois, à chaque rendu de la vue, l’expression de la fonction déclarée dans methods aurait été évaluée. Cette optimisation a son importance quand les expressions des propriétés calculées commencent à devenir complexes.
Observateurs (watch)
Lorsqu’une valeur de propriété du modèle est modifiée, on peut vouloir invoquer une fonction JavaScript (en mode asynchrone) ou modifier la valeur d’une propriété. Vue.js fournit un mécanisme appelé Observateurs qui pour chaque changement de valeur d’une propriété ciblée vous permet d’effectuer un traitement.
Nous présentons dans l’exemple de création d’un PollDLE, la mise en place d’un observateur pour la propriété polldleOptions
qui, pour chaque changement, modifiera la valeur de la propriété buttonShown
.
- Compléter la partie JavaScript du fichier CreatePolldle.vue en remplaçant la balise de commentaire
// Watcher on polldleOptions
par un observateur comme présenté ci-dessous. Tous les observateurs de ce composant devront être placés comme éléments de l’attributwatch
.
1
2
3
4
5
6
// Watcher on polldleOptions
watch: {
polldleOptions() {
this.buttonShown = this.polldleOptions != null && !(this.polldleOptions.length === 0);
}
}
Dans cet exemple, le nom de la fonction observateur prend le même nom que la propriété à observer : polldleOptions
. Ainsi tout le code contenu à l’intérieur de cette fonction sera appelé à chaque fois que la valeur de polldleOptions
change.
Dans ce cas précis, l’utilisation d’un observateur au lieu d’une propriété calculée est discutable. L’utilisation d’une propriété calculée pour obtenir la valeur de
buttonShown
aurait aussi fonctionné.
Cycle de vie
Nous vous invitons à vous positionner dans le répertoire polldle-vue-09 pour profiter des codes qui vont illustrer cette section. Pensez à faire
$ npm install
pour installer les modules et$ npm run serve
pour démarrer l’exécution en mode développement.
Un cycle de vie est utilisé comportant un ensemble d’étapes. Chaque étape est associée à un hook permettant d’exécuter un code particulier.
Nous présentons ci-dessous un diagramme du cycle de vie qui permet de lister les différentes étapes (identifiées par un rectangle rouge).
Huit hooks sont disponibles : beforeCreate
, created
, beforeMount
, mounted
, beforeUpdate
, updated
, beforeDestroy
et destroyed
. Nous ne les étudierons pas tous exceptés le hook created
et mounted
, les plus utilisés. Ce lien donne une explication détaillée de leur utilisation Understanding Vue.js Lifecycle Hooks.
Hook created
Le hook created
sera exécuté à la création du composant et toutes les propriétés de ce composant (celles qui sont dans data()
) sont initialisées et associées au système réactif. Il en est de même pour les événements que nous aborderons dans la section suivante. Le code défini dans ce hook pourra donc accéder aux propriétés du composant. Toutefois, à cette étape, le rendu du template et le DOM virtuel ne sont pas encore effectués. Vous ne devrez donc pas effectuer de modification sur le rendu du composant lors d’un hook created
.
Nous utiliserons le hook created
dans deux composants :
- VotePolldle : initialisation des données via l’appel à un service web (utilisation de la bibliothèque JavaScript Axios) ;
- ResultPolldle : initialisation du SSE (Server-Sent Event) pour faire du push serveur et récupérer le flux des mises à jour des votes.
Comme nous n’avons pas vu les appels à un service web (via la bibliothèque JavaScript Axios), nous allons focaliser notre présentation du hook created
sur le code du composant ResultPolldle sans forcément le détailler.
- Éditer le fichier ResultPolldle.vue en remplaçant le commentaire
// Use created hook to initialize EventSource object
par le code présenté dans le hookcreated
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<template>
...
</template>
<script>
...
export default {
name: "ResultPolldle",
data: () => ({
total: 0,
state: null,
question: "",
errorMessage: "",
options: options,
data: []
}),
// Use created hook to initialize EventSource object
created() {
var source = new EventSource(
"http://127.0.0.1:9991" +
"/polldles/" +
this.$route.params.pathurl +
"/votes/sse"
);
source.addEventListener(
"update-polldleresult",
e => {
var result = JSON.parse(e.data);
this.options.title.text = " ";
this.question = capitalizeFirstLetter(result.question);
this.data = result.results.map(val => ({
name: val.name,
y: val.counter
}));
this.total = result.results
.map(val => val.counter)
.reduce((partial_sum, a) => partial_sum + a);
if (this.total > 0) {
this.state = stateResult.RESULT;
} else {
this.state = stateResult.EMPTY;
}
this.options.series[0].data = this.data;
},
false
);
source.onerror = () => {
this.state = stateResult.ERROR;
this.errorMessage = "Problem to retrieve Polldle result.";
};
},
</script>
Le code présent dans le hook created
permet d’initialiser un objet EventSource
utilisé pour faire du Server-Sent Event. La première partie initialise l’objet EventSource
. La deuxième partie traite les nouvelles données envoyées par le serveur et transforme les données pour les proposer au modèle du composant Highcharts. La troisième partie est une fonction qui s’occupera de traiter les erreurs. On aperçoit dans ce code que seules les propriétés du composant sont impactées ce qui est cohérent à l’utilité du scope du hook created
.
Si vous désirez des informations supplémentaires sur Server-Sent Event, une technique pour faire du push serveur via une communication unidirectionnelle, nous vous recommandons deux supports de cours : Streaming HTTP : savoir communiquer via des flux et Streaming HTTP : mise en œuvre avec le langage Java.
Hook mounted
Le hook mounted
est celui qui vient juste après le premier rendu du template. Au niveau de cette étape, le DOM virtuel est construit et des modifications peuvent être réalisées. Pour accéder au DOM HTML, il est possible d’utiliser une propriété d’instance du composant via l’instruction suivante this.$em
.
- Éditer le fichier CreatePolldle.vue en remplaçant le commentaire
// Use mounted hook to log the text content of the DOM
par le code présenté dans le hookmounted
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
...
</template>
<script>
...
export default {
name: "CreatePolldle",
...
// Use mounted hook to log the text content of the DOM
mounted() {
console.log(this.$el.textContent);
},
...
};
</script>
Lors de l’exécution du hook mounted
, le résultat suivant sera affiché dans la console du développeur.
1
PollDLEVoting done simply in real-timeAdd your PollDLE optionsClear all PollDLE OptionsCreate PollDLESummary of your PollDLE The question is: Number of PollDLE options: 0
Savoir communiquer avec un composant
Précédemment, nous avons vu comment créer une instance d’un composant. Toutefois, nous ne nous étions pas intéressés à expliquer comment transmettre des informations vers le composant créé ou comment ce composant pouvait également communiquer avec d’autres composants. Trois techniques de communication avec un composant seront étudiées :
- en communication directe via l’utilisation de la référence d’un composant ;
- en transmettant des propriétés à un composant lors de son instanciation ;
- en utilisant des événements.
Via la référence d’un composant
Nous vous invitons à vous positionner dans le répertoire polldle-vue-10 pour profiter des codes qui vont illustrer cette section. Pensez à faire
$ npm install
pour installer les modules et$ npm run serve
pour démarrer l’exécution en mode développement.
La communication directe via la référence d’un composant est unidirectionnelle (composant parent vers le composant enfant). Elle permet à un composant parent d’accéder à un composant enfant via sa référence. L’inverse ne sera pas possible. Cette solution amène à un couplage fort entre les composants. En effet, cela suppose d’avoir accès à la référence du composant et de s’assurer que lors de l’utilisation de ce composant celui-ci est toujours existant. Si ce n’est plus le cas, il faudra s’assurer de mettre à jour la référence à manipuler.
Bien que cette solution ne soit pas la plus avantageuse, elle permet d’accéder à des éléments du composant non disponibles par le système réactif. Dans le cas de notre exemple, la communication par référence est utilisée dans le composant ResultPolldle pour accéder directement à la bibliothèque JavaScript Highcharts via le plugin vue-highcharts. En effet, depuis le composant de la bibliothèque JavaScript certaines caractéristiques ne sont pas accessibles.
- Éditer le code du composant ResultPolldle via le fichier ResultPolldle.vue en ajoutant l’attribut
ref
à la balise<highcharts/>
(voir commentaire<!-- Declaring Ref attribute -->
).
1
2
3
4
5
6
7
8
9
10
<template>
...
<div class="row">
<div class="col-8">
<!-- Declaring Ref attribute -->
<highcharts ref="highcharts"/>
</div>
</div>
...
</template>
Il sera donc possible d’accéder à l’instance de ce composant via la variable highcharts
.
- Éditer le code du composant ResultPolldle via le fichier ResultPolldle.vue en remplaçant le commentaire
// Use reference on hightcharts component
par le code présenté ci-dessous.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
<template>
...
</template>
<script>
...
watch: {
data() {
// Use reference on hightcharts component
var chart = this.$refs.highcharts.chart;
chart.series[0].update(
{
data: this.data
},
true
);
}
},
...
</script>
...
</template>
La propriété observée data
est modifiée à chaque fois qu’un vote est effectué (utilisation de Server-Sent Event depuis le hook created
). L’accès au composant se fait de cette manière. L’accès à la propriété d’instance se fait avec this.$refs
puis l’accès à la référence du composant est obtenu par this.$refs.highcharts
. Le code présenté ensuite permet d’accéder à l’instance chart
et de lui impacter des modifications (mise à jour de la partie modèle).
Via les Props
Nous vous invitons à vous positionner dans le répertoire polldle-vue-11 pour profiter des codes qui vont illustrer cette section. Pensez à faire
$ npm install
pour installer les modules et$ npm run serve
pour démarrer l’exécution en mode développement.
La communication par props (qui sont des propriétés) consiste à transmettre des données d’un composant parent à un composant enfant. Ce type de communication est unidirectionnelle (composant parent vers le composant enfant). La communication par props impose d’une part que du côté du composant enfant soit déclarées les propriétés à recevoir (section props
) et d’autre part que les valeurs des propriétés soient transmises lors de l’instanciation du composant.
Dans notre exemple, nous allons construire des instances du composant CreatePolldleOption utilisées pour afficher les différentes options de notre Polldle. La valeur de chaque option de notre Polldle, éditée depuis un champ de texte, sera transmise depuis le composant CreatePolldle lors de la création des instances CreatePolldleOption.
Côté composant enfant
- Éditer le composant CreatePolldleOption au niveau de la partie JavaScript en remplaçant le commentaire
// Add properties definition on polldleOption object
par le code présenté dansprops
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
...
</template>
<script>
export default {
...
// Add properties definition on polldleOption object
props: {
polldleOption: {
type: Object,
required: true
}
}
...
}
</scrip>
Cela permet de déclarer que le composant CreatePolldleOption doit (attribut required
) accepter une prop (propriété) de type Object
qui s’appelle polldleOption
.
Une version simplifiée est fournie à titre d’exemple ci-dessous.
1
2
3
4
5
6
7
8
9
10
<template>
...
</template>
<script>
export default {
...
props: ["polldleOption"]
...
}
</script>
- Éditer de nouveau le composant CreatePolldleOption au niveau de la partie HTML en remplaçant le commentaire
<!-- Bind both value and title attributes with polldleOption property -->
par le code présenté.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template >
<div class="polldle-option-input row justify-content-center no-gutters">
<div class="col col-auto">
<!-- Bind both value and title attributes with polldleOption property -->
<input
type="text"
class="form-control"
readonly
v-bind:value="polldleOption.text"
v-bind:title="polldleOption.text"
>
</div>
</div>
</template>
Ici la propriété polldleOption
est utilisée pour mapper la valeur textuelle avec les attributs value
et title
de la balise <input>
. Tout comme les propriétés définies dans data()
, la propriété polldleOption
est déclarée dans le système réactif de Vue.js. Ainsi, tout changement de valeur dans la propriété polldleOption
impactera les valeurs dans les attributs value
et title
.
Côté composant parent
Du côté du composant parent, lors de l’instanciation d’un composant, les valeurs transmises pour les propriétés déclarées de ce composant enfant doivent être renseignées via des attributs portant le même nom que lesdites propriétés. Ces attributs doivent également utiliser la même convention de nommage.
- Éditer le fichier CreatePolldle.vue en ajoutant l’attribut
v-bind:polldleOption="polldleOption"
(voir commentaire<!-- Send object value for polldleOption property -->
).
1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
...
<div
class="row justify-content-center"
v-for="currentPolldleOption in polldleOptions"
:key="currentPolldleOption.text"
>
<!-- Send object value for polldleOption property -->
<CreatePolldleOption v-bind:polldleOption="currentPolldleOption"/>
</div>
...
</template>
...
Dans le code montré ci-dessus, pour chaque instance nouvellement créée du composant CreatePolldleOption, un objet de type PolldleOption
(contenant une seule propriété String
) est transmis comme propriété à ce composant enfant. Ce mode de transmission est appelé dynamique puisque la valeur transmise se fait via la directive v-bind
. Ainsi, le système réactif sera également disponible dans le composant CreatePolldleOption pour chaque objet transmis.
- À titre d’exemple, voici le même code en utilisant la version simplifiée de la directive
v-bind
.
1
2
3
4
5
6
7
8
9
10
<template>
...
<div
class="row justify-content-center"
v-for="currentPolldleOption in polldleOptions"
:key="currentPolldleOption.text"
><CreatePolldleOption :polldleOption="currentPolldleOption"/></div>
...
</template>
...
Si nous avions souhaité utiliser le mode de transmission statique (en gros une valeur chaîne de caractères excepté un tableau, un objet, un nombre ou un booléen), il faudrait avant tout impacter le composant CreatePolldleOption afin de déclarer la propriété pour qu’elle soit une chaîne de caractères et non un objet. Le code correspondant à la transmission de cette chaîne de caractères aurait ressemblé à cela.
1
2
3
4
5
6
7
8
9
10
<template>
...
<div
class="row justify-content-center"
v-for="currentPolldleOption in polldleOptions"
:key="currentPolldleOption.text"
><CreatePolldleOption polldleOption="currentPolldleOption.text"/></div>
...
</template>
...
Depuis le composant ResultPolldle, il y a aussi une communication par props vers le composant highcharts en utilisant le code suivant :
<highcharts :options="options" ref="highcharts"/>
. Via:options="options"
, l’objetoptions
qui contient la configuration du graphique et les données est transmis via des propriétés.
Via les événements personnalisés
Nous vous invitons à vous positionner dans le répertoire polldle-vue-12 pour profiter des codes qui vont illustrer cette section. Pensez à faire
$ npm install
pour installer les modules et$ npm run serve
pour démarrer l’exécution en mode développement.
Une communication par événements personnalisés (Custom Event) amène à un faible couplage entre un composant parent et un composant enfant. Ce type de communication est à choisir dans le cas où vous souhaitez que votre composant enfant puisse communiquer avec le composant parent.
La mise en place de ce type de communication est assez classique. Il y a d’abord la phase de création d’événements avec sa transmission vers le composant parent. Il y a ensuite la phase d’écouteur qui consiste à réagir (appeler un code particulier) suivant l’événement personnalisé reçu. Un événement personnalisé est constitué d’un identifiant (une chaîne de caractères) et de paramètres (cardinalité zéro ou plusieurs).
Dans notre exemple, le composant CreatePolldleOption va envoyer un événement removedPolldleOption
au composant parent CreatePolldle lorsque l’utilisateur souhaite supprimer une option (icône de la poubelle). L’abonnement à l’événement removedPolldleOption
est réalisé dans le composant parent CreatePolldle. Le traitement à la réception de cet événement consistera à retirer depuis le tableau polldleOptions
l’élément correspondant à la bonne option. Pour rappel, c’est dans le composant CreatePolldle que sont stockés les objets relatifs aux options d’un Polldle.
Création d’événements personnalisés et transmission
Comme précisé dans la section précédente, un événement est composé d’une chaîne de caractères et de paramètres. Les paramètres peuvent être de types différents. La transmission de l’événement se fera via la propriété d’instance this.$emit
.
Il est à noter que chaque composant enfant à une relation avec son composant parent pour la transmission d’événement personnalisé via la propriété d’instance
this.$emit
. Si vous souhaitez communiquer avec n’importe quel composant (pas forcément un composant parent), vous pourriez utiliser un bus d’événement à partir dewindow.bus.$emit
. Toutefois, il serait préférable de passer par un système global de gestion d’état comme Vuex. Nous reparlerons de l’utilisation d’un gestionnaire d’état dans un prochain tutoriel.
- Éditer le code du composant CreatePolldleOption en remplaçant le commentaire
// Trigger an event on the current instance
par le code suivant.
1
2
3
4
5
6
7
8
9
10
11
12
13
...
<script>
export default {
...
methods: {
removePolldleOption(polldleOption) {
// Trigger an event on the current instance
this.$emit("removed-polldle-option", polldleOption);
}
},
...
}
</script>
Le code ci-dessus déclenche l’événement removed-polldle-option
sur l’instance actuelle en transmettant l’objet PolldleOption
. Pour rappel cet objet avait été transmis lors de la création de l’instance du composant PolldleOption
(via les propriétés de transmission).
Du fait que le nom de l’événement sera utilisé dans le DOM et que les majuscules seront transformées en minuscules, il est d’usage d’utiliser la convention de nommage kebab-case pour l’écriture des événements.
Abonnement à un événement
Puisque le déclenchement de l’événement se fait sur l’instance du composant CreatePolldleOption, il faut que l’abonnement s’effectue sur cette même instance. Nous allons donc utiliser la directive v-on
dont le nom de l’événement est celui que nous avons déclenché.
- Éditer le code du composant CreatePolldle en remplaçant le commentaire
<!-- Listening the removed-polldle-option event -->
par le code suivant.
1
2
3
4
5
6
7
8
9
10
11
<template>
...
<div
class="row justify-content-center"
v-for="currentPolldleOption in polldleOptions"
:key="currentPolldleOption.text"
>
<!-- Listening the removed-polldle-option event -->
<CreatePolldleOption :polldleOption="currentPolldleOption" v-on:removed-polldle-option="removedPolldleOption($event)"/>
</div>
</template>
Ce code a pour effet d’appeler la fonction removedPolldleOption($event)
où $event
contiendra l’objet transmis lors du déclenchement de l’événement.
- À titre d’exemple, voici le même code en utilisant la version simplifiée de la directive
v-on
.
1
2
3
4
5
6
7
8
9
10
<template>
...
<div
class="row justify-content-center"
v-for="currentPolldleOption in polldleOptions"
:key="currentPolldleOption.text"
>
<CreatePolldleOption :polldleOption="currentPolldleOption" @removed-polldle-option="removedPolldleOption($event)"/>
</div>
</template>
Invocation de service REST
Nous vous invitons à vous positionner dans le répertoire polldle-vue-13 pour profiter des codes qui vont illustrer cette section. Pensez à faire
$ npm install
pour installer les modules et$ npm run serve
pour démarrer l’exécution en mode développement.
Nous traitons dans cette section de la communication entre la couche web développée avec Vue.js et la couche serveur développée avec le langage Java. Nous avons déjà évoqué cela en montrant l’utilisation de l’objet EventSource
pour faire du push serveur et récupérer le flux des mises à jour des votes. Nous avions alors montré que le code produit ne concernait pas des concepts Vue.js, mais JavaScript. Il en est de même pour l’invocation de service web REST. Même si l’écosystème de Vue.js a fourni le plugin Vue Resource pour faciliter le développement d’appels de service web REST, celui-ci n’est plus recommandé. En effet, les développeurs de Vue.js ont décidé que fournir une bibliothèque spécifique à ce type de tâches était redondant face à la richesse de ce que peut proposer l’écosystème JavaScript.
Dans la suite, nous allons montrer deux façons pour invoquer un service web REST. La première est d’utiliser la nouvelle API JavaScript fetch introduite dernièrement qui se veut remplaçante à XMLHttpRequest. La seconde est d’utiliser la bibliothèque AXIOS.
Dans notre exemple, l’API fetch sera utilisée pour créer un nouveau PollDLE depuis le composant CreatePolldle, tandis que la bibliothèque AXIOS sera utilisée dans le composant VotePolldle. L’objectif est de vous montrer comment intégrer cette API et cette bibliothèque dans un code Vue.js.
Documentation de l’API REST
Nous présentons dans cette section, un détail de l’API REST de notre exemple afin de nous familiariser avec les différents services que nous allons appeler.
Deux ressources sont identifiées : un PollDLE et un Vote.
Le format des objets pour l’envoi et la réception sera du JSON pour toutes les méthodes (excepté celle qui s’occupera de l’initialisation du Server-Sent Event).
PollDLE
- Création d’un PollDLE
- POST
/polldles
- Entrée :
Polldle
- Sortie :
Polldle
(avec l’identifiant renseigné) - Retrouver un PollDLE par son identifiant
- GET
/polldles
- Entrée : pathURL (query) (identifiant du PollDLE)
- Sortie : Polldle
Vote
La ressource Vote est une sous-ressource de PollDLE.
- Création d’un vote
- POST
/polldles/{PATH_URL}/votes
(identifiant du PollDLE) - Entrée :
PolldleVote
- Lister tous les votes d’un PollDLE
- GET
/polldles/{PATH_URL}/votes
- Sortie :
PolldleResult
- Initialisation du flux (Server-Sent Event) pour la mise à jour des votes
- GET
/polldles/{PATH_URL}/votes/sse
- Format :
text/event-stream
Compiler et exécuter le code serveur
L’objectif de cette section est de montrer comment compiler et exécuter le code serveur développé avec Java afin de pouvoir tester les appels aux services web REST depuis l’application web. Les prérequis logiciels pour continuer sont Java et Maven.
- Ouvrir un nouveau terminal, se positionner à la racine du répertoire polldle-backend et exécuter la ligne de commande suivante pour compiler la couche serveur à partir de Maven.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ mvn clean package
...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for polldle-parent 0.4-SNAPSHOT:
[INFO]
[INFO] polldle-parent ..................................... SUCCESS [ 0.127 s]
[INFO] poddle-api ......................................... SUCCESS [ 1.040 s]
[INFO] polldle-server ..................................... SUCCESS [ 5.746 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7.028 s
[INFO] Finished at: 2020-06-30T21:28:55+02:00
[INFO] ------------------------------------------------------------------------
Maven compilera le code source, exécutera les tests unitaires et préparera les binaires finals. Ces derniers, disponibles dans le répertoire polldle-server/target sont composés des dépendances (ensemble de fichiers au format .jar) et de fichiers au format .class.
- Initialiser la variable d’environnement
KUMULUZEE_SERVER_HTTP_PORT
pour modifier le port d’écoute du serveur.
1
export KUMULUZEE_SERVER_HTTP_PORT=9991
- Saisir la ligne de commande suivante pour exécuter le serveur qui diffusera les services web REST.
1
2
3
4
5
$ java -cp "polldle-server/target/dependency/*:polldle-server/target/classes" com.kumuluz.ee.EeApplication
...
2020-06-30 21:29:53.508 INFO -- org.eclipse.jetty.server.AbstractConnector -- Started ServerConnector@4c402120{HTTP/1.1, (http/1.1)}{0.0.0.0:9991}
2020-06-30 21:29:53.508 INFO -- org.eclipse.jetty.server.Server -- Started @1932ms
2020-06-30 21:29:53.508 INFO -- com.kumuluz.ee.EeApplication -- KumuluzEE started successfully
Les services web REST sont désormais disponibles à cette adresse http://0.0.0.0:9991.
API Fetch
- Éditer le fichier CreatePolldle.vue en complétant la méthode
createPolldle()
par le code présenté ci-dessous (au niveau du commentaire// Call REST web service with fetch API
).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<script>
...
export default {
methods: {
createPolldle() {
var polldleObject = {
question: this.question,
polldleOptions: []
};
this.polldleOptions.forEach(element => {
var newPollOptionElement = { name: element.text };
if (element.text !== "") {
polldleObject.polldleOptions.push(newPollOptionElement);
}
});
// Call REST web service with fetch API
var request = new Request("http://127.0.0.1:9991" + "/polldles", {
method: "POST",
body: JSON.stringify(polldleObject),
headers: {
'Content-Type': 'application/json'
}
})
fetch(request).then(response => {
if (response.ok) {
return response.json();
} else {
this.errorMessage = "Problem to create a new Polldle.";
}
}).then(data => {
console.log(data.pathUrl);
}).catch((error) => {
this.errorMessage = "Problem to create a new Polldle.";
console.error(error);
});
},
...
},
...
}
</script>
...
Lors de la création d’un objet Request
, nous précisons, l’URL du serveur (qui sera remplacée par une variable d’environnement dans la partie 3), que la méthode HTTP utilisée sera du POST
, que le corps est l’objet polldleObject
et que le contenu sera au format JSON. Une première promesse retourne l’objet de la réponse si la requête envoyée au serveur s’est correctement déroulée. Une seconde promesse effectue le traitement de l’objet réponse, pour l’instant l’affichage de l’identifiant du Polldle. Dans la section routage, nous modifierons le traitement de la réponse pour rendre visible le composant VotePolldle.
Une documentation exhaustive sur l’API Fetch est disponible à cette adresse : https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
AXIOS
AXIOS est une bibliothèque JavaScript non disponible par défaut dans un projet Vue.js. Il faut donc l’intégrer dans le projet (package.json) et télécharger les dépendances.
-
Saisir la ligne de commande suivante
$ npm install axios
pour ajouter la bibliothèque AXIOS et compléter automatiquement le fichier package.json. -
Éditer ensuite le fichier VotePolldle.vue pour ajouter la dépendance de la bibliothèque JavaScript AXIOS au composant VotePolldle (remplacer le commentaire
// Add dependency to AXIO JavaScript library
par le code présenté ci-dessous).
1
2
3
4
5
6
7
<template>
...
</template>
<script>
// Add dependency to AXIO JavaScript library
import axios from 'axios';
...
- Compléter le fichier VotePolldle.vue au niveau du hook
created
en remplaçant le commentaire// To retrieve PollDLE information from REST web service
par le code présenté ci-dessous.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
...
<script>
import axios from 'axios';
...
export default {
name: "VotePolldle",
data() {
return {
...
};
},
created() {
// To retrieve PollDLE information from REST web service
axios.get("http://127.0.0.1:9991" + "/polldles", {
params : {
pathURL: this.$route.params.pathurl
}
}).then(response => {
if (response.status === 200) {
this.question = response.data.question;
this.polldleOptions = response.data.polldleOptions;
this.state = stateResult.WAITING_VOTE;
} else {
this.errorMessage = "Polldle can not be loaded.";
this.state = stateResult.ERROR;
}
}).catch(error => {
this.errorMessage = "Polldle can not be loaded.";
this.state = stateResult.ERROR;
console.error(error);
});
},
...
Ce code fait donc un appel au service web REST dédié à la récupération des informations d’un PollDLE.
- Enfin, compléter le fichier VotePolldle.vue au niveau de la méthode
vote()
en remplaçant le commentaire// To vote for a PollDLE from REST web service
par le code présenté ci-dessous.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<script>
import axios from 'axios';
...
export default {
...
methods: {
vote() {
// Prepare the data
var polldleVote = {
pathUrl: this.$route.params.pathurl,
polldleOptionResponses: [this.polldleOptionResponses]
};
// To vote for a PollDLE from REST web service
axios({
method: 'post',
baseURL: "http://127.0.0.1:9991" + "/polldles/" + this.$route.params.pathurl + "/votes",
data: JSON.stringify(polldleVote),
headers: {
'Content-Type': 'application/json'
}
}).then(response => {
if (response.status === 200) {
console.log(this.$route.params.pathurl);
} else if (response.status === 204) {
this.state = stateResult.VOTE_ERROR;
this.errorMessage = "Already voted !!!";
}
}).catch(() => {
this.state = stateResult.VOTE_ERROR;
this.errorMessage = "Problem to vote for this Polldle.";
});
},
}
};
</script>
Le code ajouté permet d’invoquer le service web REST dédié au vote (création d’une ressource Vote).
Routage avec Vue.js
Nous vous invitons à vous positionner dans le répertoire polldle-vue-14 pour profiter des codes qui vont illustrer cette section. Pensez à faire
$ npm install
pour installer les modules et$ npm run serve
pour démarrer l’exécution en mode développement.
Cette dernière section s’intéresse au routage de notre application Single-Page application. En fonction de l’état de l’URL, nous allons pouvoir choisir quel sera le composant à afficher.
- / : la création d’un PollDLE (afficher le composant CreatePolldle) ;
- /{id} : le vote d’un PollDLE ou
{id}
désigne l’identifiant du PollDLE (afficher le composant VotePolldle); - /{id}/result : le résultat des votes d’un PollDLE ou
{id}
désigne l’identifiant du PollDLE (afficher le composant ResultPolldle).
Vue.js propose un module appelé Vue-Router qui offre un mécanisme de gestion du routage. Vue-Router s’appuie sur un fichier de routage pour établir les différentes règles qui permettent de passer d’un composant à un autre en fonction de la valeur de l’URL.
Initialisation et activation du routage
-
Saisir la ligne de commande suivante
$ npm install vue-router
pour ajouter le module Vue-Router et compléter automatiquement le fichier package.json. -
Créer un dossier router à la racine du dossier src puis ajouter un fichier index.js en recopiant le code ci-dessous.
1
2
3
4
5
6
7
8
9
10
11
12
import Vue from 'vue'
import Router from 'vue-router'
import CreatePolldle from '@/components/CreatePolldle'
import VotePolldle from '@/components/VotePolldle'
import ResultPolldle from '@/components/ResultPolldle'
Vue.use(Router)
export default new Router({
// Règles de routage seront complétées dans la section suivante
})
Ce fichier index.js contrôle le routage de l’application. Les composants développés précédemment sont importés pour être utilisés dans les règles de routage (voir dans la section suivante). Le code Vue.use(Router)
permet d’utiliser le plugin Router de manière globale.
- Éditer le fichier App.vue afin de déléguer au routage le choix du composant en remplaçant le commentaire
<!-- Add router-view component -->
par le code présenté ci-dessous.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
<!-- Add router-view component -->
<router-view/>
<footerPolldle/>
</div>
</template>
<script>
import footerPolldle from "@/components/FooterPolldle.vue";
export default {
name: "app",
components : { footerPolldle }
};
</script>
Contrairement à la version précédente du fichier App.vue, le composant CreatePolldle ne sera pas explicitement utilisé. Cela sera le rôle du composant de routage disponible via la balise <router-view/>
.
- Éditer le fichier main.js en remplaçant les commentaires
// Import routing configuration
et// Enable routing
par le code présenté ci-dessous.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Vue from 'vue'
import App from './App.vue'
// Import routing configuration
import router from './router'
require('./assets/polldle.css')
Vue.config.productionTip = false
new Vue({
// Enable routing
router,
render: h => h(App)
}).$mount('#app')
Le code ajouté permettra d’utiliser le système de routage dans l’application complète.
Création de la table de routage
Le composant routage est désormais configuré et activé. Nous allons détailler comment définir des règles de routage.
- Éditer le fichier router/index.js en complétant par le code présenté dans l’objet
Router
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import Vue from 'vue'
import Router from 'vue-router'
import CreatePolldle from '@/components/CreatePolldle'
import VotePolldle from '@/components/VotePolldle'
import ResultPolldle from '@/components/ResultPolldle'
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'CreatePolldle',
component: CreatePolldle
},
{
path: '/:pathurl',
name: 'VotePolldle',
component: VotePolldle
},
{
path: '/:pathurl/result',
name: 'ResultPolldle',
component: ResultPolldle
}
]
})
Trois règles de routage ont été définies dans routes
correspondant aux besoins exprimés en début de section. Pour chaque élément d’une route, trois propriétés sont utilisées : path
, name
et component
. La valeur proposée dans la propriété path
correspond à un pattern qui doit être satisfait pour activer une route. Si la route est activée alors c’est le composant donné par la propriété component
qui sera retourné. À titre d’exemple, pour la règle de routage définie par path: '/:pathurl/result
, si elle est active (/123/result), c’est le rendu du composant ResultPolldle qui sera intégré à la place de la balise <router-view>
du fichier App.vue.
L’option mode: 'history'
permet d’utiliser l’API history.pushState
et les URL ressembleront à cela http://localhost:8080/3/result. Dans le cas contraire, le mode par défaut de vue-router est le mode hash qui utilise la partie hash de l’URL pour simuler une URL complète. Les URL ressembleraient à cela http://localhost:8080/#/4/result. L’utilisation du mode history
aura un impact pour le déploiement de l’application puisque, quelle que soit l’URL utilisée, un code d’erreur 404 sera retourné. Nous reviendrons sur cet aspect dans la dernière partie de cet article.
Forcer le changement de route
À cet instant si vous testez l’application, vous ne pourrez changer l’affichage de l’application que par l’intermédiaire de la barre d’adresse. Toutefois, nous aimerions pouvoir activer une route quand une opération se termine. Par exemple, à la fin de la création d’un PollDLE, nous aimerions pouvoir voter (composant VotePolldle) ou, à la fin d’un vote, nous aimerions pouvoir visualiser les résultats des votes (ResultPolldle). Nous allons utiliser programmatiquement le mécanisme de navigation via la propriété d’instance this.$router
disponible dans une instance de composant Vue.js.
- Éditer le fichier CreatePolldle.vue en remplaçant le commentaire
// Programmatic navigation to display VotePolldle component
par le code présenté ci-dessous.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
<script>
...
export default {
...
methods: {
createPolldle() {
...
fetch(request).then(response => {
...
}).then(data => {
console.log(data.pathUrl);
// Programmatic navigation to display VotePolldle component
this.$router.push({
name: "VotePolldle",
params: { pathurl: data.pathUrl }
});
}).catch((error) => {
...
});
}
}
}
</script>
Lors de la réception de la réponse du service web REST, une nouvelle entrée est ajoutée dans la pile de l’historique this.$router.push
. Le nom de la règle de routage est transmis name: "VotePolldle"
ainsi que l’identifiant du PollDLE nouvellement créé pathurl: data.pathUrl
(par exemple 3
). vue-router va donc rechercher une règle portant ce nom et l’activer. Dans ce cas, c’est la règle permettant de retourner le composant VotePolldle
qui sera déclenchée.
- Éditer le fichier VotePolldle.vue en remplaçant le commentaire
// Programmatic navigation to display ResultPolldle component
par le code ci-dessous.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
<script>
...
export default {
...
methods: {
vote() {
...
axios({
...
}).then(response => {
if (response.status === 200) {
console.log(this.$route.params.pathurl);
// Programmatic navigation to display ResultPolldle component
this.$router.push({
name: "ResultPolldle",
params: { pathurl: this.$route.params.pathurl }
});
} else if (response.status === 204) {
...
}
}).catch(() => {
...
});
}
}
}
</script>
Le même code est obtenu que pour le composant CreatePolldle.vue. À noter que le paramètre transmis params: { pathurl: this.$route.params.pathurl }
n’est pas issu de la réponse, mais de la valeur de la route courante.
Conclusion et remerciements
Cette deuxième partie a présenté les principaux concepts de Vue.js au travers d’un exemple complet PollDLE.
Dans la partie suivante, nous nous intéresserons à la problématique de déploiement d’une application Vue.js via l’utilisation de Docker.
Nous tenons à remercier Claude Leloup pour sa relecture orthographique.
Ressources
- Découvrez ou approfondissez votre connaissance de Vue 3 : un livre avec des exercices régulièrement mis à jour.
- Introduction au framework Vue.js par l’exemple : un TP complet sur Vue.js proposé par Serge Tahé.
- Vue.js Tutorials From The Official Vue Docs : des tutoriels basés sur des exemples de la documentation officielle.
- The Vue.js Cheat Sheet : un aide mémoire sur Vue.js.
- Create & Publish Web Components With Vue CLI 3 : pour publier un composant Vue.js comme un Web Components.
- Build Targets with VueCLI : la documentation de VueCLI pour générer un Web Component.
- Workshop Materials for my Introduction to Vue.js Workshop : un dépôt Github contenant des exemples pratiques.
- Vue.js REST API Consumption with Axios : un billet sur l’utilisation de la bibliothèque Axios avec Vue.js.
- Programmation : Conventions de nommage et d’écriture de code : un article sur les différentes conventions de nommage.
- Creating Vue.js Component Instances Programmatically : un article qui explique comment créer un composant programmatiquement.
Je suis Mickaël BARON Ingénieur de Recherche en Informatique à l'ISAE-ENSMA et membre du laboratoire LIAS le jour
Veilleur Technologique la nuit
#Java #Container #VueJS #Services #WebSemantic
Derniers articles et billets
Vous pouvez laisser un commentaire en répondant à ce Tweet.