AJAX
AJAX (Asynchronous JavaScript And XML) désigne un ensemble de technologies (HTML, CSS, Javascript, DOM, ... l'objet
XMLHttpRequest)
qui peuvent être utilisées conjointement pour créer des applications web qui présentent des interfaces
utilisateurs mises à jour de manière incrémentales, c.a.d. ne nécessitant pas le rechargement global
des pages.
Dans les faits, une page web utilisant AJAX va exécuter des scripts qui vont lancer des requêtes
sur des serveurs web, attendre les réponses, et utiliser les résultats pour transformer
dynamiquement le DOM de la page en cours (sans rechargement).
La classe XMLHttpRequest
Au coeur d'AJAX, il y a la classe XMLHttpRequest
dont les instances permettent de lancer
des requêtes en Javascript sur des serveurs distants.
Pour créer une instance, on écrira :
<script>
...
let httpRequest = new XMLHttpRequest()
// RQ : le nom de variable 'httpRequest' est souvent utilisé
// mais vous pouvez tout à fait en choisir un autre...
...
</script>
Préparation : gestionnaire d'état onreadystatechange
Avant de lancer une requête, il est nécessaire d'indiquer à l'instance d'XMLHttpRequest ce qu'on voudra
faire de la réponse.
Pour ce faire, il faut bien comprendre que lancer des requêtes sur un serveur au travers du réseau
est plus "aléatoire" (prend plus de temps, peut échouer, ...) qu'une exécution locale :
-
Après avoir lancé une requête, il faudra attendre la réponse.
-
Le traitement de la requête se fait en plusieurs étapes.
-
Il se peut qu'il y ait des problèmes d'exécution (ex. demande de fichier qui n'existe pas, panne de réseau,
etc.).
Pour toutes ces raisons, XMLHttpRequest
fournit un gestionnaire d'évènements nommé
onreadystatechange
dans lequel on peut
enregistrer un handler (comme pour un click sur un bouton) qui va décrire comment doit réagir
votre script pendant qu'il attend la réponse du serveur, lorsque la requête est en cours de traitement,
lorsqu'elle est terminée, ou encore lorsqu'il se produit une erreur...
Syntaxe
La syntaxe pour écrire le handler est la suivante :
<script>
...
let handler = function() {
// détails des traitements en fonction de l'état de la réponse
}
httpRequest.onreadystatechange = handler
...
</script>
Ou en plus synthétique :
<script>
...
httpRequest.onreadystatechange = function() {
// détails des traitements en fonction de l'état de la réponse
}
...
</script>
Contenu du handler
Comme son nom l'indique, le handler sera déclenché à chaque changement d'état de la requête. Il faut donc
(à chaque appel) tester l'état pour savoir comment réagir, ce qui résulte généralement en une suite de
if
indiquant l'action à exécuter en fonction de chaque état considéré.
L'état de la requête est stocké dans l'attribut readyState
de l'instance de
XMLHttpRequest
.
L'état qui nous intéressera en général est le numéro correspondant à la constante
XMLHttpRequest.DONE
qui indique que la requête est terminée.
La liste des états possibles est décrite sur :
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState.
On pourra ensuite vérifier le code de réponse de la requête HTPP (pour savoir si tout s'est bien passé,
ou s'il y a eu des problèmes).
Le code de réponse de la requête HTPP est stocké dans l'attribut status
de l'instance de
XMLHttpRequest
.
Le code 200
indique que tout est OK.
De plus amples informations sur les codes de réponse HTTP sont disponibles sur :
https://developer.mozilla.org/fr/docs/Web/HTTP/Status
Enfin, il faut indiquer ce qui va être fait de la réponse reçue.
Le résultat de la requête est stocké dans l'attribut response
de l'instance de
XMLHttpRequest
, cet attribut pouvant être décliné en responseText
ou encore responseXML
selon le type de réponse attendu.
Exemple de structure de handler "classique" :
<script>
...
let handler = function (){
if (httpRequest.readyState === XMLHttpRequest.DONE) { // REQUETE TERMINÉE
if (httpRequest.status === 200) { // LA RÉPONSE EST OK :
// traitement de la réponse
let resultat = httpRequest.response
...
} else { // IL Y A EU UN PROBLÈME
// traitement de l'erreur
}
}
}
httpRequest.onreadystatechange = handler
...
</script>
Exécution de la requête
L'instance de XMLHttpRequest
étant préparée à traiter les réponses du serveur, il ne reste
plus qu'à effectivement lancer la requête avec open
suivi de send
.
La méthode open(method, url)
de XMLHttpRequest
permet d'instancier une requête HTTP.
Le paramètre method indique la méthode HTTP à utiliser (GET, POST, PUT, DELETE, ...).
Le paramètre url indique l'URL à laquelle la requête va être envoyée.
NB: il est possible d'ajouter d'autres paramètres, mais nous ne les utiliserons pas ici.
Pour plus de détails :
https://developer.mozilla.org/fr/docs/Web/API/XMLHttpRequest/open
La méthode send()
de XMLHttpRequest
permet d'envoyer une requête vers le serveur
avec la méthode et l'URL qui on été spécifiées grâce à open
.
Pour plus de détails :
https://developer.mozilla.org/fr/docs/Web/API/XMLHttpRequest/send
Le shéma global d'utilisation d'AJAX est donc le suivant :
<script>
...
let url = "..." // une URL valide
let method = "..." // un choix parmi "GET", "POST", "PUT", ...
// préparation
let httpRequest = new XMLHttpRequest()
httpRequest.onreadystatechange = function (){
// code du handler pour la gestion des états
...
}
// lancement de la requête qui a été préparée
httpRequest.open(method, url)
httpRequest.send()
...
</script>
Un exemple avec la méthode 'GET'
Le code ci-dessous donne un exemple dans lequel un div
nommé #ajax-01-display
est rempli dynamiquement avec du contenu HTML récupéré par une instance de XMLHttpRequest
qui lance une requête HTTP 'GET'.
ATTENTION:
Si vous voulez tester ce code, vous ne pouvez pas utiliser une URL relative comme je l'ai fait (car votre page n'est pas sur
mon serveur..).
Remplacez donc la valeur de url
par :
http://web.gregory-bourguin.fr/teaching/php/requests/ajax/01_test.php
<p>Voici du HTML récupéré avec AJAX :</p>
<div id="ajax-01-display" style="width: 50%"></div>
<p>Cool !</p>
<script>
document.addEventListener('DOMContentLoaded', function (){
let display = document.querySelector("#ajax-01-display")
let method = "GET"
let url = "/teaching/php/requests/ajax/01_test.php"
// NB : j'ai utilisé ici une URL relative (le script php visé est sur le même serveur).
// Si on voulait accéder à un site tiers, il faudrait mettre une URL complète.
// (... et dans ce cas il faudrait en plus que le serveur tiers visé autorise les CORS...)
let httpRequest = new XMLHttpRequest()
httpRequest.onreadystatechange = function (){
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
display.innerHTML = httpRequest.response
} else {
alert('ERREUR avec la requête.');
}
}
}
httpRequest.open(method, url)
httpRequest.send()
})
</script>
Resultat
Voici du HTML récupéré avec AJAX :
Cool !
Remarque importante à propos des C.O.R.S.
Il faut savoir que pour des raisons de sécurité, un script utilisant
AJAX ne peut par défaut recevoir des données que du serveur dont il est issu.
Ainsi, il n'y a pas de problème pour qu'une page de votre site web utilise AJAX pour récupérer des
informations provenant de ce même site (votre site web donc...).
Par contre, pour qu'AJAX puisse consommer des données provenant d'un site tiers, il faut
que ce site tiers ait autorisé les
CORS (Cross-Origin Resource Sharing)
.
Si vous tentez de consommer des informations d'un site tiers n'autorisant pas les CORS,
votre script déclenchera une erreur du style :
XMLHttpRequest ... blocked by CORS policy: No 'Access-Control-Allow-Origin'
header is present on the requested resource.
JSON
Les serveurs web sont aujourd'hui de plus en plus utilisés non plus "seulement" pour fournir des pages web,
mais aussi pour fournir des données. En d'autres termes, dans cette configuration, le serveur ne fournit
plus directement la page à afficher, mais "juste" des données/informations qui peuvent être récupérées
par un client qui pourra lui-même les mettre en forme et les intégrer dans son interface.
JSON (JavaScript Object Notation)
est un format d'échange de données qui par essence peut être facilement consommé par Javascript pour
reconstruire des objets.
De fait, JSON est particulièrement adapté à la réception de données par AJAX (Javascript), une réponse
de requête en JSON pouvant être transformée en objets Javascript qui seront utilisés pour manipuler
le DOM et mettre à jour/compléter la page web en cours d'affichage.
L'objet JSON
JSON est un format d'échange de données, mais en Javascript, c'est aussi un objet.
L'objet JSON
permet de :
-
JSON.stringify(object)
: sérialiser des objets Javascript
(obtenir la représentation d'un objet sous forme de chaine au format JSON).
-
JSON.parse(string)
parser des String au format JSON
(reconstruire un objet à partir d'une chaine au format JSON).
stringify
: objet -> string
Dans l'exemple suivant, un objet user
est créé en Javascript, puis l'objet est sérialisé
dans une chaine qui est alors affichée dans l'élément HTML #json-display
.
Dans une application plus conséquente, cette chaine pourrait être envoyée sur le réseau,
mise dans un fichier, etc.
<style>
#json-display{
color: darkgray;
}
</style>
Voici un objet sérialisé en JSON : <span id="json-display"></span>
<script>
document.addEventListener('DOMContentLoaded', function (){
// création d'un objet Javascript
let user = new Object()
user.firstName = "Grégory"
user.lastName = "Bourguin"
// sérialisation en JSON
let result = JSON.stringify(user)
// affichage
let display = document.getElementById("json-display")
display.innerText = result
})
</script>
Resultat
Voici un objet sérialisé en JSON :
parse
: string -> objet
Dans l'exemple suivant, une chaine JSON est utilisée pour reconstruire un objet Javascript user
.
Les attribut de l'objet sont alors affichés dans des éléments HTML nommés #user-firstname
et #user-lastname
.
Dans une application plus conséquente, cette chaine pourrait provenir du réseau, etc.
<style>
#user-firstname, #user-lastname{
color: blue;
font-weight: bold;
}
</style>
Voici les attributs d'un objet construit à partir d'une chaine JSON :<br>
Prénom > <span id="user-firstname"></span> <br>
Nom > <span id="user-lastname"></span>
<script>
document.addEventListener('DOMContentLoaded', function (){
// chaine en JSON
let jsonString = '{"firstName":"Grégory","lastName":"Bourguin"}'
// récupération d'un objet Javascript à partie de JSON
let user = JSON.parse(jsonString)
// affichage
let fname = document.getElementById("user-firstname")
fname.innerText = user.firstName
let lname = document.getElementById("user-lastname")
lname.innerText = user.lastName
})
</script>
Resultat
Voici les attributs d'un objet construit à partir d'une chaine JSON :
Prénom >
Nom >
AJAX & JSON
Il existe de nombreux serveurs web qui ont pour but de fournir des données JSON, et puisque ce sont
des serveurs web, il est possible de les interroger grâce à AJAX.
Nous allons donc pouvoir écrire des applications web dont la partie cliente en Javascript utilise des
données reçue en JSON en réponse à des requêtes AJAX lancées sur des serveurs tiers
(ou votre propre serveur si vous le configurez dans ce but...).
API REST
Ces serveurs (appelés REST) sont généralement capables de répondre à diverses questions.
Les différentes questions, la manière de les poser, et le "type" des résultats sont décrits
dans l'API REST du serveur. Pour simplifier, on peut imaginer que l'API REST décrit des
fonctions (noms, paramètres, types de retour) qui peuvent être exécutées à distance.
Pour poser une question précise, il "suffit" de configurer spécifiquement la requête HTTP qu'on
va envoyer en fonction de la question qu'on veut leur poser (et éventuellement du type de réponse
qu'on veut recevoir).
Il faut aussi savoir que certains serveurs limitent les accès (par exemple en demandant de configurer
les requêtes avec une clé d'identification).
Du point de vue d'AJAX, interroger un serveur REST revient à lancer une requête HTTP
avec une instance XMLHttpRequest
configurée conformément à l'API du serveur ciblé.
Un exemple : "Les blagues à papa"
Dans l'exemple ci-dessous, nous allons utiliser le serveur
https://icanhazdadjoke.com dont
le but est de fournir des "blagues à papa"...
Chaque serveur REST étant différent, la première étape consiste à aller consulter la documentation de
son API pour savoir :
-
quelles questions peuvent être posées
-
... et comment s'y prendre !
En consultant la documentation sur
https://icanhazdadjoke.com/api,
vous constaterez que ce serveur ne pose pas de restrictions
(
#authentication
),
qu'il propose des blagues en aléatoire
(
#fetch-a-random-dad-joke
),
des blagues contenant certains mots
(
#search-for-dad-jokes
),
etc.
Il est aussi possible d'indiquer sous quelle forme on veut recevoir la/les blague(s) :
message texte, slack, JSON, image.
Pour notre exemple, nous allons
demander une blague aléatoire en JSON.
En AJAX, il faut donc :
-
créer une instance de
XMLHttpRequest
-
configurer un
header
(cf. ci-après) avec "Accept: application/json"
NB: je ne l'invente pas -> c'est la
documentation
qui le dit...
-
lancer la requête en HTTP GET sur l'URL : https://icanhazdadjoke.com/
NB: je ne l'invente pas non plus -> c'est aussi la
documentation qui le dit...
-
parser la réponse (faite en JSON) pour obtenir un objet Javascript
La blague se trouvera dans l'attribut joke
.
NB: je ne l'invente toujours pas non plus ->
documentation...
-
afficher la blague reçue dans un des éléments HTML de la page
(pour que l'utilisateur puisse en profiter :) )
Voici le code correspondant :
<style>
@import url('https://fonts.googleapis.com/css2?family=Architects+Daughter&display=swap');
.joke{
margin: 10px 0;
background: antiquewhite;
padding: 15px;
font-family: 'Architects Daughter', cursive;
font-size: 1.3em;
}
</style>
<div id="joke-01" class="joke"></div>
<script>
(function() {
let display = undefined
let method = "GET"
let url = "https://icanhazdadjoke.com/"
let httpRequest = new XMLHttpRequest()
httpRequest.onreadystatechange = function () {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
let result = JSON.parse(httpRequest.response)
display.innerHTML = result.joke
} else {
alert('ERREUR avec la requête.');
}
}
}
// l'envoi est ici pour attendre que le DOM soit chargé
document.addEventListener('DOMContentLoaded', function () {
display = document.querySelector("#joke-01")
httpRequest.open(method, url)
// configuration de l'entête pour indiquer qu'on veut du JSON (cf. doc de l'API du serveur)
httpRequest.setRequestHeader('Accept', 'application/json');
httpRequest.send()
})
})()
</script>
On peut modifier un peu le code :
-
Pour éviter de faire un
JSON.parse(...)
sur la réponse, il est possible d'indiquer
à l'instance XMLHttpRequest
que ce qui est attendu est du JSON. Il suffit dans ce cas
de fixer l'attribut responseType
avec la valeur "json"
.
-
On peut mettre le chargement de blague dans une fonction
getNewJoke()
qu'on lie
au click d'un bouton (pour charger dynamiquement de nouvelles blagues).
<div id="joke-02" class="joke"></div>
<button id="get-new-joke" class="btn">Nouvelle blague..</button>
<script>
(function() {
let display = undefined
let method = "GET"
let url = "https://icanhazdadjoke.com/"
let httpRequest = new XMLHttpRequest()
// on indique que la réponse sera en JSON : "response" sera alors AUTOMATIQUEMENT PARSÉ
httpRequest.responseType = "json"
httpRequest.onreadystatechange = function () {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
let result = httpRequest.response
display.innerHTML = result.joke // PLUS BESOIN DE PARSER
} else {
alert('ERREUR avec la requête.');
}
}
}
document.addEventListener('DOMContentLoaded', function () {
display = document.querySelector("#joke-02")
// handler sur le click du bouton
let button = document.getElementById("get-new-joke")
button.addEventListener('click', function (){
getNewJoke()
})
getNewJoke() // pour la 1ère blague lors du chargement
})
function getNewJoke() {
httpRequest.open(method, url)
httpRequest.setRequestHeader('Accept', 'application/json');
httpRequest.send()
}
})()
</script>
Paramètres
Comme évoqué dans la partie précédente, un serveur est souvent capable de fournir plusieurs services qui
sont différenciés par l'extension du chemin indiqué dans l'URL utilisée pour lancer la requête.
Par exemple, la documentation de
https://icanhazdadjoke.com/api
fournit les URLs :
-
GET https://icanhazdadjoke.com/
: charger une blague aléatoire
-
GET https://icanhazdadjoke.com/search
: rechercher une blague
Cependant, pour des requêtes complexes, le serveur peut avoir besoin d'informations
complémentaires, comme par exemple la (ou les) chaine(s) à utiliser pour effectuer une recherche.
Ces informations devront alors être envoyées au serveur sous forme de paramètres de la requête.
Paramètres GET
Les paramètres d'une requête GET correspondent à des paires nom=valeur
que l'on va
ajouter à l'URL visée en utilisant le signe ?
Lorsqu'il y a plusieurs paramètres à envoyer, ceux-ci doivent être séparés par des &
.
La syntaxe des paramètres pour une requête HTTP GET est :
?param=valeur[¶m=valeur]
Chaque serveur (et chaque URL) utilise ses propres noms de paramètres. De fait, pour savoir comment créer
une requête paramétrée pour une URL particulière, il est nécessaire de se référer à la
documentation de l'API visée.
Par exemple, pour la recherche de blagues sur
https://icanhazdadjoke.com
il faut :
-
Utiliser l'URL
https://icanhazdadjoke.com/search
.
-
Renseigner un paramètre "term" contenant le(s) terme(s) à rechercher.
-
renseigner un paramètre "limit" contenant le nombre maximal de blagues renvoyées lors
d'une requête (ce qui constitue une "page" de réponses).
-
Renseigner un paramètre "page" contenant le numéro de la page de blagues.
(Lorsque le nombre de blagues sur le serveur correspondant à la requête dépasse la limit,
le serveur découpe sa réponses en pages, et on peut alors indiquer celle qu'on veut récupérer).
On peut imaginer une recherche particulière contenant par exemple le terme "dog" :
https://icanhazdadjoke.com/search?term=dog
La suite d'instructions pour lancer cette requête est alors :
// url pour la recherche
let url = "https://icanhazdadjoke.com/search"
// construction des paramètres
let params = "?term=dog&limit=5&page=1"
let request = url + params
// exécution de la requête
httpRequest.open(method, request) // ATTENTION : on a remplacé 'url' par 'request'
httpRequest.setRequestHeader('Accept', 'application/json');
httpRequest.send()
encodeURIComponent(...)
: gérer les caractères interdits
Les paramètres font partie de l'URL : il y a donc certains caractères à éviter.
Par exemple, si l'on veut faire une recherche sur les blagues contenant plusieurs termes comme
"dog" ET "bike, il faut lister ces termes dans le paramètre term
en les
séparant par des espaces... or les espaces sont interdits dans une URL !
La méthode encodeURIComponent(...)
permet de résoudre ce problème en encodant les
caractères interdits.
(Par exemple les espaces seront transformés en : "%20").
Ainsi le code de construction de la chaine de paramètres peut être complété comme suit :
// construction des paramètres
let search = "dog bike"
search = encodeURIComponent(search)
let params = "?term=" + search + "&limit=5&page=1"
let request = url + params
Exemple d'une requête GET paramétrée
Il nous est maintenant possible de lancer une requête complexe avec des paramètres.
(On recherchera ici les blagues contenant les termes "dog" et "bike").
La
documentation
nous indique que la réponse du serveur à une telle requête est
quelque peu différente de celle que nous obtenions précédemment : le serveur ne renvoie plus simplement
une "joke", mais une "page" qui en contient plusieurs (ce nombre étant fixé par le paramètre
limit
).
Il nous faut donc adapter le code de traitement du résultat : dans l'exemple ci-dessous,
nous n'affichons que la 1ère blague de la 1ère page de réponse.
<div id="joke-05" class="joke"></div>
<script>
(function() {
let display = undefined
/**
* Recherche de blagues.
*/
function searchJoke() {
let method = "GET"
let url = "https://icanhazdadjoke.com/search" // url pour la recherche
// construction des paramètres
let search = "dog bike"
search = encodeURIComponent(search)
let params = "?term=" + search + "&limit=5&page=1"
let request = url + params
// exécution de la requête
httpRequest.open(method, request) // ATTENTION : on a remplacé 'url' par 'request'
httpRequest.setRequestHeader('Accept', 'application/json');
httpRequest.send()
}
let httpRequest = new XMLHttpRequest()
httpRequest.responseType = "json"
httpRequest.onreadystatechange = function () {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
let results = httpRequest.response.results // la listes des blagues reçues
if(results.length > 0){
display.innerHTML = results[0].joke // affichage de la 1ère blague
}else{
display.innerHTML = ""
}
} else {
alert('ERREUR avec la requête.');
}
}
}
document.addEventListener('DOMContentLoaded', function () {
display = document.querySelector("#joke-05")
searchJoke()
})
})()
</script>
Exercice 01
Modifiez le code de l'exemple ci-avant pour ajouter un formulaire qui permet de lancer une recherche
de blague avec les mots clés indiqués dans un champ de recherche.
NB: la documentation indique que la recherche sur une chaine vide renvoie toutes les
blagues du serveur, ce qui explique que la recherche sans mot clé donne une blague
quand même.
TP 03
A partir des exemples précédents et de la documentation de l'API
icanhazdadjoke,
réalisez la page ci-dessous en prêtant bien attention aux détails aussi bien visuels que fonctionnels.
Éléments à prendre en compte :
-
Au départ, la zone de résultats est vide.
-
L'application fournit (au maximum) 5 blagues par page, et permet de lister toutes les blagues
correspondant à une recherche grâce aux boutons "prev" et "next".
-
Le numéro de la page courante et le nombre de pages total sont affichés.
-
Les boutons "prev" et "next" sont désactivés quand ils ne peuvent (doivent) pas être utilisés.
-
La liste des mots clés correspondant à la page affichée est rappelée entre guillemets
sous la liste des blagues.
-
Une recherche avec mot(s) clé(s) qui n'existe(nt) pas affiche le message "Pas de résultat !".
Paramètres POST
POST vs GET
À la différence des paramètres GET qui, comme nous l'avons vu précédemment, sont envoyés au serveur en
étant directement écrits dans l'URL d'une requête, la méthode POST encapsule les paramètres
dans la requête HTTP.
De plus, l'envoi de données par la méthode GET est limité par les contraintes de la syntaxe des URL : elles
ne peuvent contenir que des caractères ASCII, pas d'informations binaires (comme des fichiers images, etc.),
et la taille d'une URL ne peut excéder 2048 caractères.
La méthode POST est donc utilisée lorsque le serveur requiert des données sensibles
(ex. informations sur l'utilisateur, mot de passe, ...), ou encore lors de l'envoi de fichiers.
Pour envoyer des données par POST on peut construire une chaine ressemblant à celle du GET et l'envoyer
en paramètre du send
, en n'oubliant pas de prévenir le serveur de la forme
des paramètres envoyés grâce à un setRequestHeader
.
let method = 'POST'
let url = "..." // une url acceptant des requêtes POST
httpRequest.open(method, url)
httpRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
let params = 'param1=valeur1¶m2=valeur2' // etc.
httpRequest.send(params)
Cependant, le plus simple est d'utiliser une objet FormData
!
La classe FormData
Les instances de la classe FormData
facilitent la gestion de paires clé:valeur qui
peuvent être envoyées simplement grâce à XMLHttpRequest
.
La syntaxe pour un envoi de ce type est la suivante :
let method = 'POST'
let url = "..." // une url acceptant des requêtes POST
httpRequest.open(method, url)
let data = new FormData()
data.append('param1', 'valeur1')
data.append('param2', 'valeur2')
// etc.
httpRequest.send(data)
Exemple d'une requête POST paramétrée
Dans cet exemple, nous allons utiliser une (très très simple pour la démo) API REST qui accepte des
requêtes POST. Cette API attend 2 paramètres nommés firstname
et lastname
.
Le script serveur renvoie alors une chaine JSON
contenant un id
qui a été généré à partir des données reçues. (NB: cet id
ne sert à rien,
c'est juste pour l'exemple).
Notre exemple utilise XMLHttpRequest
et FormData
pour envoyer une requête
POST avec des données dès le chargement de la page, puis insère l'id
reçu dans
le DOM de la page pour affichage.
<style>
#ajax-post-01{
color: blue;
font-weight: bold;
}
</style>
ID reçu du serveur : <span id="ajax-post-01"></span>
<script>
document.addEventListener('DOMContentLoaded', function (){
let display = document.querySelector("#ajax-post-01")
let httpRequest = new XMLHttpRequest()
httpRequest.onreadystatechange = function (){
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
let response = JSON.parse(httpRequest.response)
display.innerHTML = response.id
} else {
alert('ERREUR avec la requête.');
}
}
}
let url = "http://web.gregory-bourguin.fr/teaching/php/requests/ajax/02_generateUserID.php"
httpRequest.open('POST', url)
let data = new FormData()
data.append('firstname', 'Grégory')
data.append('lastname', 'Bourguin')
httpRequest.send(data)
})
</script>
Resultat
ID reçu du serveur :
Formulaires et FormData
L'utilisation "classique" des formulaires HTML a pour effet le chargement d'une nouvelle page correspondant
à la réponse du serveur.
Cependant, une application web "moderne" aura plutôt tendance à récupérer le résultat du serveur
pour modifier le DOM de la page.
AJAX est bien entendu tout indiqué pour réaliser ces fonctionnalités puisqu'il s'agit d'utiliser Javascript
pour envoyer les données au serveur, récupérer le résultat, et modifier le DOM sans changer de page.
Nous avons vu précédemment
que la classe FormData
facilite l'agrégation de données pour l'envoi dans des requêtes POST.
FormData
permet aussi d'agréger aisément les données correspondant aux inputs d'un formulaire.
<style>
#ajax-post-02{
color: blue;
font-weight: bold;
}
</style>
<form name="user-infos" id="user-infos" style="width: 300px"
action="http://web.gregory-bourguin.fr/teaching/php/requests/ajax/02_generateUserID.php"
method="POST">
<div class="form-group">
<label for="firstname">Prénom</label>
<input type="text" class="form-control" name="firstname" id="firstname" value="Gregory">
</div>
<div class="form-group">
<label for="lastname">Nom</label>
<input type="text" class="form-control" name="lastname" id="lastname" value="Bourguin">
</div>
<button type="submit" class="btn btn-primary">Envoyer</button>
</form>
<hr>
<p>
ID reçu du serveur : <span id="ajax-post-02" style="width: 50%"></span>
</p>
<script>
let formulaire = undefined
let display = undefined
let httpRequest = new XMLHttpRequest()
httpRequest.onreadystatechange = function (){
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
let response = JSON.parse(httpRequest.response)
display.innerHTML = response.id
} else {
alert('ERREUR avec la requête.');
}
}
}
document.addEventListener('DOMContentLoaded', function (){
formulaire = document.querySelector("#user-infos")
display = document.querySelector("#ajax-post-02")
formulaire.addEventListener('submit', function (event){
event.preventDefault() // bloquer le comportement par défaut du submit
// s'ils existent, on peut récupérer la méthode et l'action (url) sur les attributs du form
let method = formulaire.getAttribute("method")
let url = formulaire.getAttribute("action")
httpRequest.open(method, url)
// constructeur avec le formulaire en paramètre
let data = new FormData(formulaire)
// il faut que les noms des champs du formulaire correspondent à ce qu'attend le serveur !
httpRequest.send(data)
})
})
</script>
Template fecth
Il est possible d'écrire les requêtes fecth
avec plusieurs syntaxes différentes
mais on peut considérer que les étapes principales sont les suivantes :
let url = "..." // l'url du service
let options = {
method: '...', // GET, POST, PUT, DELETE, ...
headers: { ... } // à remplir au besoin
}
fetch(url, options).then(response => { // ceci déclenche l'appel...
if (response.ok) {
response.text().then(data => { // remplacer text() par json() pour parser directement
// ici le traitement quand tout s'est bien passé
})
} else {
// erreur sur le serveur
}
}).catch(error => {
// pb avec l'appel (ex. l'url est fausse)
})
Fetch & GET
La mise en place d'une requête GET paramétrée avec fetch
est similaire à la technique
mise en oeuvre avec XMLHttpRequest
: les paramètres sont encodés puis concaténés
à l'url du service GET visé.
Pour illustrer, nous allons transformer
l'exemple
que nous avions pris précédemment.
<div id="fetch-joke-05" class="joke"></div>
<script>
(function() {
let display = undefined
/**
* Recherche de blagues.
*/
function searchJoke() {
let url = "https://icanhazdadjoke.com/search" // url pour la recherche
let options = {
method: 'GET',
headers: { Accept: 'application/json' }
}
// construction des paramètres
let search = "dog bike"
search = encodeURIComponent(search)
let params = "?term=" + search + "&limit=5&page=1"
let request = url + params
// exécution de la requête
fetch(request, options).then(response => {
if (response.ok) {
response.json().then(data => { // json() parse les données
display.innerHTML = data.results[0].joke
})
} else {
alert("ERREUR avec la requête.", response.statusText);
}
}).catch(error => {
console.log("ERREUR avec le fetch.", error)
})
}
document.addEventListener('DOMContentLoaded', function () {
display = document.querySelector("#fetch-joke-05")
searchJoke()
})
})()
</script>
Fetch & POST
fetch
permet aussi d'exécuter des requêtes POST : les données sont ajoutées dans
le corps (body
) des options de l'appel.
Pour illustrer, nous transformons
l'exemple POST précédent.
<style>
#fetch-post-01{
color: blue;
font-weight: bold;
}
</style>
ID reçu du serveur : <span id="fetch-post-01" style="width: 50%"></span>
<script>
document.addEventListener('DOMContentLoaded', function (){
let display = document.querySelector("#fetch-post-01")
let url = "http://web.gregory-bourguin.fr/teaching/php/requests/ajax/02_generateUserID.php"
// préparation des données à envoyer
let data = new FormData()
data.append('firstname', 'Grégory')
data.append('lastname', 'Bourguin')
let options = {
method: 'POST',
body: data // ajout des données pour le POST
}
fetch(url, options).then(response => {
if (response.ok) {
response.json().then(data => { // json() parse les données
display.innerHTML = data.id
})
} else {
alert("ERREUR avec la requête.", response.statusText);
}
}).catch(error => {
console.log("ERREUR avec le fetch.", error)
})
})
</script>
Resultat
ID reçu du serveur :
TP 04
Pour vous exercer à mettre en oeuvre la "nouvelle" API fetch
,
recréez les exercices précédents, ainsi que le TP03, en supprimant toute utilisation
de XMLHttpRequest
.
Exercices/Fichiers à (re)créer :
-
tp04_ex01.html
: correspond à
l'Exercice 01.
-
tp04_jokes.html
: correspond au
TP 03.
-
tp04_form.html
: correspond à
l'Exemple POST avec formulaire.