Client HTML - Vue.js
Coté client, JavaScript va nous permettre de modifier le rendu de la page HTML. Pour cela, l’API DOM doit être utilisée. Cette API fournit une représentation structurée du document et une manière d’accéder à cette structure. Cela permet notamment :
- de parcourir l’arbre des éléments
- de modifier la structure de cet arbre (ajouter, enlever, déplacer des éléments)
- d’accéder aux informations des nœuds (contenu, attribut, etc.)
- de réagir à des événements (interactions utilisateur)
- etc.
L’objet global document
est l’objet principal d’un document HTML.
DOM vs. Vue.js#
Pour simplifier et accélérer le développement, des librairies comme Vue.js peuvent être utilisées. Son principal objectif est de mettre à jour le contenu d’une page HTML (au fil de la vie de l’application), en s’assurant que les développeurs ne modifient pas eux-mêmes le DOM, le tout, en structurant le code client.
Pour cela, des conventions sont mises en place.
Vue.js#
- La documentation officielle de Vue.js
- Voir le tutoriel détaillé dans la partie JavaScript/Outils
Pour commencer à utiliser Vue, il suffit de charger la librairie. Ici on utilisera Vue 3, avec le style «Options API».
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
Et de déclarer une nouvelle instance de Vue avec vos données dans data
(coté JS), et ces mêmes données coté template/rendu (donc HTML)
<div id="app">
{{ message }}
</div>
Vue.createApp({
data() {
return {
message: 'Hello Vue !',
};
},
}).mount('#app');
Notez que data
est une fonction qui retourne un objet littéral contenant nos données.
On parle alors de binding. Les données coté JS sont liées au rendu HTML. Dans Vue, le binding classique se fait avec la notation {{ }}
. Pour binder la valeur à un attribut, il faut utiliser la directive v-bind:
<div v-bind:title="message"></div>
Ou simplement la version raccourcie avec deux-points
<div :title="message"></div>
Pour éviter de mettre trop de logique dans le template (HTML), on peut créer des propriétés calculées, à définir dans l’option computed
(et non plus data
). Par exemple, pour afficher le message en sens inverse
Vue.createApp({
data() {
return {
message: 'Hello Vue !',
};
},
computed: {
reversedMessage () {
// `this` pointe sur l'instance Vue
return this.message.split('').reverse().join('');
},
},
}).mount('#app');
<div id="app">
{{ reversedMessage }}
</div>
Notez que reversedMessage
est une fonction, mais qui est utilisée telle une propriété
Pour les formulaires, il est souvent nécessaire de binder dans les 2 sens. Du code vers l’interface, mais également de l’interface vers le code (case à cocher, champ texte, etc.). Pour cela, on utilisera la directive v-model
.
<input type="text" v-model="message">
<input type="checkbox" v-model="cochee">
Vue.createApp({
data() {
return {
message: 'Hello Vue !',
cochee: true,
};
},
}).mount('#app');
Ainsi, il n’est pas forcément nécessaire de créer d’évènements. Au chargement de la page, le champ texte contient le texte "Hello Vue !"
et la checkbox est cochée. Si l’utilisateur change le texte depuis l’interface, la variable message
est mise à jour dans le code. Si il décoche la checkbox, idem, la variable cochee
est mise à jour. Et les éventuelles propriétés calculées sont recalculées en temps-réel.
Vue propose également d’autres directives intéressantes qui permettent d’ajouter de la logique dans vos templates :
v-if
/v-else
pour de l’affichage conditionnelv-for
pour générer des éléments HTML à la volée
Les évènements#
La directive v-on:
permet d’ajouter des évènements qui appelleront des méthodes stockées dans methods
Vue.createApp({
data() {
return {
message: 'Hello Vue !',
};
},
methods: {
hello () {
alert('Hello !');
},
},
}).mount('#app');
<button v-on:click="hello">Say Hello</button>
Ou simplement la version raccourcie avec @
<button @click="hello">Say Hello</button>
Pour passer des variables, vous utiliserez
<button @click="hello('world', $event)">Say Hello</button>
Il existe également des modificateurs pour gérer quelques actions classiques :
<!-- call event.preventDefault() -->
<form @submit.prevent="submit"></form>
<!-- call submit() when the keyCode is 13 (enter) -->
<input @keyup.13="submit">
Exercice - Tweetbox Twitter X#
Le but de cet exercice est de créer le champ texte sur le principe de celui utilisé par Twitter. C’est à dire, un simple champ texte, limité à un nombre défini de caractères, un bouton de validation et un bouton d’ajout de photo qui limite encore plus le nombre de caractères.
Coté HTML (voir code plus bas) :
- créez un champ texte pour écrire le message
- affichez la limite de caractères max dans un paragraphe (10 pour vos tests)
- créez un bouton de validation
- chargez la librairie Vue
En JS :
- créez une nouvelle instance de
Vue
- utilisez un élément parent pour binder l’application
- stockez les données globales dans
data
(le texte, le nombre de caractères max) - utilisez
max
dans votre HTML
- créez une propriété calculée
nombreRestants
qui renvoie le nombre de caractères restants et utilisez cette valeur pour l’affichage (à la place demax
) - utilisez
v-model
pour mettre à jour la valeur du texte (le nombre de caractères restants devrait se mettre à jour automatiquement) - créez une seconde propriété calculée pour vérifier si la limite est atteinte (doit retourner
true
oufalse
). Utilisez cette propriété pour :- afficher le texte en rouge (en ajoutant une classe)
- désactiver le bouton (attribut
disabled
)
Ensuite :
- en HTML, ajoutez une case à cocher pour simuler l’ajout de une photo (nous ne voulons pas créer la fonctionnalité) et un label associé avec le texte «Pas de photo»
- coté JS, ajoutez dans
data
une propriétéphoto
qui vautfalse
- utilisez
v-model
pour binder cette valeur - modifiez la propriété
nombreRestants
pour enlever ou pas des caractères en fonction de si la case est cochée ou non - le texte du label doit devenir «✓ Photo ajoutée»
- testez les différents cas dans l’interface
Code HTML envisagé (sans code Vue)
<div id="app">
<form action="">
<textarea></textarea>
<p>10</p>
<button>Tweet</button>
<input id="photo" type="checkbox">
<label for="photo">Pas de photo</label>
</form>
</div>
Instance Vue
Vue.createApp({
data() {
return {};
},
computed: {
}
}).mount('#app');
Enfin, il faut pouvoir simuler l’action de tweeter et que ses tweets apparaissent sous la tweetbox (la partie serveur n’est pas gérée ici). Pour cela :
- coté JS :
- créez une liste (vide) de tweets dans
data
- ajoutez l’événement
submit
sur le formulaire, et :- empêchez la validation classique (utilisez
@submit.prevent
directement) - ajoutez les données du tweet actuel (message, image) dans la liste sous forme d’objet littéral
- empêchez la validation classique (utilisez
- créez une liste (vide) de tweets dans
- coté HTML (template) :
- générez des blocs HTML basés sur cette liste (utilisation de
v-for
,v-if
) - note: pour les images, utilisez un service de récupération d’image aléatoire comme https://picsum.photos avec un paramètre
random
, exemple:<img src="https://picsum.photos/200/200?random=2">
- stylez en CSS
- générez des blocs HTML basés sur cette liste (utilisation de
Exercice - Validation de formulaire#
En JavaScript, il est possible de vérifier et valider les champs de formulaire avant d'envoyer les informations au serveur. Cela permet de s'assurer que les données sont correctement formatées (ex: email bien formé, n° de téléphone à 10 chiffres, date ...).
Note: Nous n'aborderons pas ici le traitement des données du formulaire qui s'effectue sur le serveur. Une deuxième vérification des données est alors nécessaire pour valider les formulaires qui n'auraient pas été vérifié par JavaScript (JavaScript désactivé sur le navigateur, tentative de hack, etc.).
Prenons comme exemple ce formulaire HTML :
Formulaire HTML
<form action="#" method="get">
<fieldset>
<legend>Inscription</legend>
<p><label>Nom<input type="text" name="nom"></label></p>
<p><label>Prénom<input type="text" name="prenom"></label></p>
<p><label>Tel<input type="text" maxlength="10" name="telephone"></label></p>
<p><label>Email<input type="text" name="mail"></label></p>
<p><button>Envoyer</button></p>
</fieldset>
</form>
Le résultat est le suivant :
- premièrement, créez votre application Vue
- créez 4 propriétés pour les 4 champs, et binder avec
v-model
Vue.createApp({
data() {
return {
nom: '',
prenom: '',
tel: '',
mail: ''
};
}
}).mount('...');
<input type="text" name="nom" v-model="nom">
<input type="text" name="prenom" v-model="prenom">
<input type="text" maxlength="10" name="tel" v-model="tel">
<input type="text" name="mail" v-model="mail">
- créez 4 propriétés calculées, pour chacun des champs, pour en vérifier la conformité
- champ
nom
etprenom
non vides, champtel
numérique à 10 chiffres, champemail
un email
- champ
Vue.createApp({
computed: {
validNom() {
return this.nom != '';
},
validTel() {
return this.tel.length == 10 && !isNaN(this.tel);
},
validMail() {
let regex = /^[a-z0-9]+([_|\.|-]{1}[a-z0-9]+)*@[a-z0-9]+([_|\.|-]{1}[a-z0-9]+)*[\.]{1}[a-z]{2,6}$/;
return regex.exec(this.mail);
}
}
});
- ajoutez ensuite l’événement
submit
sur le formulaire, et exécutez une fonction - cette fonction doit empêcher la validation du formulaire si au moins un champ est mal rempli
<form action="#" method="get" @submit="validate">
Vue.createApp({
methods: {
validate(event) {
if ( ... ) {
event.preventDefault();
}
}
}
});
Enfin, ajoutez une classe erreur
sur chaque champ mal rempli. Pour cela :
- créez une propriété
errors
, qui doit être un objet littéral avec 4 propriétés pour les 4 champs
Vue.createApp({
data() {
return {
errors: {
nom: false,
prenom: false,
tel: false,
mail: false
}
};
}
});
- utilisez ces propriétés pour ajouter conditionnellement une classe
<input type="text" name="nom" v-model="nom" :class="{ error: errors.nom }">
- pensez à changer à
true
dans la fonctionvalidate
Vue.createApp({
methods: {
validate(event) {
this.errors.nom = !this.validNom;
...
}
}
});
- ajoutez également en CSS le changement de style
.error {
background: red;
}