Vue.js est un framework
permettant de créer des interfaces utilisateur web (HTML, CSS, Javascript/TypeScript),
en adoptant une approche orientée composants.
Il est en général utilisé pour créer des
applications web monopage
aussi appelées
Single-Page Application (SPA).
Les SPA se distinguent des pages web classiques dans le sens où elles permettent de fournir à l'utilisateur
une interface telle qu'on la trouverait dans une application "de bureau" : les interactions avec la SPA
se font sans rechargement global de la page web.
Il serait impossible (et inutile) de vous présenter ici tous les détails de Vue.js. Ce cours n'est donc qu'une introduction aux principales fonctionnalités...
Vous pouvez trouver toute la documentation officielle sur
https://v3.vuejs.org/
Pourquoi utiliser un tel Framework ?
Tout d'abord parce que les applications web modernes sont en général proposées
aux utilisateurs sous forme de SPA, et Vue.js facilite grandement la création
de telles applications.
D'une manière générale, Vue.js facilite la mise en place de mécanismes qui sont
souvent plus lourds à réaliser en Javascript / JQuery.
Prenons un exemple : vous qui êtes
maintenant des pros du Javascript (cf. super_cours),
essayez de réaliser une page contenant l'exemple ci-dessous en Javascript (sans Vue.js).
Resultat
{{ name_01 }}
Nickname : {{ name_01}} The Terrible
Mail : {{name_01}}@truc.fr
Fiche de {{ name_01 != '' ? name_01 : '???' }}
Spoiler: avec Vue.js, c'est bcp plus simple :))
Les Bases du Framework
Activation version Prototypage (Apprentissage)
Comme indiqué dans la
documentation
il est possible d'activer Vue.js dans une page web en utilisant simplement une balise
script :
Cette manière de travailler avec Vue.js ne doit être utilisée que pour du prototypage ou
la création de composants très simples.
NB: Pour des applications conséquentes,
il faut utiliser les outils de développement tels
Vue CLI
(que nous verrons plus loin dans ce cours).
Application Vue
La création d'application Vue.js au sein d'une page web contient 2 parties principales :
Une balise quelconque (ex. un div avec un id (unique!)
contenant des expressions qui seront utilisée par le framework pour faire du
rendu dynamique(Vue va se charger de lier des données au DOM de la balise)
Une balise script contenant les propriétés et méthodes
de l'application.
Data & Rendu déclaratif : {{ ... }}
Grâce à la notation {{objet }}, Vue permet de créer des
éléments HTML complexes dynamiquement liés aux données de l'application.
<!-- Balise racine de l'application : -->
<!-- le DOM va être lié à Vue.js pour faire du rendu dynamique -->
<div id="app-01">
{{ message_01 }} <!-- référence à la propriété message_01 : dynamique !!! -->
<br>
{{ message_02 }} <!-- référence à la propriété message_02 : dynamique !!! -->
</div>
<script> // Script déclarant les données et méthodes de l'application -->
// définitions
const Application_01 = {
data() {
return {
message_01: 'Vue.js vous dit bonjour.', // les propriétés sont séparées par ','
message_02: 'C\'est coooool !!!'
}
}
}
// instanciation de l'application, et liaison avec la balise racine de l'application
Vue.createApp(Application_01).mount('#app-01')
</script>
Resultat
{{ message_01 }}
{{ message_02 }}
Génération de code HTML : v-html
La directive v-html="code_html", permet d'injecter du code HTML dans les balises.
<!-- Balise racine de l'application dont le DOM va être lié à Vue.js pour faire du rendu dynamique -->
<div id="app-01-bis">
<div v-html="message"></div>
</div>
<script> // Script déclarant les données et méthodes de l'application -->
// définitions
const Application_01_bis = {
data() {
return {
message: '<b style="color: blue">Vue.js vous dit bonjour !!!</b>'
}
}
}
// instanciation de l'application, et liaison avec la balise racine de l'application
Vue.createApp(Application_01_bis).mount('#app-01-bis')
</script>
Resultat
Méthodes
Les applications Vue peuvent déclarer et utiliser des méthodes qui contiennent des
instruction écrites en Javascript. Les méthodes peuvent être appelées dans les
instructions de rendu ou par d'autres méthodes (grâce à this).
Nous verrons ci-après
(cf. Évènements)
que les méthodes peuvent aussi servir de handler et être déclenchées par des évènements.
Elles permettent au besoin de manipuler
les propriétés décritss dans la partie data, ce qui a (généralement) un
effet direct sur le rendu de l'application.
Comme en Javascript, il est possible d'enregistrer des écouteurs d'évènements sur les
gestionnaires associés des balises HTML.
L'enregistrement dans un gestionnaire d'évènements est réalisée grâce à la directive
v-on:event=handler qui doit être insérée directement
dans la balise de l'objet DOM visé.
event représente le type d'évènement qu'on souhaite écouter (ex. click). handler représente la méthode de l'application qui sera déclenchée.
Il est aussi possible d'écouter d'autres types d'évènements, de créer des handlers paramétrés, et
au besoin de récupérer l'objet évènement qui a été généré (comme en Javascript).
Pour les évènements du DOM, on peut remplacer le v-on: par un @
(ex. @click="handler")
Écouter des évènements, passer des paramètres, récupérer l'event, ...
<div id="app-03">
Prénom : {{ firstName }}<br>
Nom : {{ lastName }} <br><br>
<button @click="remplirTexte"
@mouseenter="changerTexteBouton('Allez clique !')"
@mouseout="changerTexteBouton('Reviiiiennnnnsss !', $event)"
>{{ txt_du_bouton }}
</button> (clicks : {{ nb_clicks }})
</div>
<script>
const App_03 = {
data(){ return{
firstName: '',
lastName: '',
txt_du_bouton: 'Remplir',
nb_clicks: 0
}},
methods: {
remplirTexte(){
this.firstName = 'Gregory'
this.lastName = 'Bourguin'.toUpperCase() // pour le fun
this.nb_clicks++
},
changerTexteBouton(msg, event){
this.txt_du_bouton = msg
// récupération de la cible de l'évènement et modification du style
event.target.style.color = 'red'
},
}
}
Vue.createApp(App_03).mount('#app-03')
</script>
Resultat
Prénom : {{ firstName }}
Nom : {{ lastName }}
(clicks : {{ nb_clicks }})
Conditions : v-if
v-if="condition" permet d'indiquer à Vue si un élément du DOM doit être présent dans
la page ou non. L'élément sera présent uniquement quand la condition est vérifiée.
Bien entendu, tout cela est dynamique et la condition peut être dépendante des données
de l'application... dont la valeur peut évoluer au cours du temps...
La directive v-model="property" permet se lier l'input
d'un formulaire à une propriété de l'application.
Cette liaison est bi-directionnelle dans le sens où une modification de la valeur
de l'input sera répercutée sur la propriété (et donc ses autres apparitions dans la page
seront mises à jour), et une modification de la propriété sera répercutée dur le champ
qui l'utilise.
Si v-model est la plupart du temps suffisant pour lier un input à une variable,
il peut aussi être intéressant de noter que v-model correspond en réalité à une paire liant
un évènement et une valeur grâce à v-on & v-bind.
Pour donner un exemple, les 2 extraits de code ci-dessous sont écrits différemment mais font exactement
la même chose sur un input de type text.
Vous avez maintenant tout ce qu'il faut pour réaliser par vous même l'exemple du tout début
du cours, mais cette fois-ci en Vue.js :)
Boucles : v-for
v-for='(obj, index) in list' est une directive qui permet de générer
une liste d'éléments HTML à partid des données contenues dans une liste d'objets Javascript.
Encore une fois, la génération des éléments est dynamique, c'est à dire que toute modification
à la list sera immédiatement répercutée.
NB : on peut aussi ne pas utiliser l'index avec un simple : v-for='obj in list'
NB: vous pouvez importer l'objet countries en commençant votre script comme ci-dessous. (Il faut un serveur web pour qu'il n'y ait pas d'erreur CORS à l'exécution.)
<script type="module">
import { countries } from "../js/sample_countries.js"; // NB: mettre votre chemin
// la suite ici
</script>
Il est possible démarrer un serveur local (ici sur le port 8000) dans une console avec :
- En Python (3) : python -m http.server 8000
- En PHP : php -S localhost:8000
Attributs des balises : v-bind et :
La directive v-bind:attribute="value" permet de lier des objets
aux attributs (e.g. id, href, style, class...) des balises HTML de l'application.
v-bind: peut aussi être simplement remplacé par :
Liaison sur la valeur 'globale' de l'attribut
Ex. sur l'attribut style
<div id="app-08">
<fieldset>
<legend>Global Style Tester</legend>
<div style="display: flex ; align-items: center">
<input v-model="custom_style"
type="text" class="form-control input-sm"
placeholder="Enter style here"
>
<button @click="custom_style = ''" class="btn btn-warning">Clear</button>
</div>
</fieldset>
<br>
<div v-bind:style="custom_style">Un div qui a du style grâce à "v-bind"</i></div>
<br>
<div :style="custom_style">Un div qui a du style grâce à ":"</div>
</div>
<script>
Vue.createApp({
data(){ return {
custom_style: 'padding: 10px ; background: black ; color: greenyellow ;'
}},
}).mount('#app-08')
</script>
Comme nous l'avons vu au point précédent, il est possible de lier l'attribut class
à un objet (:class est le raccourcis pour v-bind:class). Cependant,
Vue permet d'associer chaque classe à une variable ou expression qui conditionnera son
application à l'élément visé.
:class="{ classe1: cond1, ..., classN: condN }" rend l'application
d'une classeX dépendante de l'expression booléenne condX.
<style>
.app-10-basic{
background: black;
height: auto;
}
.app-10-higlight{
color: greenyellow;
font-weight: bolder;
}
.biiig{
font-size: 2em;
text-transform: capitalize;
}
</style>
<div id="app-10">
<fieldset>
<legend>Class Tester</legend>
<div style="display: flex ; flex-direction: row ;">
<div class="form-group">
<label class="form-check-label" for="app-10-c1">Hilighted</label>
<input v-model="hilighted"
type="checkbox" class="form-check-input" id="app-10-c1">
</div>
<div style="width: 50px"></div>
<div class="form-group">
<label class="form-check-label" for="app-10-c2">Big</label>
<input v-model="big"
type="checkbox" class="form-check-input" id="app-10-c2">
</div>
</div>
</fieldset>
// Les valeurs de hilighted et big déterminent l'application des classes CSS
<div :class="{ 'app-10-higlight': hilighted, biiig: big }"
class="app-10-basic form-control">
Un div qui a du style !
</div>
</div>
<script>
Vue.createApp({
data(){ return {
hilighted: true,
big: false,
}},
}).mount('#app-10')
</script>
Resultat
// Les valeurs de hilighted et big déterminent l'application des classes CSS
L'application Vue peut parfois avoir besoin d'afficher des propriétés dont la valeur résulte
d'un calcul complexe (trop complexe pour être directement écrit dans les {{ ... }}.
Le champ computed permet de créer des propriétés d'application qui seront
calculées (et constamment mises à jour) dynamiquement.
Les propriétés computed ressemblent fortement à des méthodes qui seraient
appelées dans le code HTML. Il existe cependant une différence : les méthodes sont exécutées
à chaque apparition dans le code, alors que les résultats des propriétés calculées sont
mises en cache et ne sont recalculés que lorsque leurs dépendances sont modifiées.
En utilisant computed
(et tout ce qu'on a vue jusqu'à présent :) ),
réalisez la page ci-dessous.
Fichiers : sample_countries.js,
baseline_face_black_48dp.png --> Attention à la mise en forme <--
TP 01
Réalisez la page ci-dessous.
Quelques remarques à prendre en compte :
Testez bien tout pour voir ce qu'il faut faire !
J'ai bloqué le pays à "France" (dans un input de type texte) pour être cohérent avec le format du Téléphone...
Mettez bien en place la gestion des erreurs (couleur rouge + messages sous les inputs) !
Mettez bien en place le formatage automatique du téléphone dans la carte :
1er chiffre tt seul, puis des paires.
Vous remarquerez que le bouton "Enregistrer" est désactivé tant que tous les champs ne sont pas
correctement remplis.
Lors du click sur "Enregistrer", j'ai utilisé une boite de dialogue
Modal
de Bootstrap.
J'ai utilisé .replaceAll(...), .trim(), .substring(...) et RegExp
pour faire les vérifications / transformations.
J'ai "abusé" des propriétés "computed".
Vous remarquerez que l'input du téléphone empêche d'entrer des espace...
Vous n'êtes pas obligés d'en faire de même.
Cependant, si vous voulez essayer: dans l'input, j'ai remplacé le v-model
par une paire @input="..." (un handler pour l'évènement), et un :value="..."
pour la valeur affichée dans l'input.
Résultat attendu. --> Attention à la mise en forme <--
Les Observateurs : watch
Il est parfois nécessaire de créer des traitements complexes qui doivent être déclenchés uniquement
lorsque la valeur d'une (ou plusieurs) autre(s) propriété(s) a(ont) changé.
NB: c'est ce que font les propriétés computed, mais nous parlons ici de traitements
plus complexes qu'un "simple" calcul de valeur.
Le champ watch permet de créer des observateurs de propriétés qui ne seront déclenchés
que lorsque la valeur de la propriété observée a changé.
Dans l'exemple suivant, les watchers sont utilisés pour :
Rendre les propriétés kg et g interdépendantes, c.a.d. que la modification
de l'une, entraine la mise à jour de l'autre : il ne s'agir donc pas de faire un computed
"classique" car la dépendance qui nous intéresse ici est bidirectionnelle.
(NB: ...bon ...il serait en fait possible de le faire avec un computed + des
accesseurs, mais ce n'est pas le sujet ici ;) ).
Déclencher l'apparition d'un message (uniquement) lorsque la valeur de la propriété
conclusion (qui est elle-même computed !) a changé.