Pour votre progression, c'est très important de finir le sujet précédent avant de commencer celui-ci.
Vous devez travailler connectés (lien « Connexion » en haut à droite de la page). Sinon, vous rencontrerez des problèmes d'avancement et vous risquez d'être comptés absent. En étant connectés vous retrouverez l'endroit où vous en étiez à la séance suivante.
Où s’exécute le code suivant ?
fs.readFile
npm init
console.log('bonjour')
SELECT * FROM chats WHERE nom='minou'
import express from 'express';
Quelle commande faut-il taper dans un terminal pour accéder à une console JS ?
Oui, c'est « import »
C'est vrai qu'on peut utiliser require, mais ce n'est pas le choix qu'on a fait dans ces sujets.
Non. « npm » est une commande qui s'utilise sur la ligne de commande.
Ici, on demande ce qu'il faut écrire dans un fichier JS.
Non. « node » est une commande qui s'utilise sur la ligne de commande.
Ici, on demande ce qu'il faut écrire dans un fichier JS.
import * as abcd from 'node:fs/promises';
await XYZ.readFile('exemple.txt');
Que faut-il écrire à la place de XYZ ?
abcd
import * as abcd from 'node:fs/promises';
Oui, cette ligne veut dire :
« importer tout le contenu de la librairie « node:fs/promises » dans un objet qu'on veut appeler « abcd » »
fs
import * as abcd from 'node:fs/promises';
Non, cette ligne veut dire :
« importer tout le contenu de la librairie « node:fs/promises » dans un objet qu'on veut appeler « abcd » »
Lire un fichier est une opération:
lente
Lire un fichier est lent, comparé à l’exécution de quelques lignes de code. En plus l'opération peut bloquer, en cas de problème.
rapide
Lire un fichier est lent, comparé à l’exécution de quelques lignes de code. En plus l'opération peut bloquer, en cas de problème.
Quel est le type renvoyé par fs.readFile ici ?
import * as fs from 'node:fs/promises';
fs.readFile('exemple.txt')
Oui, c'est bien « Promise ».
Comme readFile peut prendre du temps, et qu'on ne veut pas bloquer le JS en attendant, readFile nous renvoie rapidement une promesse.
Quand cette promesse sera tenue, on pourra récupérer le contenu de ce fichier.
Il y a deux manières de faire quelque-chose après qu'une promesse soit tenue:
Quel est le mot clé de la deuxième manière de faire ?
Non, then est la première manière. Il faut fournir une fonction à then qui sera appelée plus tard:
p.then(function(){...});
Habituellement on utilise:
let fichier=await fs.readFile('exemple.txt');
Mais on pourrait décomposer:
let promesse=fs.readFile('exemple.txt');
let fichier=XYZ;
Que faut-il écrire à la place de XYZ (tout en utilisant await) ?
Vous n'utilisez pas await
Reprenons: Il y a deux manières de faire quelque-chose après qu'une promesse soit tenue:
Utilisez la première approche pour afficher dans la console le contenu du fichier exemple.txt
Rappel : fs.readFile('exemple.txt')
Vous êtes sur la bonne voie. La réponse commence bien par:
fs.readFile('exemple.txt').then( f=> ...
Le problème est après.
Vous êtes sur la bonne voie. La réponse commence bien par:
fs.readFile('exemple.txt').then( ...
Mais après vous créez une fonction sans paramètre. Rappelez vous que vous devez récupérer le contenu du fichier...
Vous êtes sur la bonne voie. La réponse commence bien par:
fs.readFile('exemple.txt').then( ...
Le problème est après.
Indice: votre réponse doit commencer par fs.readFile
Quelle commande faut-il taper pour démarrer un nouveau projet Node.js ?
Quel fichier est crée par la commande
npm init
Presque! Il y a une petite erreur.
En Node.js, quelle commande faut-il taper dans le terminal pour pouvoir utiliser une librairie appelée abcd ?
Non. « import » s'utilise dans le JS, pas dans le terminal.
En général, combien de paquets sont installés par la commande suivante ?
npm install abcd
Plus de 2
0
1
2
Dans quel répertoire sont téléchargés les paquets installés avec
npm install abcd
Comment s’appelle le framework Node.js permettant de créer un serveur web qu'on a utilisé ?
Quelles affirmations sont vraies ?
En Express, on crée notre propre serveur web
En PHP, on crée notre propre serveur web
En PHP, il y a une boucle d'événements
Quand on lit un fichier dans Express, il faut utiliser la programmation asynchrone (callback, Promesses, await)
Quelles affirmations sont vraies ?
Quand Express reçoit plusieurs requêtes, il les traites les unes après les autres
Quand Apache+PHP reçoit plusieurs requêtes simultanées, il les traites les unes après les autres
Express crée un processus différent pour chaque requête reçue
Dans Express le code JS doit être non-bloquant
En PHP le code doit être non-bloquant
Quelles affirmations sont vraies ?
Express permet d'obtenir une meilleure performance parce-qu’on n'a pas besoin de plusieurs processus.
La programmation asynchrone (callback, Promesses, await) simplifie notre code (par rapport à la programmation synchrone du PHP)
En PHP un processus peut prendre plusieurs centaines de ms avant de finir
Express se trouve dans « app ».
Que faut-il écrire ?
Utilisez une fonction fléchée, avec des accolades, et un deuxième paramètre appelé « reponse ».
Vous êtes sur la bonne voie. La réponse commence bien par:
app.get('/hello' ...
Le problème est après.
Vous êtes sur la bonne voie. La réponse commence bien par:
app.get( ...
Le problème est après.
Revenons à nos profils du sujet précédent !
Attention: il y a d'importants changements dans tous les fichiers. Il faut tous les reprendre...
Rappels pour recréer la structure, si vous avez perdu le répertoire « profils »:
app.js
import express from 'express';
import * as fs from "node:fs/promises";
const app = express();
app.use(express.static('public'));
app.listen(3000, () => {
console.log('Notre app « profils » écoute sur le port 3000')
});
app.get('/profil/:id', async (requete, reponse) => {
console.log('Requete GET /profil/:id reçue');
let id=requete.params.id;
let profilsTexte=await fs.readFile('profils.json','utf8');
let profils=JSON.parse(profilsTexte);
reponse.send(profils[id]);
})
profils.json
{
"p132": {"id": "p132", "nom": "Minou", "sexe": "homme", "portrait": "https://moodle.iutv.univ-paris13.fr/img/bjs2/chat2.svg", "messages": 54, "description": "Miaou ? 🐁 🐭 ? ", "likes": 132 },
"p125": {"id": "p125", "nom": "L. Neige", "sexe": "femme", "portrait": "https://moodle.iutv.univ-paris13.fr/img/bjs2/reindeer.png", "messages": 172, "description": "Je suis un peu timide.", "likes": 232 },
"p241": {"id": "p241", "nom": "Anonyme", "sexe": "femme", "portrait": "https://moodle.iutv.univ-paris13.fr/img/bjs2/et-masque.png", "messages": 2, "description": "J'ai beaucoup voyagé. Je cherche quelqu'un qui accepte les différences.", "likes": 5 },
"p112": {"id": "p112", "nom": "Père N.", "sexe": "homme", "portrait": "https://moodle.iutv.univ-paris13.fr/img/bjs2/santa.png", "messages": 1300482832, "description": "J'aime la nature et les paysages nordiques. Frileuses s'abstenir. Voyage d'affaires en période de fêtes.", "likes": 5103003013 }
}
public/index.html
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8"/>
<title>Profils</title>
<script src="https://moodle.iutv.univ-paris13.fr/img/bjs2/bjs2-js-lib.js"></script>
<link rel="stylesheet" href="profils.css"/>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>
<ul id="liste">
<li data-id="p125">L. Neige</li>
<li data-id="p112">Père N.</li>
<li data-id="p132">Minou</li>
<li data-id="p241">Anonyme</li>
</ul>
<div id="profil-principal" class="profil">
<img class="portrait" src="" alt=""/>
<p class="nom"></p>
<p class="stats"><span class="messages">? messages</span> <span class="likes">? likes</span></p>
<p class="description"></p>
<span id="editer">[Éditer]</span>
</div>
<div id="formulaire">
<div id="formulaire-boite">
<span id="fermer">x</span>
<div id="ligne-nom" ><label for="edit-nom" >Nom: </label><input id="edit-nom" type="text"/> </div>
<div id="ligne-sexe" ><label for="edit-sexe" >Sexe: </label><!--
--><select id="edit-sexe">
<option value="femme">Femme</option>
<option value="homme">Homme</option>
<option value="autre">Autre</option>
</select>
</div>
<div id="ligne-messages" ><label for="edit-portrait" >Portrait: </label><input id="edit-portrait" type="text"/></div>
<div id="ligne-messages" ><label for="edit-messages" >Messages: </label><input id="edit-messages" type="number"/></div>
<div id="ligne-likes" ><label for="edit-likes" >Likes: </label><input id="edit-likes" type="number"/></div>
<div id="ligne-description"><label for="edit-description">Description:</label><textarea id="edit-description"></textarea> </div>
<input id="enregistrer" type="button" value="Enregistrer"/>
</div>
</div>
<script type="module" src="profils.js"></script>
</body>
</html>
public/profils.css
/* Inspiré de https://freefrontend.com/css-profile-cards/ */public/profils.js
@import url("https://fonts.googleapis.com/css2?family=Baloo+Paaji+2:wght@400;500&display=swap");
body{
background: linear-gradient(45deg, rgba(255,255,255,1) 0%, rgba(240,180,149,1) 100%);
background-repeat: no-repeat;
font-family: sans;
color: #444;
background-attachment: fixed;
}
/***********************************/
/* liste **/
/***********************************/
#liste{
display: flex;
list-style-type: none ;
}
#liste li{
margin: 1em;
background-color: #222831;
color: white;
padding: .7em;
border-radius: .3em;
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.4);
cursor: pointer;
}
#liste li{
margin: 1em;
background-color: #222831;
color: white;
padding: .7em;
border-radius: .3em;
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.4);
cursor: pointer;
}
#liste li:hover{
background-color: #522831;
}
/***********************************/
/* Profil **/
/***********************************/
#profil-principal{
margin-left: 7em;
}
.profil {
position: relative;
background-color: #222831;
color: white;
width: 17em;
text-align: center;
border-radius: 5px;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
font-family: 'Baloo Paaji 2', cursive;
padding: .5em;
}
.profil .portrait {
border-radius: 50%;
width: 160px;
height: 160px;
border: 5px solid #272133;
margin-top: 20px;
background-color: white;
}
.profil .portrait.homme {
box-shadow: 0 10px 50px rgba(25, 110, 235, .8);
}
.profil .portrait.femme {
box-shadow: 0 10px 50px rgba(235, 25, 110, .6);
}
.profil .stats {
font-size: 1.2em;
margin-top: .5em;
}
.profil .stats span {
margin: 1em;
}
.profil .nom{
margin-top: 15px;
margin-bottom: .5em;
font-size: 1.5em;
}
#editer{
cursor: pointer;
cursor: pointer;
}
#editer:hover{
color: #aa0;
}
.profil[data-id="p132"]::before{
content: '🐁';
position: absolute;
top: 200px;
right: -100px;
animation: 1s 1 normal souris;
animation-delay: 4000ms;
opacity: 0;
}
@keyframes souris {
from {
right: -100px;
opacity: 1;
}
to {
right: 300px;
opacity: 1;
}
}
.profil[data-id="p132"]::after{
content: '';
border-radius: 50%;
background-color: #444;
width: 5px;
height: 5px;
position: absolute;
top: 106px;
left: 133px;
animation: 1s 1 normal oeilg;
animation-delay: 4000ms;
z-index: 2;
opacity: 0;
}
.profil[data-id="p132"] .nom::before{
content: '';
border-radius: 50%;
background-color: #444;
width: 5px;
height: 5px;
position: absolute;
top: 107px;
left: 154px;
animation: 1s 1 normal oeild;
animation-delay: 4000ms;
z-index: 2;
opacity: 0;
}
@keyframes oeilg {
from {
opacity: 1;
left: 133px;
}
to {
opacity: 1;
left: 126px;
}
}
@keyframes oeild {
from {
opacity: 1;
left: 154px;
}
to {
opacity: 1;
left: 147px;
}
}
.profil[data-id="p132"] .nom::after{
content: '';
width: 14px;
height: 8px;
border-right: 8px solid white;
border-left: 7px solid white;
position: absolute;
top: 104px;
left: 129px;
animation: 1s 1 normal blanc;
animation-delay: 4000ms;
opacity: 0;
}
@keyframes blanc {
from {opacity: 1;}
to {opacity: 1;}
}
/***********************************/
/* Formulaire **/
/***********************************/
#formulaire{
display: none;
position: fixed;
top: 0;
left: 0;
z-index: 1000;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,.5);
}
#formulaire-boite{
position: absolute;
top: 5em;
left: 5em;
width: 25em;
background: linear-gradient(45deg, rgba(255,255,255,1) 0%, rgba(240,180,149,1) 100%);
border-radius: 5px;
padding: 1em;
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
}
#fermer{
position: absolute;
right: 0;
top: 0;
display: inline-block;
padding: .3em;
cursor: pointer;
}
#fermer:hover{
color: #aa0;
}
#formulaire-boite>div{
margin: .5em 0;
}
#formulaire label{
display: inline-block;
min-width: 7em;
vertical-align: top;
}
#formulaire-boite>div input,#formulaire textarea{
width: 240px;
box-sizing: content-box;
}
#formulaire textarea{
height: 7em;
}
// vide pour l'instant
Au dernier sujet on avait écrit:
app.get('/profil/:id', async (requete, reponse) => {
console.log('Requete GET /profil/:id reçue');
let id=requete.params.id;
let profilsTexte=await fs.readFile('profils.json','utf8');
let profils=JSON.parse(profilsTexte);
reponse.send(profils[id]);
})
Ce code est une « route ». Il reçoit une requete du type http://localhost:3000/profil/p132 et renvoie une réponse JSON contenant le profil en question.
Pour ça, il lit le fichier profils.json qui contient tous les profils, et en choisit un.
Lancez
$ node app.js
Et affichez: http://localhost:3000/
Vous devriez voir une page avec du CSS correct, comme ceci:
Ouvrez un autre onglet et regardez: http://localhost:3000/profil/p132
Vous devriez voir des données JSON sur le profil p132 « Minou »
Notre page n'affiche personne, c'est triste !
On voudrait afficher un profil quand l'utilisateur clique sur un nom.
Les noms sont dans une liste:
<ul id="liste">
<li data-id="p125">L. Neige</li>
<li data-id="p112">Père N.</li>
<li data-id="p132">Minou</li>
<li data-id="p241">Anonyme</li>
</ul>
On a le choix entre deux solutions pour réagir quand l'utilisateur clique sur un nom, donc sur <li>:
On va utiliser la deuxième solution. Elle va être utile plus tard.
Comment s'appelle le comportement où un événement remonte progressivement dans l'arbre DOM ?
(souvenirs du S2!)
Indice: un mot en anglais commençant par un « b »
Presque! Ca commence bien par « bub »
Écrivez le code permettant d'afficher « click li » sur la console, uniquement quand l'utilisateur a cliqué sur un <li> (et pas sur le <ul>, par exemple entre deux noms).
Indice: utilisez event.target.nodeName pour savoir ce qui a été cliqué.
Ensuite on veut récupérer le « id » du nom cliqué:
Les <li> contiennent, dans le HTML, le id du profil:
<ul id="liste">
<li data-id="p125">L. Neige</li>
<li data-id="p112">Père N.</li>
<li data-id="p132">Minou</li>
<li data-id="p241">Anonyme</li>
</ul>
const liste =document.getElementById('liste');
...
liste.addEventListener('click',(event)=>{
if(event.target.nodeName!=='LI'){return;}
console.log('click li');
const id=event.target.getAttribute('data-id');
console.log(id);
});
On veut écrire le code pour chercher le profil sur le serveur et l'afficher.
Comme on aura besoin d'utiliser ce code par ailleurs, on va créer une fonction séparée.
Créez une fonction appelée afficher_profil(id) qui prend en paramètre l'id d'un profil.
Appelez-là depuis votre gestionnaire d'événement click.
Dans afficher_profil, utilisez simple_fetch pour chercher le profil sur le serveur.
Affichez le profil dans la console.
// Afficher le profil quand l'utilisateur clique sur un nom
liste.addEventListener('click',(event)=>{
if(event.target.nodeName!=='LI'){return;}
const id=event.target.getAttribute('data-id');
afficher_profil(id);
});
async function afficher_profil(id){
const profil=await simple_fetch('profil/'+id);
console.log(profil);
}
Créez en début de fichier le raccourci suivant:
const principal =document.getElementById('profil-principal');
Dans afficher_profil, affichez tous les champs.
En particulier, n'oubliez pas le portrait:
il faut à la fois indiquer src et le sexe, qui est utilisé comme une classe dans l'image
const principal =document.getElementById('profil-principal');
// Afficher le profil quand l'utilisateur clique sur un nom
liste.addEventListener('click',(event)=>{
if(event.target.nodeName!=='LI'){return;}
const id=event.target.getAttribute('data-id');
afficher_profil(id);
});
async function afficher_profil(id){
const profil=await simple_fetch('profil/'+id);
principal.querySelector('.nom' ).textContent=profil.nom;
principal.querySelector('.portrait' ).src=profil.portrait;
principal.querySelector('.portrait' ).className='portrait '+profil.sexe;
principal.querySelector('.messages' ).textContent=profil.messages+' messages';
principal.querySelector('.likes' ).textContent=profil.likes+' likes';
principal.querySelector('.description').textContent=profil.description;
}
On arrive enfin à voir les profils ! Ouf.
La liste de noms en haut de la page est fixée dans le HTML.
On ne peut pas ajouter de profils ! C'est dommage.
Dans les pages suivantes, on va essayer de construire une liste dynamique.
Effacez les <li> dans <ul id="liste">
Pour pouvoir afficher la liste des noms, il va falloir la chercher sur le serveur.
Créez dans app.js une nouvelle route, qui corresponde à http://localhost:3000/profil
Affichez « Requete GET /profil reçue » dans la console du serveur.
Vérifiez que ça marche.
app.get('/profil', async (requete, reponse) => {
console.log('Requete GET /profil reçue');
});
En JS, il y a de nombreuses (beaucoup trop) manières de parcourir un objet.
Prenons un exemple:
let exemple={On voudrait faire une boucle pour parcourir soit les clés ("Joe", "Leila", "Tom" ), soit les valeurs (123,543,91), soit les deux (clés et valeurs).
"Joe": 123,
"Leila": 543,
"Tom": 91,
};
Object.values(...) renvoie un tableau JS (et non pas un objet) avec toutes les valeurs.
On peut alors parcourir ce tableau avec une boucle for...of:
for(const valeur of Object.values(exemple)){
console.log(valeur);
}
Object.entries(...) renvoie un tableau JS (et non pas un objet) constitué de petits tableaux contenant 2 éléments :une clés et une valeurs:
Object.entries(exemple) renvoie:
[
["Joe", 123],
["Leila", 543],
["Tom", 91],
]
On peut alors parcourir ce tableau avec une boucle for...of:
for(const [cle,valeur] of Object.entries(exemple)){
console.log(cle,valeur);
}
let commande={
"Salade": 4.5,
"Désert": 3.2,
"plat": 6,
};
let total=0;
XYZ
Que faut-il écrire à la place de XYZ pour calculer le prix total de la commande ?
Utilisez une boucle for...of et Object.values
Vous n'avez pas utilisé Object.values, comme demandé dans l'énoncé
Vous êtes sur la bonne voie. Ca commence bien par:
for(const v of Object.values(commande)) ...
Le problème est après
On voudrait renvoyer au navigateur un objet qui ressemble à ceci:
{"p132":"Minou", "p125":"L. Neige","p241":"Anonyme", ... }
On va commencer par un objet vide:
let noms={};
Ensuite, on va lui ajouter tous les id:nom.
Il faut donc faire une boucle pour parcourir les profils.
Utilisez Object.values et for...of pour construire la réponse et la renvoyer au navigateur (rappel).
Vérifiez que ça marche avec : http://localhost:3000/profil
Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur,
puis copiez-le ici pour que votre enseignant puisse le relire plus tard:
app.get('/profil', async (requete, reponse) => {
console.log('Requete GET /profil reçue');
let profilsTexte=await fs.readFile('profils.json','utf8');
let profils=JSON.parse(profilsTexte);
let noms={};
for(let profil of Object.values(profils)){
noms[profil.id]=profil.nom;
}
reponse.send(noms);
})
Attention (2023): dans certaines salles de l'IUT la version de Firefox ne permet pas d'utiliser await directement. Vous devez créer une fonction async, mettre votre code dedans et appeler cette fonction.
Maintenant il nous reste à faire la partie client (navigateur).
Vers le début de votre fichier, utilisez simple_fetch pour chercher la liste des utilisateurs sur le serveur.
Ceci nous renvoie un objet.
Utilisez Object.entries et for...of pour parcourir cet objet (rappel).
Dans la boucle, créez les <li>.
N'oubliez pas l'attribut data-id (setAttribute)
Rappel création élément:
Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur, puis copiez-le ici pour que votre enseignant puisse le relire plus tard:
const principal =document.getElementById('profil-principal');
// Chercher les noms pour créer la liste en haut de la page
const noms=await simple_fetch('profil');
for(const [id,nom] of Object.entries(noms)){
const li=document.createElement('li');
li.setAttribute('data-id',id);
li.textContent=nom;
liste.append(li);
}
// Afficher le profil quand l'utilisateur clique sur un nom
liste.addEventListener('click',(event)=>{
if(event.target.nodeName!=='LI'){return;}
const id=event.target.getAttribute('data-id');
afficher_profil(id);
});
async function afficher_profil(id){
const profil=await simple_fetch('profil/'+id);
principal.querySelector('.nom' ).textContent=profil.nom;
principal.querySelector('.portrait' ).src=profil.portrait;
principal.querySelector('.portrait' ).className='portrait '+profil.sexe;
principal.querySelector('.messages' ).textContent=profil.messages+' messages';
principal.querySelector('.likes' ).textContent=profil.likes+' likes';
principal.querySelector('.description').textContent=profil.description;
}
On veut maintenant pouvoir éditer les profils.
Un petit formulaire est prévu dans le HTML, mais il est caché par défaut dans le CSS (display: none).
Quand l'utilisateur clique sur éditer, affichez le formulaire (display: block).
const principal =document.getElementById('profil-principal');
const formulaire =document.getElementById('formulaire');
// Chercher les noms pour créer la liste en haut de la page
const noms=await simple_fetch('profil');
for(const [id,nom] of Object.entries(noms)){
const li=document.createElement('li');
li.setAttribute('data-id',id);
li.textContent=nom;
liste.append(li);
}
// Afficher le profil quand l'utilisateur clique sur un nom
liste.addEventListener('click',(event)=>{
if(event.target.nodeName!=='LI'){return;}
const id=event.target.getAttribute('data-id');
afficher_profil(id);
});
async function afficher_profil(id){
const profil=await simple_fetch('profil/'+id);
principal.querySelector('.nom' ).textContent=profil.nom;
principal.querySelector('.portrait' ).src=profil.portrait;
principal.querySelector('.portrait' ).className='portrait '+profil.sexe;
principal.querySelector('.messages' ).textContent=profil.messages+' messages';
principal.querySelector('.likes' ).textContent=profil.likes+' likes';
principal.querySelector('.description').textContent=profil.description;
}
// Afficher et remplir le formulaire quand l'utilisateur clique sur editer
document.getElementById('editer').addEventListener('click',async ()=>{
formulaire.style.display='block';
});
On voudrait remplir le formulaire avec les données du profil.
On a donc besoin d'aller chercher le profil sur le serveur.
Mais pour ça, il nous faut le id du profil.
Dans afficher_profil, créez un attribut data-id (avec setAttribute) sur #profil-principal contenant le id.
De cette manière, dans éditer, on peut récupérer le id avec getAttribute.
Faites-le et affichez le id dans la console quand l'utilisateur clique sur « Editer ».
Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur,
puis copiez-le ici pour que votre enseignant puisse le relire plus tard:
const principal =document.getElementById('profil-principal');
const formulaire =document.getElementById('formulaire');
// Chercher les noms pour créer la liste en haut de la page
const noms=await simple_fetch('profil');
for(const [id,nom] of Object.entries(noms)){
const li=document.createElement('li');
li.setAttribute('data-id',id);
li.textContent=nom;
liste.append(li);
}
// Afficher le profil quand l'utilisateur clique sur un nom
liste.addEventListener('click',(event)=>{
if(event.target.nodeName!=='LI'){return;}
const id=event.target.getAttribute('data-id');
afficher_profil(id);
});
async function afficher_profil(id){
const profil=await simple_fetch('profil/'+id);
principal.setAttribute('data-id',id);
principal.querySelector('.nom' ).textContent=profil.nom;
principal.querySelector('.portrait' ).src=profil.portrait;
principal.querySelector('.portrait' ).className='portrait '+profil.sexe;
principal.querySelector('.messages' ).textContent=profil.messages+' messages';
principal.querySelector('.likes' ).textContent=profil.likes+' likes';
principal.querySelector('.description').textContent=profil.description;
}
// Afficher et remplir le formulaire quand l'utilisateur clique sur editer
document.getElementById('editer').addEventListener('click',async ()=>{
formulaire.style.display='block';
const id=principal.getAttribute('data-id');
console.log(id);
});
Utilisez simple_fetch pour chercher le profil sur le serveur.
Remplissez les champs.
Ajoutez aussi un attribut data-id (avec setAttribute) sur le formulaire (on en aura besoin plus tard).
Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur,
puis copiez-le ici pour que votre enseignant puisse le relire plus tard:
const principal =document.getElementById('profil-principal');
const formulaire =document.getElementById('formulaire');
const liste =document.getElementById('liste');
// Chercher les noms pour créer la liste en haut de la page
const noms=await simple_fetch('profil');
for(const [id,nom] of Object.entries(noms)){
const li=document.createElement('li');
li.setAttribute('data-id',id);
li.textContent=nom;
liste.append(li);
}
// Afficher le profil quand l'utilisateur clique sur un nom
liste.addEventListener('click',(event)=>{
if(event.target.nodeName!=='LI'){return;}
const id=event.target.getAttribute('data-id');
afficher_profil(id);
});
async function afficher_profil(id){
const profil=await simple_fetch('profil/'+id);
principal.setAttribute('data-id',id);
principal.querySelector('.nom' ).textContent=profil.nom;
principal.querySelector('.portrait' ).src=profil.portrait;
principal.querySelector('.portrait' ).className='portrait '+profil.sexe;
principal.querySelector('.messages' ).textContent=profil.messages+' messages';
principal.querySelector('.likes' ).textContent=profil.likes+' likes';
principal.querySelector('.description').textContent=profil.description;
}
// Afficher et remplir le formulaire quand l'utilisateur clique sur editer
document.getElementById('editer').addEventListener('click',async ()=>{
const id=principal.getAttribute('data-id');
const profil=await simple_fetch('profil/'+id);
formulaire.style.display='block';
formulaire.setAttribute('data-id',id);
document.getElementById('edit-nom' ).value=profil.nom;
document.getElementById('edit-sexe' ).value=profil.sexe;
document.getElementById('edit-portrait' ).value=profil.portrait;
document.getElementById('edit-messages' ).value=profil.messages;
document.getElementById('edit-likes' ).value=profil.likes;
document.getElementById('edit-description').value=profil.description;
});
Quand l'utilisateur clique sur enregistrer, créez un objet « profil » avec tous les champs, y compris id.
Vous pouvez récupérer le id avec l'attribut data-id sur #formulaire
Affichez l'objet « profil » dans la console.
Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur,
puis copiez-le ici pour que votre enseignant puisse le relire plus tard:
document.getElementById('enregistrer').addEventListener('click',async ()=>{
const id=formulaire.getAttribute('data-id');
const profil={id};
profil.nom =document.getElementById('edit-nom' ).value;
profil.sexe =document.getElementById('edit-sexe' ).value;
profil.portrait =document.getElementById('edit-portrait' ).value;
profil.messages =document.getElementById('edit-messages' ).value;
profil.likes =document.getElementById('edit-likes' ).value;
profil.description=document.getElementById('edit-description').value;
console.log(profil);
});
On veut maintenant envoyer ce profil sur le serveur, pour qu'il l'enregistre dans profils.json
Quelle méthode faut-il utiliser pour la requête AJAX ?
POST
GET
En vous inspirant de la route GET /profil/:id
Créez une route POST /profil/:id
Pour pouvoir lire les données envoyées par POST, ajoutez cette ligne dans app.js:
const app = express();
app.use(express.static('public'));
app.use(express.urlencoded({extended:false}));
Dans votre route POST, vous pouvez lire les données envoyées avec requete.body
Affichez les dans la console du serveur.
Ensuite, lisez profils.json, convertissez le en objet et modifiez le profil posté.
Retransformez-le en JSON et enregistrez le fichier avec
await fs.writeFile('profils.json',JSON.stringify(profils));
Utilisez simple_fetch(..., {post:profil}) pour envoyer la requête POST.
Une fois la requête finie, cachez le formulaire et affichez le profil avec la fonction afficher_profil()
Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur,
puis copiez-le ici pour que votre enseignant puisse le relire plus tard:
import express from 'express';
import * as fs from "node:fs/promises";
const app = express();
app.use(express.static('public'));
app.use(express.urlencoded({extended:false}));
app.listen(3000, () => {
console.log('Notre app « profils » écoute sur le port 3000')
});
app.get('/profil', async (requete, reponse) => {
console.log('Requete GET /profil reçue');
let profilsTexte=await fs.readFile('profils.json','utf8');
let profils=JSON.parse(profilsTexte);
let noms={};
for(let profil of Object.values(profils)){
noms[profil.id]=profil.nom;
}
reponse.send(noms);
})
app.get('/profil/:id', async (requete, reponse) => {
console.log('Requete GET /profil/:id reçue');
let id=requete.params.id;
let profilsTexte=await fs.readFile('profils.json','utf8');
let profils=JSON.parse(profilsTexte);
reponse.send(profils[id]);
})
app.post('/profil/:id', async (requete, reponse) => {
console.log('Requete POST /profil/:id reçue');
let id=requete.params.id;
let profilsTexte=await fs.readFile('profils.json','utf8');
let profils=JSON.parse(profilsTexte);
// Attention: ici on ne fait aucune validation.
// Il faudrait valider les données (nottament "portrait" et les nombres) et vérifier les noms des champs.
profils[id]=requete.body;
await fs.writeFile('profils.json',JSON.stringify(profils));
reponse.send({message:'ok'});
})
const principal =document.getElementById('profil-principal');
const formulaire =document.getElementById('formulaire');
const liste =document.getElementById('liste');
// Chercher les noms pour créer la liste en haut de la page
const noms=await simple_fetch('profil');
for(const [id,nom] of Object.entries(noms)){
const li=document.createElement('li');
li.setAttribute('data-id',id);
li.textContent=nom;
liste.append(li);
}
// Afficher le profil quand l'utilisateur clique sur un nom
liste.addEventListener('click',(event)=>{
if(event.target.nodeName!=='LI'){return;}
const id=event.target.getAttribute('data-id');
afficher_profil(id);
});
async function afficher_profil(id){
const profil=await simple_fetch('profil/'+id);
principal.setAttribute('data-id',id);
principal.querySelector('.nom' ).textContent=profil.nom;
principal.querySelector('.portrait' ).src=profil.portrait;
principal.querySelector('.portrait' ).className='portrait '+profil.sexe;
principal.querySelector('.messages' ).textContent=profil.messages+' messages';
principal.querySelector('.likes' ).textContent=profil.likes+' likes';
principal.querySelector('.description').textContent=profil.description;
}
// Afficher et remplir le formulaire quand l'utilisateur clique sur editer
document.getElementById('editer').addEventListener('click',async ()=>{
const id=principal.getAttribute('data-id');
const profil=await simple_fetch('profil/'+id);
formulaire.style.display='block';
formulaire.setAttribute('data-id',id);
document.getElementById('edit-nom' ).value=profil.nom;
document.getElementById('edit-sexe' ).value=profil.sexe;
document.getElementById('edit-portrait' ).value=profil.portrait;
document.getElementById('edit-messages' ).value=profil.messages;
document.getElementById('edit-likes' ).value=profil.likes;
document.getElementById('edit-description').value=profil.description;
});
// Envoyer le profil edit par POST au serveur quand l'utilisateur clique sur enregistrer
document.getElementById('enregistrer').addEventListener('click',async ()=>{
const id=formulaire.getAttribute('data-id');
const profil={id};
profil.nom =document.getElementById('edit-nom' ).value;
profil.sexe =document.getElementById('edit-sexe' ).value;
profil.portrait =document.getElementById('edit-portrait' ).value;
profil.messages =document.getElementById('edit-messages' ).value;
profil.likes =document.getElementById('edit-likes' ).value;
profil.description=document.getElementById('edit-description').value;
await simple_fetch('profil/'+id,{post:profil});
formulaire.style.display='none';
afficher_profil(id);
});
On y est presque!
Quelques petites touches:
const principal =document.getElementById('profil-principal');
const liste =document.getElementById('liste');
const formulaire =document.getElementById('formulaire');
// Chercher les noms pour créer la liste en haut de la page
const noms=await simple_fetch('profil');
for(const [id,nom] of Object.entries(noms)){
const li=document.createElement('li');
li.setAttribute('data-id',id);
li.textContent=nom;
liste.append(li);
}
// Afficher le profil quand l'utilisateur clique sur un nom
liste.addEventListener('click',(event)=>{
if(event.target.nodeName!=='LI'){return;}
const id=event.target.getAttribute('data-id');
afficher_profil(id);
});
async function afficher_profil(id){
principal.style.display='block';
const profil=await simple_fetch('profil/'+id);
principal.setAttribute('data-id',id);
principal.querySelector('.nom' ).textContent=profil.nom;
principal.querySelector('.portrait' ).src=profil.portrait;
principal.querySelector('.portrait' ).className='portrait '+profil.sexe;
principal.querySelector('.messages' ).textContent=profil.messages+' messages';
principal.querySelector('.likes' ).textContent=profil.likes+' likes';
principal.querySelector('.description').textContent=profil.description;
}
// Afficher et remplir le formulaire quand l'utilisateur clique sur editer
document.getElementById('editer').addEventListener('click',async ()=>{
const id=principal.getAttribute('data-id');
const profil=await simple_fetch('profil/'+id);
formulaire.style.display='block';
formulaire.setAttribute('data-id',id);
document.getElementById('edit-nom' ).value=profil.nom;
document.getElementById('edit-sexe' ).value=profil.sexe;
document.getElementById('edit-portrait' ).value=profil.portrait;
document.getElementById('edit-messages' ).value=profil.messages;
document.getElementById('edit-likes' ).value=profil.likes;
document.getElementById('edit-description').value=profil.description;
});
// Envoyer le profil edit par POST au serveur quand l'utilisateur clique sur enregistrer
document.getElementById('enregistrer').addEventListener('click',async ()=>{
const id=formulaire.getAttribute('data-id');
const profil={id};
profil.nom =document.getElementById('edit-nom' ).value;
profil.sexe =document.getElementById('edit-sexe' ).value;
profil.portrait =document.getElementById('edit-portrait' ).value;
profil.messages =document.getElementById('edit-messages' ).value;
profil.likes =document.getElementById('edit-likes' ).value;
profil.description=document.getElementById('edit-description').value;
await simple_fetch('profil/'+id,{post:profil});
formulaire.style.display='none';
afficher_profil(id);
});
document.getElementById('fermer').addEventListener('click',()=>{
formulaire.style.display='none';
});
Dans profils.css:
#profil-principal{
margin-left: 7em;
display: none;
}