Accueil > Intranet Michel Buffa > Cours HTML5/NodeJS/Angular etc. Master 2 NTDP 2016-2017 > HTML5 : exercice fil rouge sur formulaire, tableau, persistence, drag'n'drop, Ajax

HTML5 : exercice fil rouge sur formulaire, tableau, persistence, drag'n'drop, Ajax

De $1

Exercice fil rouge : un gestionnaire de contacts téléphoniques

Partie 1 : écriture d'une page de "template" pour la saisie et l'affichage de contacts dans un tableau

Objectif : réaliser une page HTML structurée avec les nouvelles balises HTML5 pour servir de « template » à l'application: <header>, <footer>, <section>, <article>, <nav>, <aside>, révision sur les tables HTML avec <table><thead><th><tbody><tr><td>... Insister sur le fait que la spec recommande d'utiliser des <header><h1> si on vise les browsers récents, mais que si on vise les anciens, utiliser <Hn> à la place. Dans tous les cas ne pas se reposer sur la CSS par défaut.

Travail à faire :

Dans un répertoire dédié à l'exercice (e.g. exercice1), créer une page HTML nommée index.html utilisant les nouveaux éléments cités dans le cours :

  • doctype simplifié
 -encodage en UTF8

  • Balises <header>,<footer>,<nav>,<aside>,<section>,<article>
  • Cette page devra contenir une liste de contacts codée directement dans le HTML, dans un tableau codé dans les règles de l'art (table, thead, th, tbody, tr, td...)

Exemple de résultat que l'on désire obtenir:

LaDevRoomExo1.jpg

Note pour les formateurs:

  • Correction ici : http://jsbin.com/manahi/3/edit
  • On fournira peut-être la feuille de style CSS que l'on commentera ensembles, ou bien on en montre des bouts au vidéo projecteur. On montre aux élèves le résultat à obtenir, en indiquant que ce n'est pas essentiel que leur travail ressemble exactement à celui là.
  • Erige, François, Hugo : vous pouvez jeter un oeil à la feuille CSS, je suis nul en CSS design.

Sur l'exemple, la barre de navigation permet théoriquement de se déplacer vers les différents écrans de l'application. En réalité, la page d’accueil n'existe pas (le lien pointera sur une page inexistante ou surla même page). Cette barre correspond à une balise <nav>. Les libellés de cette barre sont encapsulés dans une liste d'items non-ordonnés (balises <ul> et <li>)

La page contient une grande <section> et deux <article>, un pour le formulaire de saisie / modification à) venir, l'autre pour la présentation des contacts sous forme de tableau. N'oubliez pas la bonne pratique du cours qui dit que chaque "sectionning element" doit être immédiatement suivi d'un <header><h1>...

Le second <article> contient donc un élément <table>. Sur la droit, la balise <aside> contiendra les dernières actions effectuées dans l'application, là encore sous forme de liste d'actions (balises <ul> et <li>).

Le <footer> de l'application a été positionné de manière fixe, il doit être présent et visible même si on scrolle la page (voir "positions" en CSS).

Partie 2 : ajout d'un formulaire permettant l'ajout de contacts

Objectif : Appréhender les nouveaux éléments <input> de HTML5, quelques nouveaux attributs

Travail à faire :

Dans la partie correspondant au premier <article> de la page créée dans l'exercice 1, créer un formulaire permettant l'ajout de clients en utilisant les types de balise <input> appropriés. Ce formulaire apparaitra "à la demande" car on le mettra à l'intérieur d'éléments <summary><details>. Attention, ces éléments ne sont pas supportés par tous les navigateurs. Vous testerez avec Google Chrome ou Opera pour le moment. Cette limitation sera l'occasion de voir ensemble l'utilisation d'un "polyfill" (outils permettant la rétro-compatibilité).

Contraintes à prendre en compte :

  • Les champs pour les noms et prénoms sont de type texte,
  • Le champ pour la saisie de l'adresse electronique est de type email,
  • Le champ pour le No de téléphone est de type tel,
  • Le champ pour la saisie de la date de naissance est de type date, 
  • Le champ nombre d'enfants est de type range,
  • Pour le moment on ne fera rien lors de la soumission.

Il est recommandé d'utiliser des balises <label> pour le libellé des champs. Ainsi lorsque l'utilisateur cliquera sur le libellé, le focus sera automatiquement positionné sur le champ <input> 
correspondant.
 De même, il est recommandé d'utiliser une balise <fieldset> et <legend> pour la « décoration » du formulaire. Les libellés entourant le champ <input> de type range (i.e. 0 et 5) sont en dur dans la page.


Voici un exemple de réalisation : 

LaDevRoomExo2.jpgNote pour les formateurs :

  • Correction ici : http://jsbin.com/jufemu/6/edit, pour le moment pas de JS du tout, pas de required, pas de CSS feedback, etc. On peut discuter de l'intérêt ou du risque à utiliser summary/details, montrer que ça ne marche pas dans IE, dans FF, etc. Indiquer que c'est sorti de la spec pour cause de non implémentation généralisée mais que c'est passé dans la spec HTML5.1, donc pas abandonné (mais risque que ce soit plus affiné, notamment concernant le stylage CSS). Et enfin, montrer le support dans caniuse.com, et les polyfills disponibles dans 
  • Faire quelques rappels ou commentaires sur comment présenter des forms avec fieldset, legend, label, input etc.
     
  • Version avec polyfill "léger" : http://jsbin.com/nojobo/1/edit, celui indiqué par caniuse.com (https://gist.github.com/remy/370590), le lien pointe sur un commentaire qui contient une démo jsbin (http://jsbin.com/akadi4/26/edit). C'est un polyfill "léger", juste du js à inclure (voir onglet js) et trois lignes dans le HTML.
     
  • Correction utilisant un polyfill très complet ici, un trouvé en cherchant sur google, gère le stylage, mes events, etc... si on veut le support des vieux IEs, il faut aussi des polyfills pour le DOM, les sections, articles, etc...: http://jsbin.com/qexisa/2/edit (marche partout). Le polyfill utilisé est celui-ci: https://github.com/termi/Element.details, il contient une démo jsbin.com dont j'ai copié des lignes. Conclusion : un support complet en polyfill c'est LOURD!

Partie 3 : Validation "statique" du formulaire + feedback CSS

Objectif : appréhender la validation « statique » (i.e. sans JavaScript) et les règles CSS spécifiques aux champs <input> de HTML5...

Travail à faire :

On continue à tavailler à partir de l'exercice précédent.. On va rajouter quelques attributs sur les champs de saisie et ajouter des règles CSS pour avoir un "feedback visuel" lors de la saisie.

Contraintes :

  • Les champs "prénom", "nom" et "téléphone" sont obligatoires,
  • La longueur maximale des champs nom et prenom est de 32 caractères, 
  • La longueur maximale du champ email est de 128 caractères.,
  • Le téléphone doit respecter le format français (10 chiffres, par groupes de 2 séparés par un espace, par exemple), vous utiliserez l'attribut pattern pour cela,
  • On affichera un exemple de No de téléphone en filligrane (attribut placeholder),
  • Le nombre d'enfants est compris entre 0 et 5,
  • Implémenter quelques règles CSS pour modifier le visuel des champs s'ils ne sont pas valides. Vous pourrez jouer sur la bordure, la couleur de fond, l'ombre. Ex : rose si non valide, vert clair si valide.
  • Une fois que tout ceci fonctionne, essayez d'affiner les règles pour la saisie du numéro de téléphone (on autorisera les +33 etc...), voir ce site qui référence les patterns les plus fréquentes : http://html5pattern.com/Phones

Exemple de résultat :

LaDevRoomExo3.jpg

Note pour les formateurs :

Partie 4 : validation dynamique depuis les APIs de validation de formulaires de HTML5

Objectif :nappréhender l'API JavaScript de validation des formulaires.

Travail à faire :

Implémenter les règles suivantes :

  • Le nom et le prénom ne doivent pas contenir les caractères : %, &, $, et !,
  • La date de naissance doit être dans le passé.

Plusieurs méthodes sont envisageables:

  1. Pour le champ de saisie des noms et prénoms, vous pourrez soit écouter chaque appui de touche (événement "input"), soit effectuer une validation lors de la soumission du formulaire. 
  2. Le champ de saisie de date peut être validé par l'écoute de l'évènement "input" ou "change",
  3. On peut aussi mettre un événement "submit" sur le <form onsubmit="valider();">, mais on privilégiera plutot la validation "à la volée" des propositions 1 et 2.
  4. Pour valider le fait qu'on ne puisse pas taper de caractères %, &, $ et !, on pourra utiliser soir une expression regulière JavaScript (dans l'écouteur de l'événement input), soit un attribut pattern dans la définition HTML du champ. Mais essayez de le faire en JS, pour une fois !

On pourra rajouter des ids aux champs de saisie, ou bien utiliser l'API du DOM au niveau du formulaire. En effet, l'objet DOM document.forms retourne un tableau de tous les éléments DOM correspondant aux balises <form> du document. Dans un objet DOM correspondant à la balise <form>, il est possible de pointer sur les éléments DOM correspondants aux balises <input> par leur nom. Par exemple, si une balise <input> possède un attribut name="nom", l'expression document.forms[0].nom retournera l’élément DOM correspondant. 
A vous de prendre la méthode que vous préférez.

La propriété "valueAsDate" d'un élément DOM associé à une balise <input type="date"> de type date (e.g. le champ date de naissance) retourne la date saisie sous forme d'un objet de type Date.
 Deux objets de type Date ne nécessitent pas de traitement particulier pour être comparés : deux objets de type Date peuvent donc être comparés entre-eux avec les opérateurs <, >, ==, != ... 


La propriété "valueAsNumber" d'un élément DOM associé à une balise <input> de type range (e.g. Le champ Nombre d'enfants) retourne la valeur saisie sous forme d'une valeur de type number. 

On n'utilisera pas de polyfills ici, vous avez compris le principe lors des exemples vu dans la partie 2. Vous testerez donc cette partie avec un navigateur supportant <input type="date"> Toutefois si vous désirez investiguer, vous pourrez aller voir ce polyfill ou celui là.

 
Exemple de résultat :
LaDevRoomExo4.jpg

Note pour les formateurs :

  • Correction ici : http://jsbin.com/cotiqi/1/edit, si on veut interdire les caractères bizarres avec une pattern on utilisera par exemple pattern="[^&%$!]+",
  • C'est l'occasion aussi de montrer des polyfills si les étudiants sont curieux.
  • C'est aussi l'occasion de rappeler les règles de base de la manipulation du DOM et de la gestion des events en JavaScript.
  • On peut présenter aussi une version avec un beep (dont le contenu est dans un dataURL embarqué dans la page, pour qu'il soit prêt en même temps que la page) lorsqu'on tape une erreur:  http://jsbin.com/zaqowa/4/edit, ici une autre version qui utilise l'API WebAudio poursynthétiser un beep, Peugeot m'avait demandé ça pour avoir des beeps différents: http://jsbin.com/zaqowa/6/edit, là on peut passer la durée et le type de son en paramètre à la fonction beep(...);

Partie 5 : Sauvegarde de contact dans le LocalStorage

Objectif :utiliser l'API localStorage pour écrire des données. 

Travail à faire :

Modifier l'application pour que le formulaire permette de stocker les données saisies dans le localStorage, sous la forme d'un tableau d'objet (des contacts) au format JSON. Chaque case du tableau contiendra les informations d'un contact. Le formulaire aura donc pour rôle d'ajouter un objet dans ce tableau lors de la validation du formulaire, puis de l'enregistrer dans le localStorage.

L'ajout des données est fait dès que l'utilisateur valide les données du formulaire, c'est à dire au moment où l'élément DOM correspondant à la balise <form> lève l’événement submit. (form onsubmit=... par exemple).

Avant de modifier l'exemple fil rouge, vous commencerez par étudier ce petit exemple qui montre comment écrire un tableau de données au format JSON : http://jsbin.com/nacoho/6/edit

LaDevRoomExo5preparation.jpg

Vous pouvez vérifier que la sauvegarde est correcte en ouvrant les devtools e Google Chrome par exemple (F12 ou cmd alt i sur mac), et en allant dans l'onglet "ressources":

LaDevRoomExo5preparation1.jpg

Maintenant, en vous inspirant de ce petit exemple, modifiez le programme fil rouge pour que les données saisies dans le formulaire s'ajoutent à un tableau nommé "contacts" et que ce tableau soit sauvegardé au format JSON dans le localStorage. Vous veillerez à ce que lorsqu'on relance le programme le tableau précédemment sauvegardé soit restauré.

LaDevRoomExo5.jpg

Vous veillerez à être capables d'ajouter des contacts : vérifiez bien que le tableau dans le localStorage "grandit". Fermez et ouvrez l'onglet de la page de votre application, les données doivent être persistantes.

Pour les formateurs :

  • Correction ici :  http://jsbin.com/xefequ/7/edit, rien de spécial à ajouter...
  • Profiter de l'exercice de préparation pour rappeler comment on transforme des objets JavaScript en JSON, avec JSON.stringfy(obj), et l'inverse avec JSON.parse(string_json) -> object JS.

Partie 6 : lecture de contacts dans le localStorage et affichage dans le tableau

Objectif : continuer avec localStorage, manipuler l'API du DOM de JavaScript, les tableaux...

Travail à faire : cette fois-ci vous allez modifier votre code pour que le tableau de contacts lu sitôt la page chargée, soit affiché dans le tableau. Vous ferez également en sorte que lors de la saisie d'un nouveau contact, le tableau soit rafraichi.

  • Retirer les données en dur dans le tableau de clients,
  • Implémenter une fonction construitTable(tableauDeContacts) qui prend en paramètre le tableau de contacts et reconstruit les lignes du tableau en conséquence.
  • Se débrouiller pour que lorsqu'on ajoute un nouveau client on ajoute une ligne dans le tableau. Les plus malins pourront essayer de ne pas le ré-afficher totalement (optionnel)

Exemple de résultat :

LaDevRoomExo6.jpg

Note pour les formateurs :

  • Correction ici: http://jsbin.com/maxebu/1/edit?output, remarquez plusieurs choses dans cette correction:
    1. On ne veut pas soumettre le formulaire, puisqu'on veut juste en cas de soumission valide, rajouter une ligne dans le tableau après avoir mis à jour les données dans le localStorage.
    2. Mais on veut quand même que si on clique sur le bouton de soumission, le formulaire soit validé par le browser par la validation de HTML5.
    3. Il suffit pour cela de modifier l'appel de la fonction de callback de l'événement submit <form onsubmit="soumettre()"> par <form onsubmit="return soumettre"> et de renvoyer false à la fin de la sonction soumettre().
    4. On utilise pas jquery mais il est probable que cela fasse gagner quelques lignes de code. Néanmoins il est important de savoir se débrouiller avec l'API standard du DOM, ce n'est pas compliqué.
 

Partie 7 : suppression d'un client par drag'n'drop

 
Objectif : premiers pas avec l'API HTML5 du Drag and drop. 
 
Travail à faire : 
  • ajouter une icone en forme de poubelle à la fin du tableau pour permettre la suppression d'un contact de la liste par une action de glisser-déposer.
  • On ajoutera aussi en première colonne du tableau une icone en forme de document, si on clique et qu'on draggue cette icone sur la poubelle, la ligne sera supprimée du tableau HTML mais aussi du tableau JavaScript. Ce dernier sera alors écrit dans le localStorage.
  • Vous devrez ajouter au code JavaScript une fonction supprimeContact(n) prenant en paramètre un index (le numéro de la ligne à supprimer). Cette fonction devra supprimer du tableau HTML le contact avec l'index donné en paramètre, sauver dans le localStorage le tableau de clients modifié et le retourner.
  • Le drop handler attaché à la poubelle appellera la fonction précédente,
  • Le drag handler attaché à la nouvelle icone de chaque ligne copiera dans le clipboard du drag'n'drop l'index de la ligne à supprimer.
  • Icone poubelle que vous pouvez utiliser : http://i.imgur.com/yHyDPio.png
  • Icone document que vous pouvez utiliser : http://i.imgur.com/lPxVKkf.png

Exemple de résultat :

LaDevRoomExo7.jpg

Note pour les formateurs :

  • Correction ici : http://jsbin.com/niduqi/1/edit
  • On a créé l'écouteur de drag dans la fonction qui créée les lignes du tableau, et on a dû utiliser une closure, essayer d'expliquer pourquoi aux élèves, mais après qu'ils aient eu des soucis...
  • L'écouteur de drop est défini au début, avec les autres, dans la fonction d'init.
 
 

Partie 8 : ajout de quelques règles CSS3 (webfonts, arrondis, ombres, etc.)

 
Objectif : appréhender quelques nouveautés de CSS3
 
travail à faire :
 
A partir d'une feuille de style dédiée à l'application, ajouter des règles pour :
  • Utiliser une Web Font comme police pour le titre de l'application,
  • Mettre le titre de l'application dans un rectangle arrondi avec une ombre,
  • Essayer de ne mettre q'une ligne sur trois en surligné dans le tableau

Exemple de résultat :

LaDevRoomExo8.jpg

Note pour les formateurs :

  • Correction ici :http://jsbin.com/bijiku/2/edit, notez que pour que la police marche il faut 1) la downloader et 2) tester ce code en dehors de jsbin.com, à cause des problèmes de cross origin. Pour vraiment changer la police, tester avec les google fonts. Comme ici : http://jsbin.com/tudifi/1/edit
  • On a juste mis un div autours du titre principal + du CSS via la classe .box, et une règle font-family: sur le H1 qui est de la classe .box
 

Partie 9 : jouer avec les transitions CSS3

 
Objectif : apprendre à utiliser les transitions et animations HTML5
 
Travail à faire :
  • Sans utiliser JavaScript, faire en sorte que lorsqu'on passe sur une case du tableau et qu'on reste au moins 1s sur immobile, sa bordure change de couleur, en utilisant une transition CSS3. Lorsqu'on quitte la ligne, elle revient comme avant.
  • Faire en sorte que le titre principal ait une animation cyclique sur son ombre, que le titre donne l'impression d'avancer vers nous puis de repartir, par cycle de 1s.
  • Lorsque la souris passe sur le titre, celui-ci tourne à 360 degrès, oui, on a le droit d'un peu s'amuser dans cette formation :-)

Note pour les formateurs :

Partie 10 : interface avec un Web Service REST via Xhr2 / JSON

 
Objectif : appréhender la communication HTTP avec du JSON en s'appuyant sur l'API XHR2. 
 
Important : pour cet exercice vous ne pourrez plus utiliser jsbin.com, il est impératif que l'application soit dans des fichiers car nous allons travailler avec un serveur nodeJS dédié.
 
Travail à faire :
  • Utiliser le serveur fourni pour les requêtes HTTP. Celui-ci requiert l'installation préalable de nodejs téléchargeable à l'adresse suivante : http://nodejs.org/.
  • Modifier le code de l'application existante pour pouvoir vous interfacer avec ce serveur :
    • Lors de l'ouverture de la page, on récupère les données via une requête XhR2 sur le serveur, on construit le tableau JavaScript et HTML des contacts, on continue à la sauvegarder dans le localStorage comme avant.
    • On laisse la fonctionnalité existante telle quelle mais on rajoute la couche "lecture depuis le serveur au démarrage" et "écriture du tableau sur le serveur chaque fois qu'on l'écrit aussi dans le localStorage.
 
L'application serveur gère les requêtes suivantes :
URL Méthode HTTP Données
/contacts GET

Entrée : aucune
,

Sortie : Tableau de contacts au format JSON.

/contact/new POST

Entrée : nouveau contact au format JSON.


Sortie: un objet au format JSON contenant l'index du client ajouté.

 
 Code serveur, à faire exécuter par nodeJS ("node server.js") :    
 
var http = require("http");
var URL = require("url");
var contacts = [];
var headers = {
	"Content-Type" : "application/json",
	"Access-Control-Allow-Origin" : "*",
	"Access-Control-Allow-Headers" : "origin, content-type, accept",
	"Access-Control-Allow-Methods" : "GET, POST"
};

http.createServer(function(request, response) {
	var buffer = "";

	request.on("data", function(chunk) {
		buffer += chunk;
	});

	request.on("end", function() {
		var data;
		var pathname = URL.parse(request.url, true).pathname;
		if (request.method == "GET" && pathname == "/contacts") {
			response.writeHead(200, headers);
			response.end(JSON.stringify(contacts));
		} else if (request.method == "POST" && pathname == "/contacts/new") {
			response.writeHead(200, headers);
			try {
				data = JSON.parse(buffer);
			} catch (e) {
			}
			if (data) {
				contacts.push(data);
				response.end(JSON.stringify({}));
			} else {
				response.end(JSON.stringify({
					error : true
				}));
			}
		} else if (request.method == "OPTIONS") {
			response.writeHead(200, headers);
			response.end();
		} else {
			response.writeHead(403, headers);
			response.end();
		}
	});
}).listen(8084);
 
Pour lancer le serveur, ouvrir une invite de commande, copier le code ci-dessus dans un fichier server.js, l'exécuter en tapant la commande "node server.js"
 
Note pour les formateurs :
  • Correction à venir..
  • On devrait peut-être le re-écrire le code serveur avec express, ce serait plus simple.
  • Pré-remplir le tableau des contacts avec des données de test ? Je ne l'ai pas fait car je ne sais pas comment les élèves auront nommé les propriétés des contacts...

Partie 11 : faire fonctionner l'application en mode offline, utilisation du cache HTML5

Objectif : rendre l'application disponible en mode déconnecté.

Travail à faire :

  • Créer un fichier manifest pour y déclarer les fichiers accessibles hors-connexion
  • Si on est en mode hors connexion, alors on ne travaille qu'avec le localStorage,
  • Utiliser l'API applicationCache pour détecter une mise à jour et proposer aussitôt à l'utilisateur de recharger l'application,
  • Enfin (plus difficile), écouter les évènements permettant de détecter un passage en mode connecté, et dans ce cas, synchroniser les données avec le serveur. Pour simplifier on supposera que personne ne peut changer les données serveur en votre absence (pas de merge, on remet juste les données serveur à jour en copiant celles du localStorage sur le serveur).

Note pour les formateurs :

  • Correction à faire...
Mots clés:
 
Images (0)
 
Commentaires (0)
Vous devez être connecté pour poster un commentaire.