Programmation Web en JavaScript
Transcription
Programmation Web en JavaScript
, Programmation Web Côté Client avec JavaScript et jQuery Rémy Malgouyres LIMOS UMR 6158, IUT, département info Université Clermont 1 B.P. 86 63172 AUBIERE cedex http://malgouyres.org/ Tous mes cours sur le Web sont sur le Web : Cours de programmation WEB sur les documents hypertexte HTML/CSS : http://malgouyres.org/programmation-html-css Tutoriel sur le CMS Drupal : http://malgouyres.org/tutoriel-drupal Cours de programmation WEB côté serveur en PHP : http://malgouyres.org/programmation-php Cours de programmation WEB côté client en JavaScript : http://malgouyres.org/programmation-javascript Cours sur l’administration de serveurs (Serveurs WEB avec apache, SSL, LDAP...) : http://malgouyres.org/administration-reseau Table des matières 1 Premiers pas en JavaScript 1.1 Balise <script> et Hello world en JavaScript . . 1.2 Types, variables et portée . . . . . . . . . . . . . 1.3 Fonctions . . . . . . . . . . . . . . . . . . . . . . 1.4 Objets . . . . . . . . . . . . . . . . . . . . . . . . 1.5 Tableaux (le type Array) . . . . . . . . . . . . . . 1.6 Exemple : traitement d’un formulaire avec jQuery . . . . . . 5 5 6 6 7 11 13 . . . . . . . 16 16 19 20 21 23 27 37 . . . . . 41 41 42 45 47 55 4 Interfaces Hommes Machines (IHM ) 4.1 Filtrage Basique des Inputs d’un Formulaire . . . . . . . . . . . . . . . . . . . 4.2 Pattern Mediator pour le filtrage d’attributs . . . . . . . . . . . . . . . . . . . 4.3 Exemple : génération automatique de formulaire d’adresse . . . . . . . . . . . 58 58 60 64 5 Exemple d’Application Interactive 5.1 Principe de l’application et analyse fonctionnelle 5.2 Modèle de donnée . . . . . . . . . . . . . . . . . 5.3 Pattern Mediator : centraliser les événements . 5.4 Événements concernant les personnes . . . . . . 5.5 Événements concernant les Adresses . . . . . . . 71 71 71 74 77 90 . . . . . . . . . . . . . . . . . . . . . . . . 2 Programmation Fonctionnelle et Objet en JavaScript 2.1 Le Pattern Module . . . . . . . . . . . . . . . . . . . . . 2.2 Passage d’arguments par objets . . . . . . . . . . . . . . 2.3 Exemple de fabrique sommaire . . . . . . . . . . . . . . 2.4 Structuration d’une application . . . . . . . . . . . . . . 2.5 Exemple : un module metier.regexUtil . . . . . . . . . 2.6 Module Métier adresse . . . . . . . . . . . . . . . . . . 2.7 Création d’un Module myApp.view.adresse . . . . . . . 3 Constructeurs, Prototype et Patterns Associés 3.1 Constructeurs . . . . . . . . . . . . . . . . . . . 3.2 Prototypes . . . . . . . . . . . . . . . . . . . . . 3.3 Exemple : assurer l’implémentation d’interfaces 3.4 Fabrique d’Objets Métier avec prototype . . . . 3.5 Patterns pseudo-classique (à éviter) . . . . . . . 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . TABLE DES MATIÈRES 6 Requêtes Asynchrones et API Restful 6.1 Qu’est-ce qu’une requête asynchrone ? . . . . . . . . 6.2 Requête Ajax . . . . . . . . . . . . . . . . . . . . . . 6.3 Qu’est-ce qu’une API REST (ou systèmes Restful) ? . 6.4 Exemple d’API Restful . . . . . . . . . . . . . . . . . 6.5 Persistance avec AJAX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 99 100 103 103 114 A Graphisme avec les Canvas HTML5 125 A.1 Notion de canvas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 A.2 Exemple d’animation dans un canvas . . . . . . . . . . . . . . . . . . . . . . . 126 B Programmation Événementielle en JavaScript 128 B.1 Rappel sur la Gestion d’Événements en CSS . . . . . . . . . . . . . . . . . . . 128 B.2 Événements en Javascript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 C Gestion des fenêtres 134 C.1 Charger un nouveau document . . . . . . . . . . . . . . . . . . . . . . . . . . 134 C.2 Naviguer dans l’historique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 C.3 Ouvrir une nouvelle fenêtre (popup) . . . . . . . . . . . . . . . . . . . . . . . 136 D Document Object Model (DOM) 137 D.1 Qu’est-ce que le DOM ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 D.2 Sélection et Manipulation de Base sur le DOM . . . . . . . . . . . . . . . . . . 138 2 Architectures client/serveur et API Architecture d’une application multi plate-formes Une application multi plate-formes contemporaine cherchera à se structurer suivant (voir figure 1) : 1. Une application sur un serveur (API ) qui traitera les données et assurera la persistance ; 2. Une application sur chaque type de client, qui utilise ce serveur via des requêtes, et gère l’interface (par exemple une Interface Homme Machine (IHM)). Base de données API Serveur HTTP requêtes données es ée êt nn qu s es re ée êt do nn qu Client Web re Client Mobile do s Acteur secondaires Appli. Tierce etc. Figure 1 : La structure typique d’une application multi plate-formes On aura dans la mesure du possible intérêt à limiter le plus possible le travail côté client pour les raisons suivantes : 1. L’implémentation côté client dépend de la plate-forme et l’implémentation sur le serveur constitue un forme de factorisation du code. 2. Sur certaines plate-formes, comme dans le cas des applications web en JavaScript, la sécurité et la confidentialité côté client sont très mauvaises, alors que nous pouvons implémenter la sécurité côté serveur. Cependant, dans la mesure du possible, les opérations peu sensible, par exemple concernant l’ergonomie, se feront côté client pour limiter les coûts d’infrastructure (nombre de serveurs...) et améliorer la réactivité de l’application. 3 TABLE DES MATIÈRES Le cas de l’application Web Dans ce cours, nous étudions le développement d’applications Web (auxquelles on accède via un navigateur internet), avec une architecture client/serveur dans laquelle (voir la figure 2) : • Notre API est un serveur HTTP implémenté en PHP avec une architecture MVC et DAL ; • Notre application côté client est en JavaScript (qui s’est imposé comme un langage standard côté client), et utilise la librairie jQuery pour la gestion des événements, des vues, et des interactions (requêtes et échange de données au format JSON ) avec le serveur. Client : IHM événements utilisateur requêtes asynchrones requêtes GET, POST, PUT, DELETE architecture MVC données parse/génère JSON génération vues HTML Serveur : API points d’entrée et actions JSON parse/génère JSON persistance Driver de BD architecture DAL (génér./exec. SQL) Figure 2 : L’architecture client/serveur de notre application Web 4 BD Chapitre 1 Premiers pas en JavaScript 1.1 Balise <script> et Hello world en JavaScript Une première manière d’insérer un script JavaScript dans un fichier HTML est de mettre le code JavaScript dans une balise <script></script>. On a alors accès au document dans le code JavaScript et on peut sortir du code HTML : exemples/bases/ex01_helloWorld.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!doctype HTML> <html lang=” f r ”> <head> <meta charset=”UTF−8” /> <t i t l e>H e l l o World en J a v a s c r i p t</ t i t l e> </head> <body> <p> <s c r i p t> document . w r i t e ( ” H e l l o w o r l d ! ” ) ; </s c r i p t> <p> </body> </html> Une première manière d’insérer un script JavaScript dans un fichier HTML est de mettre le code JavaScript dans un fichier .js séparé, qui est inclus dans le HTML au niveau du header par une balise <script src=''...''></script>. exemples/bases/ex02_helloWorld.html 1 2 3 4 5 6 7 8 9 10 <!doctype HTML> <html lang=” f r ”> <head> <meta charset=”UTF−8” /> <t i t l e>H e l l o World en J a v a s c r i p t</ t i t l e> <s c r i p t src=” . / e x0 2_ h el lo Wo rld . j s ”></s c r i p t> </head> <body> </body> </html> 5 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery exemples/bases/ex02_helloWorld.js 1 a l e r t ( ” H e l l o World ! ” ) ; Dans ce dernier cas, on n’a pas accès au document dans le fichier JavaScript, mais il y a d’autres avantages (factorisation et mise en cache du code JavaScript partagé entre plusieurs pages HTML par exemple). 1.2 Types, variables et portée Le JavaScript est un langage faiblement typé, car on n’indique pas le type des variables lors de la déclaration. Lors le la déclaration des variables, le type est fixé implicitement par le type de la donnée affectée à la variable. La déclaration de la variable peut contenir ou non le mot clef var. Un variable déclarée avec le mot clef var (on parle de déclaration explicite) est locale à la fonction où la variable est déclarée. Une variable déclarée sans le mot clef var (on parle de déclaration implicite) est globale. Il n’y a pas, contrairement au C++ ou Java, de visibilité locale à un bloc. Un variable déclarée n’importe où dans une fonction est visible dans toute la fonction au moins. Pour cette raison, on déclarera systématiquement les variables locales à la fonction au début du corps de la fonction, contrairement aux bonnes pratiques dans d’autres langages où on déclare la variable au plus près de son point de première utilisation. Dans les programmes assez gros structurés en modules ou packages, on peut créer en JavaScript l’équivalent d’un namespace par un patron de conception consistant à mettre le code de l’ensemble d’un module dans le corps de définition d’une fonction ou dans un littéral définissant un objet (voir plus loin pour la notion d’objet). 1.3 Fonctions Les fonctions en JavaScript sont déclarées par le mot clef function. c’est un type de données comme un autre, et une fonction peut ainsi être affectée à une variable. Voici un exemple de fonction qui calcule le prix TTC d’un produit à partir de son prix hors taxes. Comme les paramètres des fonctions ne sont pas typés, on peut vérifier le type des paramètres dans la fonction et éventuellement renvoyer une exception si le type du paramètre effectif n’est pas le bon. exemples/bases/ex03_function.html 1 2 3 4 5 6 7 8 9 10 11 12 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>F o n c t i o n s</ t i t l e> </head> <body> <p> <s c r i p t> // Dé c l a r a t i o n d ’ une v a r i a b l e de t y p e f o n c t i o n var calculPrixTTC = function ( prixHT , tauxTVA ) { i f ( ! ( typeof prixHT == ” number ” ) | | ! ( typeof tauxTVA == ” number ” ) ) { 6 Chapitre 1 : Premiers pas en JavaScript 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 throw new E r r o r ( ” Function calculPrixTTC a p p e l é e a v e c paramètre i n c o r r e c t . ” ) } return prixHT *(1.0+tauxTVA / 1 0 0 . 0 ) ; }; // Appel c o r r e c t de l a f o n c t i o n : try { document . w r i t e ( ” P r i x TTC : ” + calculPrixTTC ( 1 8 0 . 0 , 1 9 . 6 ) ) ; } catch ( e r r ) { alert ( err ) ; } // Appel i n c o r r e c t de l a f o n c t i o d é c l e n c a h n t une e x c e p t i o n : try { document . w r i t e ( ” P r i x TTC : ” + calculPrixTTC ( ” coucou ” , 1 9 . 6 ) ) ; } catch ( e r r ) { alert ( err ) ; } </s c r i p t> <p> </body> </html> Notons que l’on peut aussi déclarer une fonction un peu comme en PHP de la manière suivante : 1 2 3 function myFunction (myParam) { return (myParam < 0 ) ; } mais la fonction est alors globale (son nom existe dans tout le programme). La bonne pratique consiste à déclarer les éléments d’un programme de sorte qu’ils aient la portée la plus locale possible, donc à déclarer la fonction avec le mot clé var comme dans le premier exemple de fonction ci-dessus. 1.4 Objets Un objet JavaScript rassemble plusieurs propriétés, qui peuvent être des données, d’autres objets, ou encore des fonctions, alors appelées méthodes. Un objet n’est ni tout à fait une structure comme en C, ni tout à fait une classe comme dans un langage objet classique. Par exemple, un objet JavaScript n’a pas de visibilité (privée, public) pour ses propriétés. Par ailleurs, le principal mécanisme d’héritage en JavaScript se fait par la notion de prototype et est très différent de l’héritage dans les langages objet classiques. Là encore, on peut mimer une notion de visibilité via des patrons de conception. Les noms de propriétés peuvent être • Soit une chaîne de caractère (comme ”nom de propriété !”) quelconque (respecter les doubles quotes dans un tel cas). 7 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery • Soit des noms légaux (commençant par une lettre suivi par une suite de lettres, chiffres, et underscores (caractère _) auquel cas les doubles quotes sont optionnelles pour désigner le nom. 1.4.1 Création d’un objet au moyen d’un littéral On peut créer un nouvel objet par un littéral, en définissant ses propriétés des accolades {}. On met alors chaque nom de propriété suivi d’un : suivi de la valeur de la propriété. Les propriétés ainsi construites sont séparées par des virgules. exemples/bases/ex04_objectLitteral.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>O b j e t s</ t i t l e> </head> <body> <p> <s c r i p t> // L i t t é r a l d é f i n i s s a n t un o b j e t : var p r o d u i t = { ” denomination ” : ” Notebook s o u s Ubuntu 4 c o r e s 2 . 0GB” , ” prixHT ” : 1 8 0 . 0 , ”tauxTVA” : 1 9 . 6 }; // Dé c l a r a t i o n d ’ une f o n c t i o n a v e c un paramètre : var calculPrixTTC = function ( prod ) { // Test d ’ e x i s t e n c e d e s p r o p r i é t é s de l ’ o b j e t : i f ( ” prixHT ” in prod && ”tauxTVA” in prod ) { return prod . prixHT *(1.0+ prod . tauxTVA / 1 0 0 . 0 ) ; } else { // R e j e t d ’ une e x c e p t i o n p e r s o n n a l i s é e : // On r e j e t t e un o b j e t a v e c une prop . ”name” e t une prop . ” message ” . throw {name : ”Bad Parameter ” , message : ” Mauvais t y p e de paramètre pour l a f o n c t i o n calculPrixTTC ”}; } }; // E s s a i d ’ a p p e l de l a f o n c t i o n try { document . w r i t e ( ” P r i x TTC du p r o d u i t \” ”+p r o d u i t . denomination +” \” : ”+calculPrixTTC ( p r o d u i t ) ) ; } catch ( e ) { // a f f i c h a g e de l ’ e x c e p t i o n p e r s o n n a l i s é e . a l e r t ( ”Une e r r e u r \” ” + e . name + ” \” s ’ e s t p r o d u i t e :\n” + e . message ) ; } </s c r i p t> <p> </body> </html> 8 Chapitre 1 : Premiers pas en JavaScript Un objet peut contenir des propriétés qui sont de type function. On parle alors de méthode de l’objet. Dans une méthode, on accède aux propriétés des l’objet grâce à l’identificateur this, désignant l’objet auquel appartien la méthode. exemples/bases/ex05_objectMethod.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>O b j e t s</ t i t l e> </head> <body> <s c r i p t> var p r o d u i t = { ” denomination ” : ” Notebook s o u s Ubuntu 4 c o r e s 2 . 0GB” , ” prixHT ” : 1 8 0 . 0 , ”tauxTVA” : 1 9 . 6 , // Dé f i n i t i o n d ’ une mé t h o d e : getPrixTTC : function ( ) { return t h i s . prixHT *(1.0+ t h i s . tauxTVA / 1 0 0 . 0 ) ; } }; // F on c ti o n dans l e c o n t e x t e g l o b a l var getHtmlObjet = function ( o b j e t ) { var c h a i n e = ” ” ; // Parcours de t o u t e s l e s p r o p r i é t é s de l ’ o b j e t ( s t y l e ” f o r e a c h ” ) f o r (nom in o b j e t ) { c h a i n e += ” o b j e t [ \ ” ”+nom+” \ ” ] = ”+o b j e t [ nom]+ ”<br />” ; } return c h a i n e ; }; // a p p e l d ’ une f o n c t i o n d é f i n i e dans l e c o n t e x t e g l o b a l document . w r i t e ( ”<p>” + getHtmlObjet ( p r o d u i t ) + ”</p>” ) ; : : // a p p e l d ’ une mé t h o d e : document . w r i t e ( ”<p>P r i x TTC : ” + p r o d u i t . getPrixTTC ( ) + ”</p>” ) ; </s c r i p t> </body> </html> Une méthode d’objet JavaScript n’est pas tout à fait comme une méthode d’un langage à objet classique, car la méthode JavaScript existe en autant d’exemplaires qu’il y a d’instance des objets. Nous verrons plus loin la notion de prototype, qui permet de crée des méthodes qui existent en un seul exemplaire pour toute une classe d’objets ayant les mêmes propriétés. exemples/bases/ex06_nestedObjects.html 1 2 3 4 5 6 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>O b j e t s</ t i t l e> </head> 9 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 7 <body> 8 <s c r i p t> 9 var p r o d u i t = { 10 denomination : ” Notebook s o u s Ubuntu ” , 11 prixHT_base : 1 8 0 . 0 , 12 tauxTVA : 2 0 . 0 , 13 // O b j e t ” n i c h é ” dans un sur−o b j e t 14 options : { 15 p r o c e s s o r : ” I n t e l 4 c o r e s 2 . 5 Ghz” , 16 memory : ”4GB” , 17 ” p r i x s u p p l é m e n t a i r e HT” : 5 0 . 0 , 18 getHtml : function ( ) { 19 return t h i s . p r o c e s s o r + ” ” + t h i s . memory + 20 ” ( s u p p l é ment : ” + t h i s [ ” p r i x s u p p l é m e n t a i r e HT” ] + ” &euro ; ) ” ; 21 } 22 }, 23 // Dé f i n i t i o n d ’ une mé t h o d e : 24 getHtml : function ( ) { 25 return t h i s . denomination + 26 ”<br />p r i x TTC t o u t compris : ” 27 + ( t h i s . prixHT_base + ( t h i s . o p t i o n s [ ” p r i x s u p p l é m e n t a i r e HT” ] | | 0.0) ) 28 *(1.0+ t h i s . tauxTVA / 1 0 0 . 0 ) 29 + ” &euro ;<br />” + t h i s . o p t i o n s . getHtml ( ) + ”<br />” ; 30 } 31 }; 32 33 // a p p e l d ’ une mé t h o d e : 34 document . w r i t e ( ”<p>” + p r o d u i t . getHtml ( ) + ”</p>” ) ; 35 </s c r i p t> 36 </body> 37 </html> 1.4.2 Constructeur d’objets, mot réservé new On peut créer un objet via le constructeur Object. Voici un exemple où l’on crée un objet qui représente un produit. On crée ensuite une fonction qui calcule le prix TTC de ce produit après avoir testé l’existence d’attributs. exemples/bases/ex07_objectNew.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>O b j e t s</ t i t l e> </head> <body> <p> <s c r i p t> var p r o d u i t = new Object ( ) ; p r o d u i t . denomination = ” Notebook s o u s Ubuntu 4 c o r e s 2 . 0GB” ; p r o d u i t . prixHT = 1 8 0 . 0 ; p r o d u i t . tauxTVA = 2 0 . 0 ; 10 Chapitre 1 : Premiers pas en JavaScript 15 16 17 18 19 20 21 22 23 24 25 26 27 var calculPrixTTC = function ( prod ) { i f ( ” prixHT ” in prod && ”tauxTVA” in prod ) { return prod . prixHT *(1.0+ prod . tauxTVA / 1 0 0 . 0 ) ; } else { throw new E r r o r ( ” Mauvais t y p e de paramètre pour l a f o n c t i o n calculPrixTTC ” ); } } document . w r i t e ( ” P r i x TTC du p r o d u i t \” ”+p r o d u i t . denomination+” \” calculPrixTTC ( p r o d u i t ) ) ; </s c r i p t> <p> </body> </html> : ”+ Dans la mesure du possible, il est préférable de définir les objets JavaScript par des littéraux car ça peut être plus efficace que la construction dynamique avec le constructeur Object. 1.5 Tableaux (le type Array) 1.5.1 Notion d’Array et construction Dans les langages classique, un tableau est une séquence d’éléments, contigus en mémoire, avec un accès aux éléments par un indice entier. En JavaScript, les tableaux sont des objets dont les propriétés sont automatiquement nommées avec les chaînes '0', '1', '2'. Ces tableaux possèdent certains avantages des objets, comme par exemple la possibilité d’avoir des éléments de types différents, mais sont significativement plus lents que les tableaux classiques. Un tableau peut être créé par un littéral, entre crochets [ ]. exemples/bases/ex08_arrayLitterals.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>Tableaux</ t i t l e> </head> <body> <p> <s c r i p t> // Dé c l a r a t i o n d ’ una a r r a y s o u s forme de l i t t é r a l var tab = [ 1 , 3 , ” coucou ” , 6 ] ; tab [ 4 ] = 9 ; // a j o u t d ’ un é l é ment fo r ( var i =0 ; i<tab . l e n g t h ; i ++){ document . w r i t e ( tab [ i ]+ ”<br />” ) ; } </s c r i p t> <p> </body> </html> Un tableau peut aussi être créé par le constructeur d’Array. Celui-ci peut prendre en argument soit le nombre de cases du tableau à allouer, soit les éléments du tableau initialisés lors 11 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery de la création du tableau. On peut toujours ajouter des éléments au tableau par une simple affectation de ces éléments et la mémoire évolue automatiquement. exemples/bases/ex09_arraysNew.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>Tableaux</ t i t l e> </head> <body> <p> <s c r i p t> var tab = new Array ( 1 , 3 , ” coucou ” , 6 ) ; tab . push ( 9 ) ; // a j o u t d ’ un é l é ment fo r ( var i =0 ; i<tab . l e n g t h ; i ++){ document . w r i t e ( tab [ i ]+ ”<br />” ) ; } </s c r i p t> <p> </body> </html> De même que pour les objets, il est préférable de définir les tableaux JavaScript par des littéraux car ça peut être plus efficace que la construction dynamique avec le constructeur Array. 1.5.2 Quelques méthodes prédéfinies sur le type Array • Suppression du dernier élément (l’élément supprimé est retourné par la méthode) : function array.pop(); • Suppression du premier élément (l’élément supprimé est retourné par la méthode) : function array.shift(); • Suppression d’une partie des éléments : function array.splice(firstElementKey, numberOfElementsToRemove); • Ajout d’un ou plusieurs élément(s) à la fin : function array.push(element1, element2...); • Tri d’un tableau : function array.sort(compareFuntion); où compareFuntion est une fonction permettant de comparer deux éléments du tableau qui a pour prototype : function compareFuntion(a, b); et renvoie 0 si les éléments sont égaux, un nombre négatif si a est strictement inférieur à b, et un nombre positif si a est strictement supérieur à b. Pour les chaînes de caractère, on peut utiliser la méthode string.localCompare(that) (similaire à strcmp). 12 Chapitre 1 : Premiers pas en JavaScript 1.6 Exemple : traitement d’un formulaire avec jQuery 1.6.1 Qu’est-ce que jQuery ? La librairie jQuery, dont on peut obtenir le code et la documentation sur api.jquery.com, permet de simplifier la gestion de différents aspects d’une application côté client en JavaScript : • Gestion des événements utilisateur ; • Récupération des valeurs saisies dans un formulaire ; • Manipulation du document via le DOM ; • Requêtes asynchrones (transfert de données entre serveur et client en dehors du chargement initial de la page) ; • Codage des données pour la transfert (par exemple JSON ). Pour utiliser jQuery, il suffit d’insérer son code dans un script, via une balise (remplacer x.xx.x par le numéro de version utilisé sur jquery.com) : <script src="https://code.jquery.com/jquery-x.xx.x.js"></script> Pour travailler offline, on peut utiliser jQuery en local après téléchargement dans le répertoire courant : <script src="./jquery-x.xx.x.js"></script> Les méthodes de jQuery peuvent être appelées (avec des argument args) par l’abréviation $(args). 1.6.2 Récupérer, filtrer et afficher les données d’un formulaire Le script suivant récupère les données d’un formulaire, les filtres par expressions régulières, et les affiche en modifiant le DOM. Plus précisément, le script réalise les opération suivantes : • Déclaration d’un gestionnaire (fonction afficheDonneesForm) de l’événement submit du formulaire ayant formStudentData pour ID ; • Dans cette fonction afficheDonneesForm, – Récupération des valeurs saisies dans les éléments ayant nom et annee pour ID, qui sont respectivement un input et un select. – Test sur la forme (expression régulière, champs obligatoire,...) sur les valeurs des champs nom et année du formulaire (à l’aide d’un littéral de type expression régulière entre slashes /.../). – Ajout dans le <span> ayant spanResultat pour ID du code HTML du résultat de la saisie (affichage du nom et de l’année, ou le cas échéant un message d’erreur). – Empêcher le comportement par défaut de la soumission du formulaire (appel du script action côté serveur lors du click sur l’input de type submit). 13 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery Voici le fichier HTML : exemples/bases/ex10_jQueryForm.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>F o r m u l a i r e s avec jQuery</ t i t l e> </head> <body> <form i d=” formStudentData ”> <p> <l a b e l fo r=”nom” >Nom</ l a b e l> <i nput name=”nom” i d=”nom” /> </p> <p> <s e l e c t name=” annee ” i d=” annee ”> <option v a l u e=” c h o i s i s s e z ” s e l e c t e d d i s a b l e d>−− c h o i s i s s e z −−</option> <option v a l u e=” p r e m i e r e ”>Première ann é e</option> <option v a l u e=” deuxième ”>Deuxième ann é e</option> </s e l e c t> </p> <p> <i nput type=” s u b m i t ” v a l u e=” V a l i d e r ” /> </p> </form> <p> <s trong>donn é e s s a i s i e s  ; :</s trong><br /> <!−− Les r é s u l t a t s de l a s a i s i e du f o r m u l a i r e vont s ’ a f f i c h e r dans c e span −−> <span i d=” s p a n R e s u l t a t ”></span> </p> <s c r i p t s r c=” j q u e r y . j s ”></s c r i p t> <s c r i p t s r c=” ex10_jQueryForm . j s ”></s c r i p t> </body> </html> Il est recommandé de mettre, dans la mesure du possible, le script à la fin du document, car cela limite le coût et le délai des chargements et parsing de la librairie jQuery lors d’un premier chargement (ou rafraîchissement) de la page. Voici le fichier JavaScript : exemples/bases/ex10_jQueryForm.js 1 2 3 4 5 6 7 8 9 10 11 // Mé t h o d e de r é cup é r a t i o n e t a f f i c h a g e d e s i n p u t s de f o r m u l a i r e // à l ’ a i d e de jQuery : var a f f i c h e D o n n e e s F o r m = function ( e v e n t ) { // r é cup é r a t i o n ( v i a jQuery ) de l a v a l e u r de l ’ i n p u t d ’ ID ”nom” var nom = $ ( ”#nom” ) . v a l ( ) ; // r é cup é r a t i o n ( v i a jQuery ) de l a v a l e u r du s e l e c t d ’ ID ” annee ” var annee = $ ( ”#annee ” ) . v a l ( ) ; // t e s t de champs o b l i g a t o i r e s e t e x p r e s s i o n s r é g u l i è r e s i f ( annee==” c h o i s i s s e z ” | | ! /^ [ a−zA−ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæ ¸ è é ê ö ì í î ïðñ ò ó ôõö÷øùúûĀāüýþÿ \ s ” ’ −]{1 ,} $/ 14 Chapitre 1 : Premiers pas en JavaScript 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 . t e s t (nom) ) { $ ( ”#s p a n R e s u l t a t ” ) . html ( ” Problème : forme d ’ un champs i n c o r r e c t . ” ) ; } else { $ ( ”#s p a n R e s u l t a t ” ) . html ( ”<em>Nom : </em>” + nom + ”<br />” + ”<em>Annee : </em>” + annee ) ; } // É v i t e r d ’ a p p e l e r l ’” a c t i o n ” par d é f a u t ( ) s c r i p t PHP, e t c . . . ) // du f o r m u l a i r e l o r s du s u b m i t event . preventDefault ( ) ; }; // G e s t i o n de l ’ é v é nement s u b m i t du f o r m u l a i r e . // On d é f i n i t afficheDonneesForm comme g e s t i o n n a i r e ( h a n d l e r ) // de l ’ é v é nement $ ( ”#formStudentData ” ) . on ( ” s u b m i t ” , a f f i c h e D o n n e e s F o r m ) ; 15 Chapitre 2 Programmation Fonctionnelle et Objet en JavaScript On distingue en JavaScript deux catégories de patterns (et éventuellement des patterns hybrides) : • Les patterns dits fonctionnels s’appuient sur les aspects de JavaScript en tant que langage fonctionnel. Autrement dit, ces patterns exploitent les propriétés des fonctions JavaScript en tant que données, ainsi que la portée des variables dans ces fonctions. • Les patterns dits prototypaux s’appuient sur les aspects de JavaScript en tant que langage prototypal. Ceci est lié à une propriété que possèdent tous les objets JavaScript, appelée le prototype. Le prototype permet de partager des propriétés entre plusieurs objets, et il conduit naturellement à des notions d’héritage. Il permet aussi d’augmenter les objets pour leur rajouter des propriétés, bien après que ces objets aient été définis, y compris sur les types de base comme String. Nous commencerons par voir un certain nombre de patterns fonctionnels, qui permettent de faire de la programmation objet avec des notions comme la visibilité, la structuration d’une application en modules (ou packages), des fabriques, ou encore des patterns permettant le découplage des composants d’une application à base d’événements, ou comme subscriber/publisher. Ces patterns peuvent paraître déconcertant au premier abord pour un développeur habitué aux langages objet classiques. Avec un peu d’habitude, on en vient à considérer que JavaScript est un excellent langage objet, très expressif et très souple. Cependant, certains problèmes de conception du langage, qui n’ont pu être corrigés pour assurer la compatibilité ascendante, nécessitent quelques précautions, sous la forme de bonnes habitudes. 2.1 Le Pattern Module Le pattern Module permet de créer des composants qui peuvent jour le rôle que jouent les classes dans les langages objet classiques. Il permet, entre autre, de créer des données et méthodes privées, et une interface publique avec d’autres données et méthodes, qui sont accessibles de l’extérieur, et qui peuvent, elles, accéder aux propriétés privées. Le pattern consiste à créer une fonction. Les données et méthodes privées sont simplement des variables locales de la fonction. Elles ne sont donc pas visibles du monde extérieur à la 16 Chapitre 2 : Programmation Fonctionnelle et Objet en JavaScript fonction. La fonction renvoie un objet, qui constitue l’interface publique du module, dont les propriétés (données, objets ou fonctions) accèdent aux variables privées. Lorsque l’objet est retourné, on ne peut plus accéder directement aux variables locales de la fonction, mais cellesci restent vivantes (leur cycle de vie ne se termine pas) tant que l’objet retourné qui s’y réfère n’est pas lui-même détruit. Autrement dit, on peut continuer à manipuler ces variables locales au travers des méthodes de l’interface publique. exemples/objet/ex01_modulePattern.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 var mySecretModule = function ( d e f a u l t S e c r e t V a l u e ) { // donn é e p r i v é e a v e c une v a l e u r par d é f a u t var m y P r i v a t e S e c r e t = ( ( d e f a u l t S e c r e t V a l u e && typeof d e f a u l t S e c r e t V a l u e === ” s t r i n g ” ) && d e f a u l t S e c r e t V a l u e ) | | ”” ; // Mé t h o d e p r i v é e : var myRegexTestMethod = function ( c h a i n e ) { return ( typeof c h a i n e === ” s t r i n g ” ) && /^ [ a−z ] * $/ i . t e s t ( c h a i n e ) ; }; // On c r é e un o b j e t q u i va ê t r e rendu p u b l i c // Cet o b j e t va ê t r e r e t o u r n é , mais pas l e s donn é e s p r i v é e s . var p u b l i c I n t e r f a c e = { // Les é l é ments p u b l i c s s o n t l e s p r o p r i é t é s de p u b l i c I n t e r f a c e // Donné e p u b l i q u e : donneePublique : ’ donn é e par d é f a u t ’ , // S e t t e r s e t S e c r e t : function ( s e c r e t V a l u e ) { // Test d ’ e x p r e s s i o n r é g u l i è r e i f ( myRegexTestMethod ( s e c r e t V a l u e ) ) { myPrivateSecret = secretValue ; } else { throw { name : ” I l l e g a l A r g u m e n t E x c e p t i o n ” , message : ”Le s e c r e t ” + s e c r e t V a l u e + ” e s t i n v a l i d e . ” }; } }, // A c c e s s e u r : g e t S e c r e t : function ( ) { return m y P r i v a t e S e c r e t ; }, } ; // Fin de p u b l i c I n t e r f a c e return p u b l i c I n t e r f a c e ; } exemples/objet/ex01_modulePattern.html 17 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <!doctype HTML> <html lang=” f r ”> <head> <meta charset=”UTF−8” /> <t i t l e>P a t t e r n ” Module ”</ t i t l e> </head> <body> <p> <s c r i p t src=” . /ex01_modulePattern . j s ”></s c r i p t> <s c r i p t> var s e c r e t M o d u l e = mySecretModule ( ” i n i t S e c r e t ” ) ; document . w r i t e ( ” d o n n e e P u b l i q u e : ” + s e c r e t M o d u l e . donneePublique + ”<br />” ) ; // M o d i f i c a t i o n de l a donn é e p u b l i q u e s e c r e t M o d u l e . donneePublique = ” n o u v e l l e donn é e p u b l i q u e ” ; document . w r i t e ( ” d o n n e e P u b l i q u e : ” + s e c r e t M o d u l e . donneePublique + ”<br />” ) ; // A c c e s s e u r de s e c r e t document . w r i t e ( ” S e c r e t : : ” + s e c r e t M o d u l e . g e t S e c r e t ( ) + ”<br />” ) ; // T e n t a t i v e de m o d i f i e r l e s e c r e t try { s e c r e t M o d u l e . s e t S e c r e t ( ” abcde ” ) ; document . w r i t e ( ” S e c r e t : ” + s e c r e t M o d u l e . g e t S e c r e t ( ) + ”<br />” ) ; } catch ( e ) { document . w r i t e ( ” Erreur de t y p e ” + e . name + ”<br />Message : ” + e . message + ”<br />” ) ; } try { s e c r e t M o d u l e . s e t S e c r e t ( ” abcde567 ” ) ; document . w r i t e ( ” S e c r e t : ” + s e c r e t M o d u l e . g e t S e c r e t ( ) + ”<br />” ) ; } catch ( e ) { document . w r i t e ( ” Erreur de t y p e ” + e . name + ” : ” + e . message + ”<br />” ) ; } </s c r i p t> </p> </body> </html> Lé mécanisme du langage essentiel pour ce pattern est la portée (scope) des variables locales à une fonction, qui s’étend aux sous-fonctions de la fonction, et à leurs sous-fonctions... 18 Chapitre 2 : Programmation Fonctionnelle et Objet en JavaScript 2.2 Passage d’arguments par objets En JavaScript, il est souvent plus pratique, plutôt que de passer une série de paramètres, ce qui oblige à tenir compte de l’ordre de ces paramètres, de donner en argument à une fonction les données dans les propriétés d’un objet, soit construit à la volée, soit construit auparavant. Ce pattern offre souvent plus de souplesse que la manière classique. Dans l’exemple suivant, la fonction génère le code HTML de l’objet passé en paramètre, sans savoir de quel type d’objet il s’agit. On l’utilise ensuite pour afficher une adresse. exemples/objet/ex02_affichageObjetBasic.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 /* * * Cré ee un cha î ne de c a r a c t è r e l i s i b l e q u i r e p r é s e n t e l ’ o b j e t . * On s u p p o s e que t o u t e s l e s p r o p r i é t é s de l ’ o b j e t s o n t de t y p e cha î ne ou nombre . * ( e l l e s p e u v e n t ê t r e automatiquement c o n v e r t i e s en cha î ne ) */ var objectToHtmlTable = function ( s p e c ) { var c h a i n e = ”<table><tbody>” ; // Parcours d e s p r o p r i é t é s de l ’ o b j e t s p e c p a s s é en argument fo r ( propertyName in s p e c ) { // La p r o p r i é t é e s t d é f i n i e e t non v i d e . // E l l e ne v i e n t pas du p r o t o t y p e de l ’ o b j e t // Ce n ’ e s t pas une f o n c t i o n i f ( s p e c [ propertyName ] && s p e c . hasOwnProperty ( propertyName ) && typeof s p e c [ propertyName ] != ” f u n c t i o n ” ) { // Concat é n a t i o n à une cha î ne . Les nombres s o n t c o n v e r t i s . c h a i n e += ’<tr><td s t y l e =”text −align : right ; ”><em>’ + propertyName + ” em></td>” + ”<td>” + s p e c [ propertyName ] + ”</td></ tr>” ; } }; c h a i n e += ”<tbody></table>” ; return c h a i n e ; }; exemples/objet/ex02_affichageObjetBasic.html 1 <!doctype HTML> 19 :</ Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <html lang=” f r ”> <head> <meta charset=”UTF−8” /> <t i t l e>A f f i c h a g e d ’ O b j e t s Géné r i q u e</ t i t l e> </head> <body> <p> <s c r i p t src=” . / e x 0 2 _ a f f i c h a g e O b j e t B a s i c . j s ”></s c r i p t> <s c r i p t> document . w r i t e ( objectToHtmlTable ( { id : ”0 f 3 e a 7 5 9 b 1 ” , numeroRue : ”2 b i s ” , r u e : ”Rue de l a Paix ” , complementAdresse : ” ” , c o d e P o s t a l : ” 63000 ” , v i l l e : ” Clermont−Ferrand ” , pays : ” France ” }) ) ; </s c r i p t> <p> </body> </html> 2.3 Exemple de fabrique sommaire Dans l’exemple suivant, une fabrique, suivant un pattern module très sommaire, construit un objet de type adresse (éventuellement partiellement rempli), en sélectionnant les propriétés que l’objet en paramètre qui sont dans une liste. Cet exemple est plutôt un exemple d’école et nous verrons plus loin des exemple plus complets, les propriété de l’objet retourné manipulant des données (attributs) privées. exemples/objet/ex03_methodLitteralParam.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var f a b r i q u e A d r e s s e V e r s i o n 1 = function ( s p e c ) { // O b j e t à r e t o u r n e r i n i t i a l e m e n t v i d e var a d r e s s e = { } ; var l i s t e P r o p r i e t e s = [ ” i d ” , ”numeroRue” , ” rue ” , ” complementAdresse ” , ” c o d e P o s t a l ” , ” v i l l e ” , ” pays ” ] ; // Parcours d e s p r o p r i é t é s de l ’ o b j e t s p e c p a s s é en argument fo r ( propertyName in s p e c ) { i f ( s p e c . hasOwnProperty ( propertyName ) ) { // S i l a p r o p r i é t é e x i s t e dans l e t y p e a d r e s s e : i f ( l i s t e P r o p r i e t e s . indexOf ( propertyName ) >= 0 ) { a d r e s s e [ propertyName ] = s p e c [ propertyName ] ; } else { throw {name : ” UnknownPropertyException ” , message : ” P r o p r i é t é de l ’ a d r e s s e inconnue . ” } ; } } } 20 Chapitre 2 : Programmation Fonctionnelle et Objet en JavaScript 21 22 return a d r e s s e ; }; exemples/objet/ex03_methodLitteralParam.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>A f f i c h a g e Gén é r i q u e d ’ O b j e t s</ t i t l e> </head> <body> <p> <s c r i p t s r c=” . / e x 0 2 _ a f f i c h a g e O b j e t B a s i c . j s ”></s c r i p t> <s c r i p t s r c=” . / ex03_methodLitteralParam . j s ”></s c r i p t> <s c r i p t> // c r é a t i o n d ’ une i n s t a n c e var a d r e s s e = f a b r i q u e A d r e s s e V e r s i o n 1 ( { i d : ”0 f 3 e a 7 5 9 b 1 ” , numeroRue : ” 2 b i s ” , r u e : ”Rue de l a Paix ” , complementAdresse : ” ” , c o d e P o s t a l : ” 63000 ” , v i l l e : ” Clermont−Ferrand ” , pays : ” France ” }) ; document . w r i t e ( objectToHtmlTable ( a d r e s s e ) ) ; </s c r i p t> <p> </body> </html> 2.4 Structuration d’une application L’un des principaux défauts de JavaScript est sa tendance à créer, parfois sans faire exprès, des variables globales, ce qui a tendance à créer des interactions involontaires entre des parties du code qui n’ont rien à voir, ce qui génère des bugs difficiles à débusquer... Nous allons voir maintenant comment rédiore les nombres de variables globales de notre programme à une seule variable, ici appelée myApp, qui contient toute notre application. L’objet myApp, initialement, ne contient que deux méthodes : • Une méthode addModule qui permet d’ajouter un objet quelconque (de type Objet, Function, Array, etc.) sous la forme de propriété de l’application. • Une méthode init, qui permet de rajouter un ensemble de propriétés prédéfinies, sans avoir à les créer une par une. exemples/objet/ex04_structureApplication.js 1 2 /* * Dé f i n i t i o n d ’ une v a r i a b l e a p p l i c a t i o n . * L ’ a p p l i c a t i o n e s t i n i t i a l e m e n t v i d e e t ne comporte que l a f o n c t i o n n a l i t é 21 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 3 * p e r m e t t a n t d ’ a j o u t e r d e s modules . 4 * Une mé t h o d e i n i t ( ) permet d ’ i n i t i a l i s e r p l u s i e u r s modules . 5 * 6 */ 7 var myApp = { 8 /* * Mé t h o d e q u i a j o u t e un module à n o t r e a p p l i c a t i o n 9 * Un module p e u t ê t r e n ’ i m p o r t e q u e l o b j e t q u i c o n t i e n t 10 * d e s donn é e s ou d e s mé t h o d e s . . . 11 * @method addModule 12 * @param {( O b j e c t | f u n c t i o n | s t r i n g | r e g e x | number ) } m o d u l e O b j e c t 13 * − un o b j e t ou v a l e u r q u e l c o n q u e à a j o u t e r à n o t r e a p p l i c a t i o n . 14 */ 15 addModule : function ( moduleName , moduleObject ) { 16 i f ( typeof moduleName === ” s t r i n g ” && 17 /^ [ a−z ] { 1 , } [ a−z0 −9\_] * $/ i . t e s t ( moduleName ) ) { 18 t h i s [ moduleName ] = moduleObject ; 19 } else { 20 throw { 21 name : ” I l l a g e a l A r g u m e n t E x c e p t i o n ” , 22 message : ” I m p o s s i b l e de c r é e r l e s module : nom ” + moduleName 23 + ” i l l é gal ” 24 } 25 } 26 }, 27 28 /* * A j o u t e t o u t e s l e s p r o p r i é t é s d ’ un o b j e t à n o t r e a p p l i c a t i o n . 29 * @method i n i t 30 * @param { O b j e c t } s p e c − o b j e t c o n t e n a n t l e s p r o p r i é t é s à a j o u t e r . 31 */ 32 i n i t : function ( s p e c ) { 33 f o r ( propertyName in s p e c ) { 34 i f ( s p e c . hasOwnProperty ( propertyName ) ) { 35 t h i s . addModule ( propertyName , s p e c [ propertyName ] ) ; 36 } 37 } 38 } 39 } ; 40 41 // I n i t i a l i s a t i o n de l ’ a p p l i c a t i o n a v e c un module m e t i e r 42 // m e t i e r e s t i n t ) i t i a l e m e n t v i d e . 43 myApp . i n i t ( { 44 m e t i e r : {} 45 } ) ; Nous utilisons maintenant ce squelette d’application et nous créons dans notre application un module metier. Nous utilisons ensuite le pattern apply qui nous permet d’utiliser la méthode myApp.addModule en prenant comme ”this” un autre objet que myApp. En appliquant donc la méthode myApp.addModule en prenant myApp.metier comme ”this”, nous créons un sous-module de myApp.metier, appelé myApp.metier.sousModule. Ce sousmodule contient une propriété essai. exemples/objet/ex04_structureApplication.html 1 <!doctype HTML> 2 <html l a n g=” f r ”> 22 Chapitre 2 : Programmation Fonctionnelle et Objet en JavaScript 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>C l a s s e s</ t i t l e> <s ty l e> t a b l e {border −collapse : c o l l a p s e } tbody t r td , th {border −style : s o l i d ; text −align : c e n t e r ; padding : 4 pt ; } </s ty l e> </head> <body> <p> <s c r i p t s r c=” e x 0 4 _ s t r u c t u r e A p p l i c a t i o n . j s ”></s c r i p t> <s c r i p t> // a j o u t d ’ une p r o p r i é t é au mé t i e r : myApp . m e t i e r . coucou = ” t e s t ” ; // Ajout d ’ un sous−module au module myApp . m e t i e r // On a p p l i q u e ( p a t t e r n a p p l y ) addModule en p r e n a n t t h i s = myApp . m e t i e r myApp . addModule . apply (myApp . m e t i e r , [ ” sousModule ” , { e s s a i : ” Je s u i s l a p r o p r i é t é \” e s s a i \” du s o u s module ” } ]) ; // Ajout d ’ une mé t h o d e mainFunction myApp . addModule ( ” mainFunction ” , function ( ) { document . w r i t e ( ” Fo ncti on myApp . mainFunction :<br />” ) ; document . w r i t e ( ”myApp . m e t i e r . coucou : ” + myApp . m e t i e r . coucou + ”<br />” ) ; document . w r i t e ( ”myApp . m e t i e r . sousModule . e s s a i : ” + myApp . m e t i e r . sousModule . essai ) ; }) ; // Exé c u t i o n de l a mé t h o d e mainFunction myApp . mainFunction ( ) ; </s c r i p t> <p> </body> </html> 2.5 Exemple : un module metier.regexUtil L’exemple suivant montre l’utilisation du pattern Module pour créer un sous-module métier utilitaire pour tester des expressions régulières courantes : • Expressions formées avec les caractères du langage courant dans une des langues dont les accents sont normalisés dans la norme ISO 8859 − 1 (Latin-1, Europe occidentale), admettant aussi les guillemets, apostrophes et traits d’union (tiret haut). • Mêmes caractère que la précédente mais admettant en outre les chiffres. • Mêmes caractère que la précédente mais admettant en outre les caractères de ponctuation ( ; . , ! ? :) et les parenthses. Trois expressions régulières constances (donc pré-compilées) sont définies comme données statiques (en un seul exemplaire) privées. L’interface fournit trois méthodes pour tester ces 23 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery expressions régulière sur une chaîne, avec éventuellement des conditions de longueur minimale ou maximale sur la chaîne (exemple : champs obligatoire...). exemples/objet/ex05_modulePatternRegex.js 1 /* * 2 * A j o u t e au mé t i e r un o b j e t q u i e s t l ’ i n t e r f a c e p u b l i q u e 3 * d ’ une f o n c t i o n q u i s u i t l e ” p a t t e r n module ” . 4 * La f o n c t i o n r e t o u r n e son i n t e r f a c e p u b l i q u e q u i e s t un o b j e t . 5 * Cet o b j e t e s t a j o u t é comme sous−module au module ” m e t i e r ” . 6 * 7 * @module r e g e x U t i l 8 * @augments myApp . m e t i e r 9 */ 10 myApp . addModule . apply (myApp . m e t i e r , [ ” r e g e x U t i l ” , function ( ) { 11 12 // ////////////////////////////////////////////// 13 // P r o p r i é t é s e t mé t h o d e s ” s t a t i q u e s ” p r i v é e s 14 15 /* * 16 * E x p r e s s i o n r é g u l i è r e c o n s t a n t e pour l a l a n g u e n a t u r e l l e ( e t e s p a c e s ) 17 * @constant 18 * @private 19 */ 20 var r e g e x L a t i n 1 21 = /^ [ a−zA−ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæ ¸ è é ê ö ì í î ïð ñ òó ôõö÷øùúû Āāüýþÿ \ s ” ’\ −]* $/ i ; 22 23 /* * 24 * E x p r e s s i o n r é g u l i è r e c o n s t a n t e pour l a l a n g u e n a t u r e l l e e t c h i f f r e s 25 * @constant 26 * @private 27 */ 28 var r e g e x L a t i n 1 W i t h D i g i t s = 29 /^ [ a−zA−ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæ ¸ è é ê ö ì í î ïð ñ òó ôõö÷øùúû Āāüýþÿ \ s ” ’\ −0 −9]* $/ i ; 30 31 /* * 32 * E x p r e s s i o n r é g u l i è r e c o n s t a n t e pour l a l a n g u e n a t u r e l l e e t c h i f f r e s ou ponctuation 33 * @constant 34 * @private 35 */ 36 var r e g e x L a t i n 1 W i t h D i g i t s P u n c t u a t i o n = 37 /^ [ a−zA−ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæ ¸ è é ê ö ì í î ïð ñ òó ôõö÷øùúû Āāüýþÿ \ s ” ’\ −0 −9\ ;\.\ ,\ !\ ?\ :\(\) ] * $/ i ; 38 39 /* * 40 * V a l i d e une e x p r e s s i o n r é g u l i è r e s u r une c h a i n e ( a v e c c o n d i t i o n s de longueur ) 41 * @method v a l i d a t e R e g e x 42 * @param { O b j e c t } s p e c − o b j e t c o n t e n a n t l e s donn é e s du t e s t à e f f e c t u e r 43 * @param { f u n c t i o n } s p e c . r e g e x T e s t − f o n c t i o n de t e s t q u i r e n v o i e t r u e en c a s de s u c c è s 44 e t un message d ’ e r r e u r en c a s d ’ é c h e c 45 * @param { s t r i n g } s p e c . c h a i n e − cha î ne de c a r a c t è r e s à t e s t e r 46 * @param {number} [ minLength =0] − l o n g u e u r minimale pour l a cha î ne 24 Chapitre 2 : Programmation Fonctionnelle et Objet en JavaScript 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 * @param {number} [ maxLength ] − l o n g u e u r maximale pour l a cha î ne ( d é f a u t : illimit é) * @return { b o o l e a n } t r u e s i l e s c o n d i s i o n s s o n t s a t i s f a i t e s , f a l s e s i n o n . */ var v a l i d a t e R e g e x = function ( s p e c ) { i f ( typeof s p e c . c h a i n e === ” s t r i n g ” && ( ! s p e c . minLength | | s p e c . c h a i n e . l e n g t h >= s p e c . minLength ) && ( ! s p e c . maxLength | | s p e c . c h a i n e . l e n g t h <= s p e c . maxLength ) ){ return s p e c . r e g e x . t e s t ( s p e c . c h a i n e ) ; } return f a l s e ; }; // ////////////////////////////////////////////// // I n t e r f a c e p u b l i q u e du module /* * * Cré a t i o n d ’ un o b j e t c o n t e n a n t l e s donn é e s e t mé t h o d e s p u b l i q u e s * ( l e s p r o p r i é t é s p u b l i q u e s s o n t r e t o u r n é e s par l a f o n c t i o n ” module ” ) . */ var p u b l i c I n t e r f a c e R e g e x = { /* * * mé t h o d e p u b l i q u e : t e s t d ’ e x p r e s s i o n du l a n g a g e a v e c c h i f f r e s * @method t e s t R e g e x L a t i n 1 * @param { O b j e c t } s p e c − o b j e t c o n t e n a n t l e s donn é e s du t e s t à e f f e c t u e r * @param { s t r i n g } s p e c . c h a i n e − cha î ne de c a r a c t è r e s à t e s t e r * @param {number} [ minLength =0] − l o n g u e u r minimale pour l a cha î ne * @param {number} [ maxLength ] − l o n g u e u r maximale pour l a cha î ne * @return { b o o l e a n | s t r i n g } t r u e s i l e s c o n d i s i o n s s o n t s a t i s f a i t e s , un message d ’ e r r e u r s i n o n . */ t e s t R e g e x L a t i n 1 : function ( s p e c ) { // Ajout d ’ une p r o p r i é t é à s p e c ( a ug me n ta t i on ) spec . regex = regexLatin1 ; return v a l i d a t e R e g e x ( s p e c ) ; }, /* * * mé t h o d e p u b l i q u e : t e s t d ’ e x p r e s s i o n du l a n g a g e a v e c c h i f f r e s * @method t e s t R e g e x L a t i n 1 W i t h D i g i t s * @param { O b j e c t } s p e c − o b j e t c o n t e n a n t l e s donn é e s du t e s t à e f f e c t u e r * @param { s t r i n g } s p e c . c h a i n e − cha î ne de c a r a c t è r e s à t e s t e r * @param {number} [ minLength =0] − l o n g u e u r minimale pour l a cha î ne * @param {number} [ maxLength ] − l o n g u e u r maximale pour l a cha î ne * @return { b o o l e a n | s t r i n g } t r u e s i l e s c o n d i s i o n s s o n t s a t i s f a i t e s , un message d ’ e r r e u r s i n o n . */ t e s t R e g e x L a t i n 1 W i t h D i g i t s : function ( s p e c ) { // Ajout d ’ une p r o p r i é t é à s p e c ( a ug me n ta t i on ) spec . regex = regexLatin1WithDigits ; return v a l i d a t e R e g e x ( s p e c ) ; }, 25 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 /* * * mé t h o d e p u b l i q u e : t e s t d ’ e x p r e s s i o n du l a n g a g e a v e c c h i f f r e s * @method t e s t R e g e x L a t i n 1 W i t h D i g i t s P u n c t u a t i o n * @param { O b j e c t } s p e c − o b j e t c o n t e n a n t l e s donn é e s du t e s t à e f f e c t u e r * @param { s t r i n g } s p e c . c h a i n e − cha î ne de c a r a c t è r e s à t e s t e r * @param {number} [ minLength =0] − l o n g u e u r minimale pour l a cha î ne * @param {number} [ maxLength ] − l o n g u e u r maximale pour l a cha î ne * @return { b o o l e a n | s t r i n g } t r u e s i l e s c o n d i s i o n s s o n t s a t i s f a i t e s , un message d ’ e r r e u r s i n o n . */ t e s t R e g e x L a t i n 1 W i t h D i g i t s P u n c t u a t i o n : function ( s p e c ) { // Ajout d ’ une p r o p r i é t é à s p e c ( a ug me n ta t i on ) spec . regex = regexLatin1WithDigitsPunctuation ; return v a l i d a t e R e g e x ( s p e c ) ; } } ; // f i n de p u b l i c I n t e r f a c e R e g e x // On r e t o u r n e l ’ o b j e t c o n t e n a n t l ’ i n t e r f a c e p u b l i q u e . return p u b l i c I n t e r f a c e R e g e x ; } ( ) ] // f i n ET APPEL de l a f o n c t i o n q u i c r é e l ’ o b j e t ” r e g e x U t i l ” ) ; // f i n de l ’ a p p e l ” a p p l y ” de l a mé t h o d e myApp . addModule // ( a j o u t de l ’ o b j e t p u b l i c I n t e r f a c e R e g e x au m e t i e r , s o u s l e nom r e g e x U t i l ) Le fichier HTML réalise des tests des méthodes du module regexUtil sur un jeu de chaînes, et affiche les résultats dans une table. exemples/objet/ex05_modulePatternRegex.html 1 2 3 4 5 6 7 8 9 10 <!doctype HTML> <html lang=” f r ”> <head> <meta charset=”UTF−8” /> <t i t l e>Module Regex</ t i t l e> <l i nk rel=” s t y l e s h e e t ” , href=” ex05_modulePatternRegex . c s s ” /> </head> <body> <!−− Cré a t i o n de l ’ a p p l i c a t i o n v i d e a v e c deux mé t h o d e s −−> <s c r i p t src=” e x 0 4 _ s t r u c t u r e A p p l i c a t i o n . j s ”></s c r i p t> 26 Chapitre 2 : Programmation Fonctionnelle et Objet en JavaScript 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <!−− Cré a t i o n de sous−module r e g e x U t i l de myApp . m e t i e r −−> <s c r i p t src=” . /ex05_modulePatternRegex . j s ”></s c r i p t> <!−− Ajout d ’ un main e t ex é c u t i o n −−> <s c r i p t> // Ajout d ’ une mé t h o d e mainFunction myApp . addModule ( ” mainFunction ” , function ( ) { // Cha î nes pour l e s t e s t s d ’ e x p r e s s i o n s r é g u l i è r e s : var t a b C h a i n es = [ ”L ’ é norme Bla−bl à” , ” B l a b l a 2 ” , ” B l a b l a#” , ” a u t r e b l a b l a : b l i ( bleu )” ]; var i ; // R a cc o u rc i par c o p i e de r é f é r e n c e : var r e g e x U t i l = myApp . m e t i e r . r e g e x U t i l ; var t e x t e = ”<table><thead>” + ”<tr><th></th><th>L a t i n 1</th><th>L a t i n 1 w i t h D i g i t s</th>” + ”<th>L a t i n 1 w i t h D i g i t s<br />and p un c t</th></ tr></thead><tbody>” ; fo r ( i = 0 ; i< tabC ha in es . l e n g t h ; i ++){ t e x t e += ”<tr><td>” + t ab C h a i n e s [ i ] + ”</td>” + ”<td>” + r e g e x U t i l . t e s t R e g e x L a t i n 1 ( { chaine : tabChaines [ i ] } ) + ”</td>” + ”<td>” + r e g e x U t i l . t e s t R e g e x L a t i n 1 W i t h D i g i t s ( { chaine : tabChaines [ i ] } ) + ”</td>” + ”<td>” + r e g e x U t i l . t e s t R e g e x L a t i n 1 W i t h D i g i t s P u n c t u a t i o n ( { chaine : tabChaines [ i ] } ) + ”</td>” } t e x t e += ”</tbody></table>” ; 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 document . w r i t e ( t e x t e ) ; 41 } ) ; // f i n de myApp . addModule (” mainFunction ” 42 43 // Lancement de l ’ a p p l i c a t i o n : 44 myApp . mainFunction ( ) ; 45 </s c r i p t> 46 </body> 47 </html> 2.6 Module Métier adresse Nous créons maintenant un sous-module myApp.metier.adresse. Celui-ci comporte une partie privée, avec notamment une donnée membre statique propertiesPatterns, qui définit la structure d’une adresse. Ici, propertiesPatterns définit, pour chaque propriété, une méthode de test par expression régulière de validité de la valeur, et une propriété labelText à afficher pour indiquer à l’utilisateur de quelle propriété il s’agit (typiquement : texte de label associé à un input dans un formulaire). On pourrait facilement adapter le code pour permettre des propriétés calculées. L’interface propose quelques méthodes statiques utilitaires, comme l’accès à la liste des noms de propriétés, aux sonnées labelText, ou le test d’expression régulière d’une propriété. exemples/objet/ex06_moduleMetierAdresse.js 27 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 /* * * Dé f i n i t l e s p r o p r i é t é s g éné r a l e d e s o b j e t s mé t i e r s r e p r é s e n t a n t d e s a d r e s s e s . * On a j o u t e au mé t i e r un o b j e t q u i e s t l ’ i n t e r f a c e p u b l i q u e d ’ une f o n c t i o n q u i s u i t l e p a t t e r n ” module ” . * La f o n c t i o n r e t o u r n e son i n t e r f a c e p u b l i q u e q u i e s t un o b j e t . * Cet o b j e t e s t a j o u t é comme sous−module au module ” m e t i e r ” . * * Dans c e t o b j e t , on ne t r o u v e pas pour l e moment l e s p r o p r i é t é s d ’ i n s t a n c e . C e l l e s −c i s e r o n t a j o u t é e s par ” aug men tat io n ” . * * @module a d r e s s e * @augments myApp . m e t i e r */ myApp . addModule . apply (myApp . m e t i e r , [ ” a d r e s s e ” , function ( ) { // R a cc ou rc i ( a l i a s ) v e r s l e module de r e g e x var r e g e x U t i l = myApp . m e t i e r . r e g e x U t i l ; // ////////////////////////////////////////////// // P r o p r i é t é s e t mé t h o d e s ” s t a t i q u e s ” p r i v é e s /* * * * * * * * * * * * Dé f i n i t l a s t r u c t u r e d e s o b j e t s de t y p e a d r e s s e p r o p r i é t é s a t t e n d u e s , forme de c e s donn é e s . . . : @constant @private @property { O b j e c t } i d − P r o p r i é t é s de l ’ i d e n t i f i a n t u n i q u e de l ’ i n s t a n c e @property { O b j e c t } numé roRue − P r o p r i é t é s du numé ro de l a rue @property { O b j e c t } rue − P r o p r i é t é s du nom de l a rue / p l a c e @property { O b j e c t } complementAdresse − P r o p r i é t é s du compl é ment Lieu d i t / Bâ t i m e n t . . . * @property { O b j e c t } c o d e P o s t a l − P r o p r i é t é s du code p o s t a l * @property { O b j e c t } v i l l e − P r o p r i é t é s du nom de l a v i l l e * @property { O b j e c t } numé roRue − P r o p r i é t é s du nom du pays */ var p r o p e r t i e s P a t t e r n s = { id : { r e g e x T e s t : function ( c h a i n e ) { i f ( /^[0−9a−f ] { 1 0 } $/ i . t e s t ( c h a i n e ) === true ) { return true ; } else { return ”L ’ i d e n t i f i a n t d o i t comporter 10 c h i f f r e s hexa . ” ; } }, labelText : ” I d e n t i f i a n t ” }, numeroRue : { r e g e x T e s t : function ( c h a i n e ) { i f ( r e g e x U t i l . testRegexLatin1WithDigits ({ chaine : chaine , maxLength : 15 } ) === true ) { return true ; 28 Chapitre 2 : Programmation Fonctionnelle et Objet en JavaScript 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 } else { return ”Le numé ro de l a rue c o n t i e n t au p l u s 15 c a r a c t è r e s , ” +” l e t t r e s , t i r e t s e t g u i l l e m e t s ou c h i f f r e s . ” ; } }, l a b e l T e x t : ”Numé ro ” }, rue : { r e g e x T e s t : function ( c h a i n e ) { i f ( r e g e x U t i l . testRegexLatin1WithDigits ({ chaine : chaine , minLength : 1 , maxLength : 255 } ) === true ) { return true ; } else { return ” l e nom de l a rue / p l a c e , o b l i g a t o i r e ne c o n t i e n t que ” + ” d e s l e t t r e s , t i r e t s e t g u i l l e m e t s ou c h i f f r e s . ” ; } }, l a b e l T e x t : ” rue / p l a c e ” }, complementAdresse : { r e g e x T e s t : function ( c h a i n e ) { i f ( r e g e x U t i l . testRegexLatin1WithDigitsPunctuation ({ chaine : chaine , maxLength : 255 } ) === true ) { return true ; } else { return ” l e compl é ment d ’ a d r e s s e ne c o n t i e n t que d e s l e t t r e s , ” + ” t i r e t s e t g u i l l e m e t s ou c h i f f r e s . ” ; } }, l a b e l T e x t : ” Lieu d i t , Bâ timent , BP” }, codePostal : { r e g e x T e s t : function ( c h a i n e ) { i f ( /^ [0 −9]{5} $/ . t e s t ( c h a i n e ) === true ) { return true ; } else { return ”Le code p o s t a l d o i t comporter 5 c h i f f r e s d é cimaux . ” ; } }, l a b e l T e x t : ”Code P o s t a l ” }, ville : { r e g e x T e s t : function ( c h a i n e ) { i f ( r e g e x U t i l . testRegexLatin1 ({ chaine : chaine , minLength : 1 , maxLength : 255 } ) === true ) { 29 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 return true ; } else { return ”La v i l l e , o b l i g a t o i r e , ne c o n t i e n t que d e s l e t t r e s , ” + ” t i r e t s et guillemets . ” ; } }, labelText : ” Ville ” }, pays : { r e g e x T e s t : function ( c h a i n e ) { i f ( r e g e x U t i l . testRegexLatin1 ({ chaine : chaine , minLength : 1 , maxLength : 255 } ) === true ) { return true ; } else { return ”Le pays , o b l i g a t o i r e , ne c o n t i e n t que d e s l e t t r e s , ” + ” t i r e t s et guillemets . ” ; } }, l a b e l T e x t : ” Pays ” } } ; // f i n de l ’ o b j e t p r o p e r t i e s P a t t e r n s /* * * Tableau c o n t e n a n t l a l i s t e d e s p r o p r i é t é s a t t e n d u e s d ’ une i n s t a n c e d ’ a d r e s s e . Le t a b l e a u e s t pr é c a l c u l é l o r s de l ’ i n i t i a l i s a t i o n . * @member * @private */ var p r o p e r t y L i s t = function ( ) { var l i s t e = [ ] ; // Parcours d e s p r o p r i é t é s de l ’ o b j e t p r o p e r t i e s P a t t e r n s . r e g e x T e s t // q u i c o r r e s p o n d e n t aux p r o p r i é t é s de l ’ a d r e s s e fo r ( var propertyName in p r o p e r t i e s P a t t e r n s ) { // Ne p a s c o n s i d é r e r l e s p r o p r i é t é s ” h é r i t é e s ” du p r o t o t y p e . i f ( p r o p e r t i e s P a t t e r n s . hasOwnProperty ( propertyName ) ) { l i s t e . push ( propertyName ) ; } } return l i s t e ; } ( ) ; // a p p e l immé d i a t de l a f o n c t i o n anonyme . // ////////////////////////////////////////////// // I n t e r f a c e p u b l i q u e du module /* * * Cré a t i o n d ’ un o b j e t c o n t e n a n t l e s donn é e s e t mé t h o d e s p u b l i q u e s * ( l e s p r o p r i é t é s p u b l i q u e s s o n t r e t o u r n é e s par l a f o n c t i o n ” module ” ) . */ var p u b l i c I n t e r f a c e A d r e s s e = { 30 Chapitre 2 : Programmation Fonctionnelle et Objet en JavaScript 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 /* * * Renvoie l a l i s t e d e s p r o p r i é t é s a t t e n d u e s d e s i n s t a n c e s d ’ a d r e s s e . * @method g e t P r o p e r t y L i s t */ g e t P r o p e r t y L i s t : function ( ) { return p r o p e r t y L i s t ; }, /* * * Renvoie l e t e x t e de d e s c r i p t i o n de l a p r o p r i é t é a t t e n d u e d e s i n s t a n c e s d ’ adresse . * Renvoie u n d e f i n e d en c a s d ’ e r r e u r ( p r o p r i é t é inconnue ) * @method g e t L a b e l T e x t * @param { s t r i n g } propertyName − nom de p r o p r i é t é * @return { s t r i n g } l e t e x t e de d e s c r i p t i o n c o u r t e du champs */ g e t L a b e l T e x t : function ( propertyName ) { return p r o p e r t i e s P a t t e r n s [ propertyName ] . l a b e l T e x t ; }, /* * * Expose l e t e s t d ’ e x p r e s s i o n r é g u l i è r e d e s p r o p r i é t é s a t t e n d u e s d e s instances d ’ adresse . * Peut ê t r e u t i l i s é e pour l e f i l t r a g e d e s donn é e s d ’ un f o r m u l a i r e . * @method t e s t R e g e x * @param { s t r i n g } propertyName − nom de p r o p r i é t é * @param { s t r i n g } v a l u e − v a l e u r pour i n i t i a l i s e r l a p r o p r i é t é * @return { b o o l e a n | s t r i n g } t r u e s i l a c h a i n e e s t un code p o s t a l v a l i d e , un message d ’ e r r e u r s i n o n . */ t e s t R e g e x : function ( propertyName , v a l u e ) { i f ( p r o p e r t i e s P a t t e r n s [ propertyName ] === u n d e f i n e d ) { return ”La p r o p r i é t é ” + propertyName + ” n ’ e x i s t e pas ” ; } else { return p r o p e r t i e s P a t t e r n s [ propertyName ] . r e g e x T e s t ( v a l u e ) ; } } } ; // f i n de l ’ o b j e t p u b l i c I n t e r f a c e A d r e s s e return p u b l i c I n t e r f a c e A d r e s s e ; } ( ) ] // f i n ET APPEL de l a f o n c t i o n q u i c r é e l ’ o b j e t ” p u b l i c I n t e r f a c e A d r e s s e ” ) ; // f i n de l ’ a p p e l ” a p p l y ” de l a mé t h o d e myApp . addModule // ( a j o u t de l ’ o b j e t p u b l i c I n t e r f a c e A d r e s s e au me t i e r , s o u s l e nom a d r e s s e ) Nous créons ensuite, via un pattern Module, une fabrique d’instances d’adresse. Celle-ci prend comme paramètre un objet contenant des valeurs pour initialiser les propriétés, effectue les tests d’expressions régulières, et crée deux objets privés. Le premier objet, appelé adresse, contient les propriétés de l’objet adresse. Le deuxième objet, appelé dataError, contient, pour chaque propriété pour laquelle une erreur a été détectée au filtrage, un message d’erreur. Des méthodes publiques, dans l’interface du module, permettent d’accéder à, ou de modifier 31 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery les données de l’instance. exemples/objet/ex06_fabriqueAdresse.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 /* * * Fabrique qui cr é e des o b j e t s repr é sentant des adresse , suiva nt l e ” pattern module ” . * Le paramètre s p e c de n o t r e f o n c t i o n e s t un o b j e t c o n t e n a n t l e s p r o p r i é t é s d ’ une a d r e s s e à c r é e r . * * @method c r e a t e I n s t a n c e * @augments myApp . m e t i e r . a d r e s s e * @param { O b j e c t } i n p u t O b j − sp é c i f i c a t i o n d e s p r o p r i é t é s d ’ une i n s t a n c e d ’ adresse * @param { s t r i n g } i n p u t O b j . i d − i d e n t i f i a n t u n i q u e de l ’ i n s t a n c e * @param { s t r i n g } i n p u t O b j . numeroRue − numero de rue * @param { s t r i n g } i n p u t O b j . rue − nom de rue * @param { s t r i n g } i n p u t O b j . complementAdresse − compl é ment d ’ a d r e s s e ( l i e u dut , b â timent , r é s i d e n c e , e t c . ) * @param { s t r i n g } i n p u t O b j . c o d e P o s t a l − code p o s t a l * @param { s t r i n g } i n p u t O b j . v i l l e − nom de v i l l e * @param { s t r i n g } i n p u t O b j . pays − nom de pays */ myApp . addModule . apply (myApp . m e t i e r . a d r e s s e , [ ” c r e a t e I n s t a n c e ” , function ( inputObj ){ // ////////////////////////////////////////////// // P r o p r i é t é s e t mé t h o d e s ” s t a t i q u e s ” p r i v é e s /* * * O b j e t p r i v é c o n t e n a n t l e s p r o p r i é t é s de l ’ i n s t a n c e , i n i t i a l e m e n t v i d e * @member * @private */ var a d r e s s e = { } ; /* * * O b j e t p r i v é c o n t e n a n t l e s messages d ’ e r r e u r a s s o c i é s aux p r o p r i é t é s attendues des i n s t a n c e s . * @member * @private */ var d a t a E r r o r = f a l s e ; /* * * A j o u t e une p r o p r i é t é ( message d ’ e r r e u r ) dans d a t a E r r o r * @method addError * @private */ var addError = function ( propertyName , message ) { // s i d a t a E r r o r n ’ e x i s t e pas , on l e c r é e i f ( d a t a E r r o r === f a l s e ) { dataError = {} ; } // Ajout d ’ une p r o p r i é t é d a t a E r r o r [ propertyName ] = message ; } 32 Chapitre 2 : Programmation Fonctionnelle et Objet en JavaScript 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 /* * * S e t t e r : i n i t i a l i s e l a v a l e u r pour une p r o p r i é t é a t t e n d u e s d ’ une i n s t a n c e . * En c a s d ’ e r r e u r un message pour c e t t e p r o p r i é t é e s t e s t a j o u t é dans dataError . * En l ’ a b s e n c e d ’ e r r e u r , une é v e n t u e l l e e r r e u r pr é c é d e n t e e s t e f f a c é e . * @method addError * @private */ var s e t P r o p e r t y O r E r r o r = function ( propertyName , v a l u e ) { var r e s u l t T e s t R e g e x = myApp . m e t i e r . a d r e s s e . t e s t R e g e x ( propertyName , v a l u e ) ; // On i n t i a l i s e l a p r o p r i é t é de l ’ a d r e s s e a d r e s s e [ propertyName ] = v a l u e ; // S i l a v a l i d a t i o n par e x p r e s s i o n r é g u l i è r e e s t p a s s é e i f ( r e s u l t T e s t R e g e x === true ) { // On e f f a c e une v i e i l l e e r r e u r é v e n t u e l l e delete d a t a E r r o r [ propertyName ] ; } else { // On i n i t a l i s e l a p r o p r i é t é de l ’ o b j e t d e s e r r e u r s . // a v e c l e message d ’ e r r e u r . addError ( propertyName , ” P r o p r i é t é ” + v a l u e + ” i n v a l i d e : ” + resultTestRegex ) ; } } // Parcours d e s p r o p r i é t é s de g e t P r o p e r t y L i s t ( ) // q u i c o r r e s p o n d e n t aux p r o p r i é t é s de l ’ a d r e s s e à c r é e r fo r ( var i =0 ; i<th i s . g e t P r o p e r t y L i s t ( ) . l e n g t h ; ++i ) { var propertyName = t h i s . g e t P r o p e r t y L i s t ( ) [ i ] ; s e t P r o p e r t y O r E r r o r ( propertyName , inputObj [ propertyName ] ) ; } // ////////////////////////////////////////////// // I n t e r f a c e p u b l i q u e du module /* * * Cré a t i o n d ’ un o b j e t c o n t e n a n t l e s donn é e s e t mé t h o d e s p u b l i q u e s * ( l e s p r o p r i é t é s p u b l i q u e s s o n t r e t o u r n é e s par l a f o n c t i o n ” module ” ) . */ var p u b l i c I n t e r f a c e I n s t a n c e = { /* * * Retourne l e module a v e c l e s mé t h o d e s ” s t a t i q u e s ” * (comme l ’ a c c è s d i r e c t à l a l i s t e d e s p r o p r i é t é s ou l e s t e s t s r e g e x ) * @return { O b j e c t } l e module myApp . m e t i e r . a d r e s s e */ getModule : function ( ) { return myApp . m e t i e r . a d r e s s e ; }, /* * * A c c e s s e u r pour t o u s l e s membres p r i v é s d ’ i n s t a n c e . * @param { s t r i n g } propertyName − nom de l a p r o p r i é t é a t t e n d u e d ’ une instance * @return { s t r i n g } l a v a l e u r de l a p r o p r i é t é ou u n d e f i n e d en c a s de nom de p r o p r i é t é inconnu . */ 33 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 g e t P r o p e r t y : function ( propertyName ) { return a d r e s s e [ propertyName ] ; }, /* * * S e t t e r : i n i t i a l i s e l a v a l e u r pour une p r o p r i é t é a t t e n d u e s d ’ une i n s t a n c e a p r è s un t e s t . * En c a s d ’ e r r e u r , un message pour c e t t e p r o p r i é t é e s t e s t a j o u t é dans dataError . * En l ’ a b s e n c e d ’ e r r e u r , une é v e n t u e l l e e r r e u r pr é c é d e n t e e s t e f f a c é e . * @param { s t r i n g } propertyName − nom de l a p r o p r i é t é a t t e n d u e d ’ une instance * @param { s t r i n g } v a l u e − v a l e u r à p r e n d r e pour l a p r o p r i é t é a t t e n d u d ’ une instance * @return { b o o l e a n } t r u e s ’ i l y a au moins une e r r e u r , f a l s e s i n o n */ setProperty : setPropertyOrError , /* * * @return { b o o l e a n } t r u e s ’ i l y a ( au moins ) une e r r e u r , f a l s e s i n o n */ h a s E r r o r : function ( ) { i f ( d a t a E r r o r === f a l s e ) { return f a l s e ; } fo r ( var propertyName in d a t a E r r o r ) { i f ( d a t a E r r o r . hasOwnProperty ( propertyName ) ) { return true ; } } return f a l s e ; }, /* * * Donne l ’ a c c è s aux d i f f é r e n t s messages d ’ e r r e u r . * @param { s t r i n g } propertyName − nom de p r o p r i é t é d ’ une i n s t a n c e d ’ a d r e s s e * @return { s t r i n g | u n d e f i n e d } l e message d ’ e r r e u r pour une p r o p r i é t é s ’ i l e x i s t e ou u n d e f i n e d en l ’ a b s e n c e d ’ e r r e u r */ g e t E r r o r M e s s a g e : function ( propertyName ) { return d a t a E r r o r [ propertyName ] ; }, /* * * Ré c u p è r e l a l i s t e d e s champs q u i ont une e r r e u r * @return { s t r i n g [ ] } t a b l e a u d e s noms de p r o r p i é t é s q u i comportent une erreur . */ g e t E r r o r L i s t : function ( ) { var e r r o r L i s t = [ ] ; fo r ( var propertyName in d a t a E r r o r ) { i f ( d a t a E r r o r . hasOwnProperty ( propertyName ) ) { e r r o r L i s t . push ( propertyName ) ; } } return e r r o r L i s t ; 34 Chapitre 2 : Programmation Fonctionnelle et Objet en JavaScript 151 152 153 154 155 156 157 158 } } ; // f i n de p u b l i c I n t e r f a c e I n s t a n c e return p u b l i c I n t e r f a c e I n s t a n c e ; } // f i n de l a mé t h o d e c r e a t e I n s t a n c e ] ) ; // f i n de l ’ a p p e l ” a p p l y ” de l a mé t h o d e myApp . addModule Le fichier HTML réalise le test de création d’une instance et d’utilisation de setters, et affiche les données et les erreurs obtenues. exemples/objet/ex06_moduleMetierAdresse.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <!doctype HTML> <html lang=” f r ”> <head> <meta charset=”UTF−8” /> <t i t l e>Cr é a t i o n d ’ I n s t a n c e s d ’ A d r e s s e s</ t i t l e> <l i nk rel=” s t y l e s h e e t ” , href=” ex06_moduleMetierAdresse . c s s ” /> </head> <body> <!−− Cré a t i o n de l ’ a p p l i c a t i o n v i d e a v e c deux mé t h o d e s −−> <s c r i p t src=” e x 0 4 _ s t r u c t u r e A p p l i c a t i o n . j s ”></s c r i p t> <!−− Cré a t i o n de sous−module r e g e x U t i l de myApp . m e t i e r −−> <s c r i p t src=” . /ex05_modulePatternRegex . j s ”></s c r i p t> <!−− Cré a t i o n de sous−module a d r e s s e de myApp . m e t i e r −−> <s c r i p t src=” . /ex06_moduleMetierAdresse . j s ”></s c r i p t> <!−− Cré a t i o n d ’ une mé t h o d e f a b r i q u e d ’ a d r e s s e de myApp . m e t i e r . a d r e s s e −−> <s c r i p t src=” . / e x 0 6 _ f a b r i q u e A d r e s s e . j s ”></s c r i p t> <!−− Ajout d ’ un main e t ex é c u t i o n −−> <s c r i p t> 35 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 var t e s t A f f i c h e A d r e s s e = function ( a d r e s s e ) { document . w r i t e ( ”<div>” ) ; document . w r i t e ( ”<span><h2>Donné e s :</h2>” + ”<s trong>” + myApp . m e t i e r . a d r e s s e . g e t L a b e l T e x t ( ’ id ’ ) + ” :</s trong>” + a d r e s s e . g e t P r o p e r t y ( ’ id ’ ) + ”<br />” + ”<s trong>” + myApp . m e t i e r . a d r e s s e . g e t L a b e l T e x t ( ’ numeroRue ’ ) + ” :</s trong> ” + a d r e s s e . g e t P r o p e r t y ( ’ numeroRue ’ ) + ”<br />” + ”<s trong>” + myApp . m e t i e r . a d r e s s e . g e t L a b e l T e x t ( ’ rue ’ ) + ” :</s trong> ” + a d r e s s e . g e t P r o p e r t y ( ’ rue ’ ) + ”<br />” + ”<s trong>” + myApp . m e t i e r . a d r e s s e . g e t L a b e l T e x t ( ’ complementAdresse ’ ) + ” :</s trong> ” + a d r e s s e . g e t P r o p e r t y ( ’ complementAdresse ’ ) + ”<br />” + ”<s trong>” + myApp . m e t i e r . a d r e s s e . g e t L a b e l T e x t ( ’ c o d e P o s t a l ’ ) + ” :</s trong> ” + a d r e s s e . g e t P r o p e r t y ( ’ c o d e P o s t a l ’ ) + ”<br />” + ”<s trong>” + myApp . m e t i e r . a d r e s s e . g e t L a b e l T e x t ( ’ v i l l e ’ ) + ” :</s trong> ” + a d r e s s e . g e t P r o p e r t y ( ’ v i l l e ’ ) + ”<br />” + ”<s trong>” + myApp . m e t i e r . a d r e s s e . g e t L a b e l T e x t ( ’ pays ’ ) + ” :</s trong> ” + a d r e s s e . g e t P r o p e r t y ( ’ pays ’ ) + ”</span>” ) ; // v a r i a n t e en énumé r a n t automatiquement l e s p r o p r i é t é s var htmlCode = ”<span><h2>E r r e u r s :</h2>” ; fo r ( var i=0 ; i < myApp . m e t i e r . a d r e s s e . g e t P r o p e r t y L i s t ( ) . l e n g t h ; ++i ) { var propertyName = myApp . m e t i e r . a d r e s s e . g e t P r o p e r t y L i s t ( ) [ i ] ; htmlCode += ”<s trong>” + myApp . m e t i e r . a d r e s s e . g e t L a b e l T e x t ( propertyName ) + ” :</s trong> ” + a d r e s s e . g e t E r r o r M e s s a g e ( propertyName ) + ”<br />” ; } htmlCode += ”</span>” ; document . w r i t e ( htmlCode ) ; document . w r i t e ( ”</div>” ) ; }; // Ajout d ’ une mé t h o d e mainFunction myApp . addModule ( ” mainFunction ” , function ( ) { // c r é a t i o n d ’ une i n s t a n c e var a d r e s s e = myApp . m e t i e r . a d r e s s e . c r e a t e I n s t a n c e ( { id : ” 04 a b f 8 5 b c 9 ” , numeroRue : ”2 b i s ” , r u e : ”Rue de l ’ a Paix ” , // o u b l i du champs complementAdresse c o d e P o s t a l : ” 630000 ” , v i l l e : ” Clermont−Ferrand ” , pays : ” France 2” }) ; testAfficheAdresse ( adresse ) ; a d r e s s e . s e t P r o p e r t y ( ” complementAdresse ” , ” \”Bâ t i m e n t 3D\” ” ) ; a d r e s s e . s e t P r o p e r t y ( ” c o d e P o s t a l ” , ” 63000 ” ) ; a d r e s s e . s e t P r o p e r t y ( ” pays ” , ” France ” ) ; a d r e s s e . s e t P r o p e r t y ( ”numeroRue” , ”@#*m” ) ; testAfficheAdresse ( adresse ) ; }) ; 36 Chapitre 2 : Programmation Fonctionnelle et Objet en JavaScript 76 // Exé c u t i o n de l a mé t h o d e mainFunction 77 myApp . mainFunction ( ) ; 78 </s c r i p t> 79 </body> 80 </html> 2.7 Création d’un Module myApp.view.adresse Nous ajoutons, dans un module myApp.view.adresse, des méthodes pour générer le code HTML d’une adresse, au format compact (sur une ligne) ou au format développé (avec le détail des labels des attribut). Nous ajoutons enfin une méthode pour générer le code d’un formulaire d’adresse, avec affichage des erreurs sur la forme des champs saisis. exemples/objet/ex07_adresseView.js 1 // Cré a t i o n d ’ un module myApp . v i e w e t d ’ un sous−module myApp . v i e w . a d r e s s e 2 myApp . addModule ( ” v i e w ” , { a d r e s s e : { } } ) ; 3 4 /* * 5 * Mé t h o d e de g éné r a t i o n de code HTML pour une i n s t a n c e d ’ a d r e s s e . 6 * Pour chaque p r o p r i é t é a t t e n d u e d ’ une a d r e s s e , l a d e s c r i p t i o n de l a p r o p r i é t é e t sa v a l e u r s o n t a f f i c h é e s . 7 * 8 * @method g e t H t m l D e v e l o p p e d 9 * @augments myApp . v i e w . a d r e s s e 10 * @param { O b j e c t } a d r e s s e − sp é c i f i c a t i o n d e s p r o p r i é t é s d ’ une i n s t a n c e d ’ adresse 11 * @param { s t r i n g } a d r e s s e . i d − i d e n t i f i a n t u n i q u e de l ’ i n s t a n c e 12 * @param { s t r i n g } a d r e s s e . numeroRue − numero de rue 13 * @param { s t r i n g } a d r e s s e . rue − nom de rue 14 * @param { s t r i n g } a d r e s s e . complementAdresse − compl é ment d ’ a d r e s s e ( l i e u dut , b â timent , r é s i d e n c e , e t c . ) 15 * @param { s t r i n g } a d r e s s e . c o d e P o s t a l − code p o s t a l 16 * @param { s t r i n g } a d r e s s e . v i l l e − nom de v i l l e 17 * @param { s t r i n g } a d r e s s e . pays − nom de pays 18 */ 19 myApp . addModule . apply (myApp . view . a d r e s s e , [ ” g e t H t m l D e v e l o p p e d ” , function ( a d r e s s e ){ 20 var htmlCode = ” ” ; 21 22 var moduleAdresse =myApp . m e t i e r . a d r e s s e ; 23 24 i f ( a d r e s s e . g e t P r o p e r t y ( ’ numeroRue ’ ) ) { 25 htmlCode += ”<span c l a s s =\” a d r e s s e I t e m \”>” + moduleAdresse . g e t L a b e l T e x t ( ’ numeroRue ’ ) + ”  ; :</span> ” + 26 a d r e s s e . g e t P r o p e r t y ( ’ numeroRue ’ ) + ” ,<br />” ; 27 } 28 29 htmlCode += ”<span c l a s s =\” a d r e s s e I t e m \”>” + moduleAdresse . g e t L a b e l T e x t ( ’ rue ’ ) + ”  ; :</span> ” + 30 a d r e s s e . g e t P r o p e r t y ( ’ rue ’ ) + ” ,<br />” ; 31 32 i f ( typeof a d r e s s e . g e t P r o p e r t y ( ’ complementAdresse ’ ) === ” s t r i n g ” && 33 a d r e s s e . g e t P r o p e r t y ( ’ complementAdresse ’ ) !== ” ” ) { 37 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 htmlCode += ”<span c l a s s =\” a d r e s s e I t e m \”>” + moduleAdresse . g e t L a b e l T e x t ( ’ complementAdresse ’ ) + ”  ; :</span> ” + a d r e s s e . g e t P r o p e r t y ( ’ complementAdresse ’ ) + ” ,<br />” ; } htmlCode += ”<span c l a s s =\” a d r e s s e I t e m \”>” + moduleAdresse . g e t L a b e l T e x t ( ’ c o d e P o s t a l ’ ) + ”  ; :</span> ” + a d r e s s e . g e t P r o p e r t y ( ’ c o d e P o s t a l ’ ) + ”<br />” + ”<span c l a s s =\” a d r e s s e I t e m \”>” + moduleAdresse . g e t L a b e l T e x t ( ’ v i l l e ’ ) + ”& nbsp ; :</span> ” + a d r e s s e . g e t P r o p e r t y ( ’ v i l l e ’ ) + ”<br />” + ”<span c l a s s =\” a d r e s s e I t e m \”>” + moduleAdresse . g e t L a b e l T e x t ( ’ pays ’ ) + ”& nbsp ; :</span> ” + a d r e s s e . g e t P r o p e r t y ( ’ pays ’ ) + ”<br />” ; i f ( adresse . hasError () ) { var e r r o r L i s t = a d r e s s e . g e t E r r o r L i s t ( ) ; htmlCode += ”<s trong>C e r t a i n s champs ont une e r r e u r f o r ( var i =0 ; i<e r r o r L i s t . l e n g t h ; i ++){ i f ( i > 0) { htmlCode += ” , ” ; } htmlCode += e r r o r L i s t [ i ] ; } } :</s trong><br />” ; return htmlCode ; }]) ; /* * * Mé t h o d e de g éné r a t i o n de code HTML pour une i n s t a n c e d ’ a d r e s s e . * L ’ a d r e s s e e s t a f f i c h é e s u r une l i g n e , s a n s mention d e s e r r e u r s . * * @method g e t H t m l D e v e l o p p e d * @augments myApp . v i e w . a d r e s s e * @param { O b j e c t } a d r e s s e − sp é c i f i c a t i o n d e s p r o p r i é t é s d ’ une i n s t a n c e d ’ adresse * @param { s t r i n g } a d r e s s e . i d − i d e n t i f i a n t u n i q u e de l ’ i n s t a n c e * @param { s t r i n g } a d r e s s e . numeroRue − numero de rue * @param { s t r i n g } a d r e s s e . rue − nom de rue * @param { s t r i n g } a d r e s s e . complementAdresse − compl é ment d ’ a d r e s s e ( l i e u dut , b â timent , r é s i d e n c e , e t c . ) * @param { s t r i n g } a d r e s s e . c o d e P o s t a l − code p o s t a l * @param { s t r i n g } a d r e s s e . v i l l e − nom de v i l l e * @param { s t r i n g } a d r e s s e . pays − nom de pays */ myApp . addModule . apply (myApp . view . a d r e s s e , [ ” getHtmlCompact ” , function ( a d r e s s e ) { var htmlCode = ” ” ; i f ( a d r e s s e . g e t P r o p e r t y ( ’ numeroRue ’ ) ) { htmlCode += a d r e s s e . g e t P r o p e r t y ( ’ numeroRue ’ ) + ” , ” ; } htmlCode += a d r e s s e . g e t P r o p e r t y ( ’ rue ’ ) + ” , ” ; i f ( a d r e s s e . g e t P r o p e r t y ( ’ complementAdresse ’ ) ) { htmlCode += a d r e s s e . g e t P r o p e r t y ( ’ complementAdresse ’ ) + ” , ” ; 38 Chapitre 2 : Programmation Fonctionnelle et Objet en JavaScript 84 85 86 87 88 89 } htmlCode += a d r e s s e . g e t P r o p e r t y ( ’ c o d e P o s t a l ’ ) + ” ” + adresse . getProperty ( ’ v i l l e ’ ) + ” , ” + a d r e s s e . g e t P r o p e r t y ( ’ pays ’ ) ; return htmlCode ; }]) ; exemples/objet/ex07_adresseView.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <!doctype HTML> <html lang=” f r ”> <head> <meta charset=”UTF−8” /> <t i t l e>A f f i c h a g e d ’ A d r e s s e s</ t i t l e> <l i nk rel=” s t y l e s h e e t ” href=” ex07_adresseView . c s s ” /> </head> <body> <!−− Cré a t i o n de l ’ a p p l i c a t i o n v i d e a v e c deux mé t h o d e s −−> <s c r i p t src=” e x 0 4 _ s t r u c t u r e A p p l i c a t i o n . j s ”></s c r i p t> <!−− Cré a t i o n de sous−module r e g e x U t i l de myApp . m e t i e r −−> <s c r i p t src=” . /ex05_modulePatternRegex . j s ”></s c r i p t> <!−− Cré a t i o n de sous−module a d r e s s e de myApp . m e t i e r −−> <s c r i p t src=” . /ex06_moduleMetierAdresse . j s ”></s c r i p t> <!−− Cré a t i o n d ’ une mé t h o d e f a b r i q u e d ’ a d r e s s e de myApp . m e t i e r . a d r e s s e −−> <s c r i p t src=” . / e x 0 6 _ f a b r i q u e A d r e s s e . j s ”></s c r i p t> <!−− Cré a t i o n de f o n c t i o n s d ’ a f f i c h a g e dans myApp . m e t i e r . a d r e s s e −−> <s c r i p t src=” . /ex07_adresseView . j s ”></s c r i p t> <!−− Ajout d ’ un main e t ex é c u t i o n −−> <s c r i p t> var t e s t A f f i c h e A d r e s s e = function ( a d r e s s e ) { document . w r i t e ( ”<span s t y l e = \”width :260 px ; display : i n l i n e − b l o c k ; vertical−align : top ; \ ”>” + myApp . view . a d r e s s e . getHtmlDevelopped ( a d r e s s e )+ ”<br /><br />” + myApp . view . a d r e s s e . getHtmlCompact ( a d r e s s e ) + ”</span>” ) ; }; // Ajout d ’ une mé t h o d e mainFunction myApp . addModule ( ” mainFunction ” , function ( ) { // c r é a t i o n d ’ une i n s t a n c e 39 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 34 var a d r e s s e = myApp . m e t i e r . a d r e s s e . c r e a t e I n s t a n c e ( { 35 id : ” 04 a b f 8 5 b c 9 ” , 36 numeroRue : ”2 b i s ” , 37 r u e : ”Rue de l ’ a Paix ” , 38 // o u b l i du champs complementAdresse 39 c o d e P o s t a l : ” 630000 ” , 40 v i l l e : ” Clermont−Ferrand ” , 41 pays : ” France 2” 42 }) ; 43 44 testAfficheAdresse ( adresse ) ; 45 46 a d r e s s e . s e t P r o p e r t y ( ” complementAdresse ” , ” \”Bâ t i m e n t 3D\” ” ) ; 47 a d r e s s e . s e t P r o p e r t y ( ” c o d e P o s t a l ” , ” 63000 ” ) ; 48 a d r e s s e . s e t P r o p e r t y ( ” pays ” , ” France ” ) ; 49 a d r e s s e . s e t P r o p e r t y ( ”numeroRue” , ”@#*m” ) ; 50 51 testAfficheAdresse ( adresse ) ; 52 }) ; 53 54 // Exé c u t i o n de l a mé t h o d e mainFunction 55 myApp . mainFunction ( ) ; 56 </s c r i p t> 57 <s c r i p t src=” j q u e r y . j s ”></s c r i p t> 58 </body> 59 </html> 40 Chapitre 3 Constructeurs, Prototype et Patterns Associés 3.1 Constructeurs Un classe en Javascript se crée à partir d’un constructeur, qui est une fonction dont le nom est le nom de la classe à créer. À l’intérieur du constructeur, les propriétés de la classe sont créées et initialisées à l’aide de l’identificateur this. Le constructeur retourne un unique objet dont les propriétés correspondent à celles qui ont été initialisées à l’aide de l’identificateur this. En d’autres termes, le constructeur retourne une instance de la classe. Par convention, les noms de constructeurs commencent par une majuscule. exemples/objetPrototype/ex01_classeTelephone.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 /* * * C o n s t r u c t e u r de t é l é phone . Notez l a m a j u s c u l e s u r l e nom . * @constructor * @param { s t r i n g } t e l 1 − l e numé ro de t é l é phone . * @param { s t r i n g } [ t e l 2 ] − un second numé ro de t é l é phone . */ var Telephone = function ( t e l 1 , /* argument o p t i o n n e l */ t e l 2 ) { var checkPhone = function ( t e l ) { // Test d ’ e x p r e s s i o n r é g u l i è r e a p r è s s u p p r e s s i o n d e s e s p a c e s e t t a b u l a t i o n s i f ( typeof t e l . l i b e l l e !== ” s t r i n g ” | | typeof t e l . numero !== ” s t r i n g ” | | /^ ((\+33) | 0 ) [0 −9]{9} $/ . t e s t ( t e l . numero . r e p l a c e ( / \ s /g , ’ ’ ) ) !== true ) { throw { name : ” I l l e g a l A r g u m e n t E x c e p t i o n ” , message : ”Numé ro de t é l é phone \” ” + t e l . l i b e l l e + ” : ” + t e l . numero + ” \” i n v a l i d e ” } } }; checkPhone ( t e l 1 ) ; // Cré a t i o n d ’ un a t t r i b u t de l a c l a s s e t h i s . t e l 1=t e l 1 ; i f ( t e l 2 !== u n d e f i n e d ) { checkPhone ( t e l 2 ) ; // Cré a t i o n d ’ un a t t r i b u t de l a c l a s s e 41 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 t h i s . t e l 2=t e l 2 ; } /* * * @method g etH tml * @return { s t r i n g } l e code HTML pour a f f i c h e r une i n s t a n c e . */ t h i s . getHtml = function ( ) { var htmlCode = t h i s . t e l 1 . l i b e l l e + ” : ” + t h i s . t e l 1 . numero + ”<br />” ; i f ( t h i s . t e l 2 !== u n d e f i n e d ) { htmlCode += t h i s . t e l 2 . l i b e l l e + ” : ” + t h i s . t e l 2 . numero + ”<br />” ; } return htmlCode ; }; } exemples/objetPrototype/ex01_classeTelephone.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>C l a s s e s</ t i t l e> <s c r i p t s r c=” . / e x 0 1 _ c l a s s e T e l e p h o n e . j s ”></s c r i p t> </head> <body> <s c r i p t> try { // Appel du c o n s t r u c t e u r a v e c l e mot c l é ”new” : var t e l = new Telephone ( { l i b e l l e : ” Maison ” , numero : ”+33 1 23 45 67 89 ” } , { l i b e l l e : ” M o b i l e ” , numero : ” 09 87 65 43 21 ” } ) ; // U t i l i s a t i o n de l a mé t h o d e getHt ml ( ) document . w r i t e ( ”<p>” + t e l . getHtml ( ) + ”</p>” ) ; } catch ( e r r ) { a l e r t ( e r r . message ) ; } </s c r i p t> </body> </html> Un constructeur doit systématiquement être employé avec le mot clé new. En effet, l’emploi d’un constructeur sans le mot clé new, qui ne génère, en soi, aucune exception ni warning conduit à un comportement imprévisible, généralement catastrophique. D’où l’importance de respecter la convention que les noms de constructeurs commencent par une majuscule, contrairement à toutes les autres fonctions ou variables. 3.2 Prototypes 3.2.1 Notion de prototype Les méthodes de classes telles que vues jusqu’à présent ont l’inconvénient que ces méthodes sont des propriétés des objets, qui existent en autant d’exemplaires qu’il y a d’instance des 42 Chapitre 3 : Constructeurs, Prototype et Patterns Associés objets, alors qu’elles sont constantes. Pour éviter cela, on peut mettre les méthodes non pas directement dans l’objet, mais dans son prototype. Le prototype est lui-même une propriété de l’objet, mais qui est partagée entre tous les objets de la classe (il s’agit d’une variable de classe). Toutes les variables de classes doivent être crées au niveau du prototype. exemples/objetPrototype/ex02_prototype.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 /* * * Augmente l a c l a s s e Telephone en e j o u t a n t une mé t h o d e au p r o t o t y p e de Telephone * @method g e t H t m l B y L i b e l l e * @return { s t r i n g } l e code HTML pour a f f i c h e r une i n s t a n c e . */ Telephone . p r o t o t y p e . getNumero = function ( l i b e l l e ) { i f ( t h i s . t e l 1 . l i b e l l e . toLowerCase ( ) === l i b e l l e . toLowerCase ( ) ) { return t h i s . t e l 1 . numero ; } i f ( t h i s . t e l 2 !== u n d e f i n e d && t h i s . t e l 2 . l i b e l l e . toLowerCase ( ) === l i b e l l e . toLowerCase ( ) ) { return t h i s . t e l 2 . numero ; } return ”Numé ro i n e x i s t a n t ” ; }; exemples/objetPrototype/ex02_prototype.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>P r o t o t y p e s</ t i t l e> <!−− I n c l u s i o n de l a dé f i n i t i o n de l a c l a s s e Telephone −−> <s c r i p t s r c=” . / e x 0 1 _ c l a s s e T e l e p h o n e . j s ”></s c r i p t> <s c r i p t s r c=” . / e x 0 2 _ p r o t o t y p e . j s ”></s c r i p t> </head> <body> <s c r i p t> try { // Appel du c o n s t r u c t e u r a v e c l e mot c l é ”new” : var t e l = new Telephone ( { l i b e l l e : ” Maison ” , numero : ”+33 1 23 45 67 89 ” } , { l i b e l l e : ” M o b i l e ” , numero : ” 09 87 65 43 21 ” } ) ; // U t i l i s a t i o n de l a mé t h o d e getNumero ( ) du p r o t o t y p e document . w r i t e ( ”<p>” + t e l . getNumero ( ” maison ” ) + ”</p>” ) ; document . w r i t e ( ”<p>” + t e l . getNumero ( ” m o b i l e ” ) + ”</p>” ) ; document . w r i t e ( ”<p>” + t e l . getNumero ( ” t r a v a i l ” ) + ”</p>” ) ; } catch ( e r r ) { a l e r t ( e r r . message ) ; } </s c r i p t> </body> </html> La méthode Object.prototype.hasOwnProperty() permet de tester si une propriété d’un objet existe au niveau de l’objet lui-même, ou au niveau de son prototype, ou encore du 43 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery prototype de son prototype. Le fonctionnement de la notion de prototype est le suivant : Lors d’une tentative d’accès à un propriété de l’objet, la propriété est tout d’abord recherchée au niveau des propriétés propres. Seulement si la propriété n’existe pas dans les propriétés propres, elle est ensuite recherchée dans le prototype de l’objet. Si elle n’existe pas non plus à ce niveau, la propriété est recherchée dans le prototype du prototype, et ainsi de suite... Ce processus s’appelle la délégation et il permet de spécialiser les objets, en les faisant hériter des propriétés d’un prototype, tout en leur permettant de surcharger (redéfinir) les données ou méthodes. Ceci constitue un mécanisme très souple d’héritage, entièrement dynamique. 3.2.2 Surcharge des méthodes du prototype : l’exemple de toString La méthode toString, qui permet de convertir un objet en chaîne de caractères (par exemple pour l’afficher) a une implémentation par défaut définie dans le prototype de la classe Object. On peut la surcharger dans le prototype de notre classe Telephone pour changer le comportement par défaut de la méthode toString et mettre en forme à notre guise les numéros de téléphone. exemples/objetPrototype/ex03_toString.js 1 2 3 4 5 6 7 8 9 10 11 12 /* * * @override t o S t r i n g * @return { s t r i n g } une cha î ne de c a r a c t è r e r e p r é s e n t a t n t l ’ i n s t a n c e de Telephone */ Telephone . p r o t o t y p e . t o S t r i n g = function ( ) { var t e x t e = t h i s . t e l 1 . l i b e l l e + ” : ” + t h i s . t e l 1 . numero ; i f ( t h i s . t e l 2 !== u n d e f i n e d ) { t e x t e += ” e t ” + t h i s . t e l 2 . l i b e l l e + ” : ” + t h i s . t e l 2 . numero ; } return t e x t e ; } exemples/objetPrototype/ex03_toString.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>S u r c h a r g e d e s p r o p r i é t é s du p r o t o t y p e</ t i t l e> <!−− I n c l u s i o n de l a dé f i n i t i o n de l a c l a s s e Telephone −−> <s c r i p t s r c=” . / e x 0 1 _ c l a s s e T e l e p h o n e . j s ”></s c r i p t> <s c r i p t s r c=” . / e x 0 3 _ t o S t r i n g . j s ”></s c r i p t> </head> <body> <s c r i p t> try { // Appel du c o n s t r u c t e u r a v e c l e mot c l é ”new” : var t e l = new Telephone ( { l i b e l l e : ” Maison ” , numero : ”+33 1 23 45 67 89 ” } , { l i b e l l e : ” M o b i l e ” , numero : ” 09 87 65 43 21 ” } ) ; // U t i l i s a t i o n i m p l i c i t e de l a mé t h o d e t o S t r i n g ( ) ( c o n v e r s i o n ) document . w r i t e ( ”<p>” + t e l + ”</p>” ) ; } catch ( e r r ) { 44 Chapitre 3 : Constructeurs, Prototype et Patterns Associés 19 a l e r t ( e r r . message ) ; 20 } 21 </s c r i p t> 22 </body> 23 </html> 3.3 Exemple : assurer l’implémentation d’interfaces Voici une classe Interface, qui possède comme attribut un array de noms de méthodes, et qui permet de vérifier qu’un objet possède bien des méthodes avec les noms correspondants. Notons que nous ne vérifions pas que lés méthodes correspondent bien à un prototype déterminé, mais seulement que les noms de méthodes sont présents. exemples/objetPrototype/ex04_interfaceImplementation.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 /* * * Dé f i n i t une ” i n t e r f a c e ” , a v e c un nom e t un e n s e m b l e de mé t h o d e s . * Ceci nous p e r m e t t r a de v é r i f i e r qu ’ un c e r t a i n nombre d ’ op é r a t i o n s * s o n t pr é s e n t e s dans un o b j e t J a v a S c r i p t . * * @constructor I n t e r f a c e * @param { s t r i n g } name − nom de l ’ i n t e r f a c e * @param { s t r i n g [ ] } methods − t a b l e a u c o n t e n a n t l e s noms d e s mé t h o d e s de l ’ interface . */ var I n t e r f a c e = function ( methods ) { i f ( methods . l e n g t h === u n d e f i n e d ) { throw { name : ” I l l e g a l A r g u m e n t ” , message : ”Une i n t e r f a c e né c e s s i t e un a r r a y ( ou array−l i k e ) de noms de mé thodes . ” }; } // Cré a t i o n d ’ une p r o p r i é t é pour s t o c k e r l e nom de l ’ i n t e r f a c // Cré a t i o n d ’ un t a b l e a u pour s o c k e r l e s noms de mé t h o d e d t h i s . methods = [ ] ; // pour chaque nom de mé t h o d e fo r ( var i = 0 ; i < methods . l e n g t h ; ++i ) { // Vé r i f i c a t i o n de t y p e i f ( typeof methods [ i ] !== ’ s t r i n g ’ ) { throw { name : ” I l l e g a l A r g u m e n t ” , message : ” Les noms de mé t h o d e s d ’ une i n t e r f a c e d o i v e n t ê t r e de t y p e string .” }; } // Ajout du nom de mé t h o d e t h i s . methods . push ( methods [ i ] ) ; } }; /* * 45 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 * * * * * * * Vé r i f i e qu ’ un o b j e t ” i m p l é mente une i n t e r f a c e ” , en ce s e n s qu ’ i l comporte un c e r t a i n nombre de mé t h o d e s ( p r o p r i é t é s de t y p e f o n c t i o n ) q u i ont l e s mêmes noms que l e s mé t h o d e s de l ’ i n t e r f a c e . @method isImplementedBy @param { O b j e c t } o b j e t − o b j e t q u i d o i t i m p l é menter l ’ i n t e r f a c e . @return { b o o l e a n | s t r i n g } t r u e s i l ’ o b j e t comporte t o u t e s l e s mé t h o d e s de l ’ interface , * un message d ’ e r r e u r i n d i q u a n t une mé t h o d e q u i n ’ e s t pas pr é s e n t e dans l ’ o b j e t sinon . */ I n t e r f a c e . p r o t o t y p e . isImplementedBy = function ( o b j e t ) { // Pour chaque nom de mé t h o d e fo r ( var i = 0 ; i < t h i s . methods . l e n g t h ; ++i ) { var methodName = t h i s . methods [ i ] ; // S i l ’ o b j e t n ’ a pas de p r o p r i é t é de ce nom q u i s o i t de t y p e f o n c t i o n i f ( ! o b j e t [ methodName ] | | typeof o b j e t [ methodName ] !== ’ f u n c t i o n ’ ) { return ”L ’ o b j e t n ’ i m p l é mente pas l a mé t h o d e ” + methodName ; } } return true ; }; Voici un exemple dans lequel nous définissions deux interfaces attendues de nos modules métier : 1. L’interface attendue d’un module permet de tester la présence d’un certain nombre de méthodes statiques ; 2. L’interface attendue des instances permet de s’assurer de la présence d’un certain nombre de méthodes sur les instances. Remarque. Dans nos exemples, une méthode getModule permet d’obtenir le module correspondant à une instance (par exemple myApp.metier.adresse) à partir d’une instance d’adresse obtenue par la fabrique createInstance de ce même module). Nous verrons dans la partie 3.4 comment simplifier notre interface des modules métier en supprimant le besoin de cette méthode getModule. exemples/objetPrototype/ex04_interfaceImplementation.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>Impl é mentation d ’ i n t e r f a c e s</ t i t l e> <!−− Chargement de l ’ ensemble d e s modules mé t i e r −−> <s c r i p t s r c=” . / m o d u l e s M e t i e r . j s ”></s c r i p t> <!−− C l a s s e de v é r i f i c a t i o n de l ’ impl é mentation d ’ i n t e r f a c e s −−> <s c r i p t s r c=” . / e x 0 4 _ i n t e r f a c e I m p l e m e n t a t i o n . j s ”></s c r i p t> </head> <body> <s c r i p t> // Ajout d ’ une mé t h o d e mainFunction 46 Chapitre 3 : Constructeurs, Prototype et Patterns Associés 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 myApp . addModule ( ” mainFunction ” , function ( ) { // Dé f i n i t i o n de l ’ i n t e r f a c e commune aux modules mé t i e r ( a d r e s s e , personne , etc .) var metierCommonMethods = new I n t e r f a c e ( [ ” getPropertyList ” , ” getLabelText ” , ” testRegex ” , ” createInstance ” ] ) ; // Dé f i n i t i o n de l ’ i n t e r f a c e commune aux i n s t a n c e s d e s modules mé t i e r ( a d r e s s e , personne , e t c . ) var metierCommonInstanceMethods = new I n t e r f a c e ( [ ” getModule ” , ” g e t P r o p e r t y ” , ” s e t P r o p e r t y ” , ” h a s E r r o r ” , ” g e t E r r o r M e s s a g e ” , ” getErrorList ” ]) ; // c r é a t i o n d ’ une i n s t a n c e var monObjet = myApp . m e t i e r . a d r e s s e . c r e a t e I n s t a n c e ( { i d : ” 04 a b f 8 5 b c 9 ” , numeroRue : ”2 b i s ” , r u e : ”Rue de l ’ a Paix ” , complementAdresse : ”Bâ t i m e n t 3D” , c o d e P o s t a l : ” 63000 ” , v i l l e : ” Clermont−Ferrand ” , pays : ” France ” }) ; var t e s t I n s t a n c e I n t e r f a c e = metierCommonInstanceMethods . isImplementedBy ( monObjet ) ; i f ( t e s t I n s t a n c e I n t e r f a c e !== true ) { document . w r i t e ( ”<p>” + t e s t I n s t a n c e I n t e r f a c e + ”</p>” ) ; } else { var module = monObjet . getModule ( ) ; var t e s t M o d u l e I n t e r f a c e = metierCommonMethods . isImplementedBy ( module ) ; i f ( t e s t M o d u l e I n t e r f a c e !== true ) { document . w r i t e ( ”<p>” + t e s t M o d u l e I n t e r f a c e + ”</p>” ) ; } else { document . w r i t e ( ”<p>L ’ o b j e t s e m b l e b i e n i m p l é menter l e s mé t h o d e s r e q u i s e s .</p>” ) ; } } }) ; 45 46 47 48 49 // Exé c u t i o n de l a mé t h o d e mainFunction 50 myApp . mainFunction ( ) ; 51 </s c r i p t> 52 </body> 53 </html> 3.4 Fabrique d’Objets Métier avec prototype Le but de cette partie est de redéfinir la babrique d’instances d’adresses (méthode myApp.metier.adresse.createInstance de la partie 2.6) pour que les méthodes du module (méthodes getPropertyList, getLabelText, testRegex, createInstance) soient accessibles au niveau des instances (comme méthode dans le prototype des instances). Les avantages de cette implémentation sont les suivants : • Interface plus simple dans laquelle la méthode getModule() des instances, qui retournait 47 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery l’objet myApp.metier.adresse a été supprimée, puisque l’objet myApp.metier.adresse constituera désormais le prototype des instances crées par notre fabrique. Les méthodes du module seront ainsi accessibles de manière transparentes via l’instance. • Le code source des méthodes d’instance (getProperty, setProperty, hasError, getErrorMessage, getErrorList) n’est compilé qu’une seule fois, car il se trouve comme variable locale privée d’un module, est est ensuite exposé via de simples alias dans l’interface des instances. Nous avons aussi ajouté la possibilité, en passant un argument inputObj égal à null, de réer une instance par défaut (id aléatoire et autres attributs vides) Ceci permet par exemple d’initialiser un formulaire vide pour créer une nouvelle instance. exemples/objetPrototype/ex05_fabriqueAdressePrototype.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 /* * * Fabrique qui cr é e des o b j e t s repr é sentant des adresse , suiva nt l e ” pattern module ” . * Le paramètre s p e c de n o t r e f o n c t i o n e s t un o b j e t c o n t e n a n t l e s p r o p r i é t é s d ’ une a d r e s s e à c r é e r . * * @method c r e a t e I n s t a n c e * @augments myApp . m e t i e r . a d r e s s e * @param { O b j e c t | n u l l } i n p u t O b j − sp é c i f i c a t i o n d e s p r o p r i é t é s d ’ une i n s t a n c e d ’ a d r e s s e . S i i n p u t O b j e s t n u l l , on c r é e une a d r e s s e par d é f a u t ( i d a l é atoire , autres propri é t és vide ) . * @param { s t r i n g } i n p u t O b j . i d − i d e n t i f i a n t u n i q u e de l ’ i n s t a n c e * @param { s t r i n g } i n p u t O b j . numeroRue − numero de rue * @param { s t r i n g } i n p u t O b j . rue − nom de rue * @param { s t r i n g } i n p u t O b j . complementAdresse − compl é ment d ’ a d r e s s e ( l i e u dut , b â timent , r é s i d e n c e , e t c . ) * @param { s t r i n g } i n p u t O b j . c o d e P o s t a l − code p o s t a l * @param { s t r i n g } i n p u t O b j . v i l l e − nom de v i l l e * @param { s t r i n g } i n p u t O b j . pays − nom de pays */ myApp . addModule . apply (myApp . m e t i e r . a d r e s s e , [ ” c r e a t e I n s t a n c e ” , function ( ) { // ////////////////////////////////////////////// // I n t e r f a c e p u b l i q u e du module /* * * Cré a t i o n d ’ un c o n s t r u c t e u r p r i v é , c r é ant une c l a s s e dont une i n s t a n c e * p r i v é e c o n t i e n d r a l e s donn é e s de l ’ i n s t a n c e , rendue p u b l i q u e v i a l ’ interface * du module . * * L ’ u t i l i s a t i o n d ’ un c o n s t r u c t e u r e s t de d é f i n i r l e s mé t h o d e p r i v é e s au n i v e a u du p r o t o t y p e . */ var P r i v a t e I n s t a n c e C o n s t r u c t o r = function ( ) { /* * * O b j e t p r i v é c o n t e n a n t l e s p r o p r i é t é s ( id , rue , . . . ) de l ’ i n s t a n c e , initialement vide * @member * @private */ 48 Chapitre 3 : Constructeurs, Prototype et Patterns Associés 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 this . adresse = {} ; /* * * O b j e t p r i v é c o n t e n a n t l e s messages d ’ e r r e u r a s s o c i é s aux p r o p r i é t é s attendues des i n s t a n c e s . * @member * @private */ this . dataError = false ; }; /* * * A j o u t e une p r o p r i é t é ( message d ’ e r r e u r ) dans d a t a E r r o r * @method addError * @private */ P r i v a t e I n s t a n c e C o n s t r u c t o r . p r o t o t y p e . addError = function ( propertyName , message ){ // s i d a t a E r r o r n ’ e x i s t e pas , on l e c r é e i f ( t h i s . d a t a E r r o r === f a l s e ) { this . dataError = {} ; } // Ajout d ’ une p r o p r i é t é t h i s . d a t a E r r o r [ propertyName ] = message ; }; /* * * S e t t e r : i n i t i a l i s e l a v a l e u r pour une p r o p r i é t é a t t e n d u e s d ’ une i n s t a n c e . * En c a s d ’ e r r e u r un message pour c e t t e p r o p r i é t é e s t e s t a j o u t é dans dataError . * En l ’ a b s e n c e d ’ e r r e u r , une é v e n t u e l l e e r r e u r pr é c é d e n t e e s t e f f a c é e . * @method addError * @private */ P r i v a t e I n s t a n c e C o n s t r u c t o r . p r o t o t y p e . s e t P r o p e r t y O r E r r o r = function ( propertyName , v a l u e ) { var r e s u l t T e s t R e g e x = myApp . m e t i e r . a d r e s s e . t e s t R e g e x ( propertyName , v a l u e ) ; // On i n t i a l i s e l a p r o p r i é t é de l ’ a d r e s s e t h i s . a d r e s s e [ propertyName ] = v a l u e ; // S i l a v a l i d a t i o n par e x p r e s s i o n r é g u l i è r e e s t p a s s é e i f ( r e s u l t T e s t R e g e x === true ) { // On e f f a c e une v i e i l l e e r r e u r é v e n t u e l l e delete t h i s . d a t a E r r o r [ propertyName ] ; } else { // On i n i t a l i s e l a p r o p r i é t é de l ’ o b j e t d e s e r r e u r s . // a v e c l e message d ’ e r r e u r . P r i v a t e I n s t a n c e C o n s t r u c t o r . p r o t o t y p e . addError . c a l l ( this , propertyName , ” Propri é t é ” + value + ” i n v a l i d e : ” + resultTestRegex ) ; } }; /* * * @return { b o o l e a n } t r u e s ’ i l y a ( au moins ) une e r r e u r , f a l s e s i n o n */ 49 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 P r i v a t e I n s t a n c e C o n s t r u c t o r . p r o t o t y p e . h a s E r r o r = function ( ) { i f ( t h i s . d a t a E r r o r === f a l s e ) { return f a l s e ; } f or ( var propertyName in t h i s . d a t a E r r o r ) { i f ( t h i s . d a t a E r r o r . hasOwnProperty ( propertyName ) ) { return true ; } } return f a l s e ; }; /* * * Ré c u p è r e l a l i s t e d e s champs q u i ont une e r r e u r * @return { s t r i n g [ ] } t a b l e a u d e s noms de p r o r p i é t é s q u i comportent une erreur . */ P r i v a t e I n s t a n c e C o n s t r u c t o r . p r o t o t y p e . g e t E r r o r L i s t = function ( ) { var e r r o r L i s t = [ ] ; fo r ( var propertyName in t h i s . d a t a E r r o r ) { i f ( t h i s . d a t a E r r o r . hasOwnProperty ( propertyName ) ) { e r r o r L i s t . push ( propertyName ) ; } } return e r r o r L i s t ; }; // Géné r a t i o n d ’ un ID a l é a t o i r e en c a s de c r é a t i o n d ’ une n o u v e l l e a d r e s s e // ( c a s où l e s s p e c i f i c a t i o n s i n p u t O b j s o n t n u l l ) var generateRandomId = function ( ) { var idLen gth = 1 0 ; var r e s u l t a t = ” ” ; var h e x a D i g i t s = Array ( ”0” , ”1” , ”2” , ”3” , ”4” , ”5” , ”6 ” , ”7 ” , ”8 ” , ”9 ” , ” a” , ” b ” , ” c ” , ” d” , ” e ” , ” f ” ) ; var i ; fo r ( i =0 ; i<10 ; ++i ) { r e s u l t a t += h e x a D i g i t s [ Math . f l o o r ( Math . random ( ) * 1 6 ) ] ; } return r e s u l t a t ; } /* * * F a b r i c a t i o n d ’ une i n s t a n c e à p a r t i r d ’ une i n p u t O b j de sp é c i f i c a t i o n s * ( v o i r documentation du module ) e t d ’ une i n s t a n c e p r i v é e v i d e . */ var f a b r i q u e I n s t a n c e = function ( inputObj , p r i v a t e I n s t a n c e ) { /* * * C o n s t r u c t e u r de l ’ i n s t a n c e q u i s e r a e f f e c t i v e m e n t r e t o u r n é e ( i n s t a n c e publique ) , * q u i c r é e l e s donn é e s dans l ’ o b j e t p r i v a t e I n s t a n c e . * Ce c o n s t r u c t e u r c o n t i e n d r a a u s s i l e s mé t h o d e s d ’ i n s t a n c e p u b l i q u e s , * e t l e s mé t h o d e s ( s t a t i q u e s ) du module dans son p r o t o t y p e . */ var P u b l i c I n s t a n c e C o n s t r u c t o r = function ( ) { 50 Chapitre 3 : Constructeurs, Prototype et Patterns Associés 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 var a d r e s s e M e t h o d s = myApp . m e t i e r . a d r e s s e ; // r a c o u r c i // S i l ’ o b j e t en argument e s t n u l l , on c o n s t r u i t une i n s t a n c e par d é f a u t ( vide ) i f ( inputObj === null ) { privateInstance . adresse = { i d : generateRandomId ( ) , numeroRue : ” ” , rue : ”” , complementAdresse : ” ” , codePostal : ”” , v i l l e : ”” , pays : ” ” }; } else { // Parcours d e s p r o p r i é t é s de g e t P r o p e r t y L i s t ( ) // q u i c o r r e s p o n d e n t aux p r o p r i é t é s de l ’ a d r e s s e à c r é e r fo r ( var i = 0 ; i < a d r e s s e M e t h o d s . g e t P r o p e r t y L i s t ( ) . l e n g t h ; ++i ) { var propertyName = a d r e s s e M e t h o d s . g e t P r o p e r t y L i s t ( ) [ i ] ; p r i v a t e I n s t a n c e . s e t P r o p e r t y O r E r r o r ( propertyName , inputObj [ propertyName ] ) ; } } /* * * A c c e s s e u r pour t o u s l e s membres p r i v é s d ’ i n s t a n c e . * @param { s t r i n g } propertyName − nom de l a p r o p r i é t é a t t e n d u e d ’ une instance * @return { s t r i n g } l a v a l e u r de l a p r o p r i é t é ou u n d e f i n e d en c a s de nom de p r o p r i é t é inconnu . */ t h i s . g e t P r o p e r t y = function ( propertyName ) { return p r i v a t e I n s t a n c e . a d r e s s e [ propertyName ] ; }; /* * * S e t t e r : i n i t i a l i s e l a v a l e u r pour une p r o p r i é t é a t t e n d u e s d ’ une i n s t a n c e a p r è s un t e s t . * En c a s d ’ e r r e u r , un message pour c e t t e p r o p r i é t é e s t e s t a j o u t é dans dataError . * En l ’ a b s e n c e d ’ e r r e u r , une é v e n t u e l l e e r r e u r pr é c é d e n t e e s t e f f a c é e . * @param { s t r i n g } propertyName − nom de l a p r o p r i é t é a t t e n d u e d ’ une instance * @param { s t r i n g } v a l u e − v a l e u r à p r e n d r e pour l a p r o p r i é t é a t t e n d u d ’ une i n s t a n c e * @return { b o o l e a n } t r u e s ’ i l y a au moins une e r r e u r , f a l s e s i n o n */ t h i s . s e t P r o p e r t y = function ( propertyName , v a l u e ) { return p r i v a t e I n s t a n c e . s e t P r o p e r t y O r E r r o r ( propertyName , v a l u e ) ; }; /* * * @return { b o o l e a n } t r u e s ’ i l y a ( au moins ) une e r r e u r , f a l s e s i n o n */ t h i s . h a s E r r o r = function ( ) { 51 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 return p r i v a t e I n s t a n c e . h a s E r r o r ( ) ; }; /* * * Donne l ’ a c c è s aux d i f f é r e n t s messages d ’ e r r e u r . * @param { s t r i n g } propertyName − nom de p r o p r i é t é d ’ une i n s t a n c e d ’ adresse * @return { s t r i n g | u n d e f i n e d } l e message d ’ e r r e u r pour une p r o p r i é t é s ’ i l e x i s t e ou u n d e f i n e d en l ’ a b s e n c e d ’ e r r e u r */ t h i s . g e t E r r o r M e s s a g e = function ( propertyName ) { return p r i v a t e I n s t a n c e . d a t a E r r o r [ propertyName ] ; }; /* * * Ré c u p è r e l a l i s t e d e s champs q u i ont une e r r e u r * @return { s t r i n g [ ] } t a b l e a u d e s noms de p r o r p i é t é s q u i comportent une erreur . */ t h i s . g e t E r r o r L i s t = function ( ) { return p r i v a t e I n s t a n c e . g e t E r r o r L i s t ( ) ; }; } ; // f i n du P u b l i c I n s t a n c e C o n s t r u c t o r // MISE À DISPOSITION DES MÉTHODES DU MODULE VIA LE PROTOTYPE P u b l i c I n s t a n c e C o n s t r u c t o r . p r o t o t y p e = myApp . m e t i e r . a d r e s s e ; return new P u b l i c I n s t a n c e C o n s t r u c t o r ( inputObj ) ; } ; // f i n de l a f o n c t i o n f a b r i q u e I n s t a n c e // C o n s t r u c t i o n d ’ une i n s t a n c e a v e c return function ( inputObj ) { // Un o b j e t P r i v a t e I n s t a n c e C o n s t r u c t o r e s t c o n s t r u i t pour l ’ o c c a s i o n return f a b r i q u e I n s t a n c e ( inputObj , new P r i v a t e I n s t a n c e C o n s t r u c t o r ( ) ) ; }; } ( ) // f i n de l a mé t h o d e c r e a t e I n s t a n c e ] ) ; // f i n de l ’ a p p e l ” a p p l y ” de l a mé t h o d e myApp . addModule exemples/objetPrototype/ex05_fabriqueAdressePrototype.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>Cr é a t i o n d ’ I n s t a n c e s d ’ A d r e s s e s</ t i t l e> <l i nk r e l=” s t y l e s h e e t ” , h r e f=” b a s i c S t y l e . c s s ” /> </head> <body> <!−− Cr é a t i o n de l ’ a p p l i c a t i o n v i d e avec deux mé t h o d e s −−> <s c r i p t s r c=” . . / o b j e t / e x 0 4 _ s t r u c t u r e A p p l i c a t i o n . j s ”></s c r i p t> <!−− Cr é a t i o n de sous−module r e g e x U t i l de myApp . m e t i e r −−> <s c r i p t s r c=” . . / o b j e t / ex05_modulePatternRegex . j s ”></s c r i p t> <!−− Cr é a t i o n de sous−module a d r e s s e de myApp . m e t i e r −−> <s c r i p t s r c=” . . / o b j e t / ex06_moduleMetierAdresse . j s ”></s c r i p t> <!−− Cr é a t i o n d ’ une mé thode f a b r i q u e d ’ a d r e s s e de myApp . m e t i e r . a d r e s s e −−> 52 Chapitre 3 : Constructeurs, Prototype et Patterns Associés 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 <s c r i p t s r c=” . / e x 0 5 _ f a b r i q u e A d r e s s e P r o t o t y p e . j s ”></s c r i p t> <!−− Ajout d ’ un main e t ex é c u t i o n −−> <s c r i p t> var t e s t A f f i c h e A d r e s s e = function ( a d r e s s e ) { document . w r i t e ( ”<div>” ) ; document . w r i t e ( ”<span><h2>Donné e s :</h2>” + ”<s trong>” + a d r e s s e . g e t L a b e l T e x t ( ’ id ’ ) + ” :</s trong>” + a d r e s s e . g e t P r o p e r t y ( ’ id ’ ) + ”<br />” + ”<s trong>” + a d r e s s e . g e t L a b e l T e x t ( ’ numeroRue ’ ) + ” :</s trong> ” + a d r e s s e . g e t P r o p e r t y ( ’ numeroRue ’ ) + ”<br />” + ”<s trong>” + a d r e s s e . g e t L a b e l T e x t ( ’ rue ’ ) + ” :</s trong> ” + a d r e s s e . g e t P r o p e r t y ( ’ rue ’ ) + ”<br />” + ”<s trong>” + a d r e s s e . g e t L a b e l T e x t ( ’ complementAdresse ’ ) + ” :</s trong> ” + a d r e s s e . g e t P r o p e r t y ( ’ complementAdresse ’ ) + ”<br />” + ”<s trong>” + a d r e s s e . g e t L a b e l T e x t ( ’ c o d e P o s t a l ’ ) + ” :</s trong> ” + a d r e s s e . g e t P r o p e r t y ( ’ c o d e P o s t a l ’ ) + ”<br />” + ”<s trong>” + a d r e s s e . g e t L a b e l T e x t ( ’ v i l l e ’ ) + ” :</s trong> ” + a d r e s s e . g e t P r o p e r t y ( ’ v i l l e ’ ) + ”<br />” + ”<s trong>” + a d r e s s e . g e t L a b e l T e x t ( ’ pays ’ ) + ” :</s trong> ” + a d r e s s e . g e t P r o p e r t y ( ’ pays ’ ) + ”</span>” ) ; // v a r i a n t e en énumé r a n t automatiquement l e s p r o p r i é t é s var htmlCode = ”<span><h2>E r r e u r s :</h2>” ; fo r ( var i =0 ; i < a d r e s s e . g e t P r o p e r t y L i s t ( ) . l e n g t h ; ++i ) { var propertyName = a d r e s s e . g e t P r o p e r t y L i s t ( ) [ i ] ; htmlCode += ”<s trong>” + a d r e s s e . g e t L a b e l T e x t ( propertyName ) + ” trong> ” + a d r e s s e . g e t E r r o r M e s s a g e ( propertyName ) + ”<br />” ; } htmlCode += ”</span>” ; document . w r i t e ( htmlCode ) ; document . w r i t e ( ”</div>” ) ; }; // Ajout d ’ une mé t h o d e mainFunction myApp . addModule ( ” mainFunction ” , function ( ) { // c r é a t i o n d ’ une i n s t a n c e var a d r e s s e = myApp . m e t i e r . a d r e s s e . c r e a t e I n s t a n c e ( { i d : ” 04 a b f 8 5 b c 9 ” , numeroRue : ”2 b i s ” , r u e : ”Rue de l ’ a Paix ” , // o u b l i du champs complementAdresse c o d e P o s t a l : ” 630000 ” , v i l l e : ” Clermont−Ferrand ” , pays : ” France 2 ” }) ; testAfficheAdresse ( adresse ) ; a d r e s s e . s e t P r o p e r t y ( ” complementAdresse ” , ” \”Bâ t i m e n t 3D\” ” ) ; a d r e s s e . s e t P r o p e r t y ( ” c o d e P o s t a l ” , ” 63000 ” ) ; a d r e s s e . s e t P r o p e r t y ( ” pays ” , ” France ” ) ; 53 :</s Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 71 a d r e s s e . s e t P r o p e r t y ( ”numeroRue” , ”@#*m” ) ; 72 73 testAfficheAdresse ( adresse ) ; 74 }) ; 75 76 // Exé c u t i o n de l a mé t h o d e mainFunction 77 myApp . mainFunction ( ) ; 78 </s c r i p t> 79 </body> 80 </html> En utilisant cette fabrique, le code la méthode myApp.view.adresse.getHtmlDevelopped est un peu différent car il faut supprimer les appels à la méthode getModule, en accédant directement au module via le prototype des instances. En outre, l’interface des objets métiers se trouve simplifiée, car il n’y a plus qu’une seule interface, au lieu de deux dans l’exemple de la partie 3.3. exemples/objetPrototype/ex07_interfaceImplementationPrototype.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>Impl é mentation d ’ i n t e r f a c e s</ t i t l e> <!−− Cr é a t i o n de l ’ a p p l i c a t i o n v i d e avec deux mé t h o d e s −−> <s c r i p t s r c=” . . / o b j e t / e x 0 4 _ s t r u c t u r e A p p l i c a t i o n . j s ”></s c r i p t> <!−− Cr é a t i o n de sous−module r e g e x U t i l de myApp . m e t i e r −−> <s c r i p t s r c=” . . / o b j e t / ex05_modulePatternRegex . j s ”></s c r i p t> <!−− Cr é a t i o n de sous−module a d r e s s e de myApp . m e t i e r −−> <s c r i p t s r c=” . . / o b j e t / ex06_moduleMetierAdresse . j s ”></s c r i p t> <!−− Cr é a t i o n d ’ une mé thode f a b r i q u e d ’ a d r e s s e de myApp . m e t i e r . a d r e s s e −−> <s c r i p t s r c=” . / e x 0 5 _ f a b r i q u e A d r e s s e P r o t o t y p e . j s ”></s c r i p t> <!−− C l a s s e de v é r i f i c a t i o n de l ’ impl é mentation d ’ i n t e r f a c e s −−> <s c r i p t s r c=” . / e x 0 4 _ i n t e r f a c e I m p l e m e n t a t i o n . j s ”></s c r i p t> </head> <body> <s c r i p t> // Ajout d ’ une mé t h o d e mainFunction myApp . addModule ( ” mainFunction ” , function ( ) { // Dé f i n i t i o n de l ’ i n t e r f a c e commune aux i n s t a n c e s d e s modules mé t i e r ( a d r e s s e , personne , e t c . ) var metierCommonInstanceMethods = new I n t e r f a c e ( [ ” getPropertyList ” , ” getLabelText ” , ” testRegex ” , ” getProperty ” , ” se tPr op er ty ” , ” hasError ” , ” getErrorMessage ” , ” g e t E r r o r L i s t ” ] ) ; // c r é a t i o n d ’ une i n s t a n c e var monObjet = myApp . m e t i e r . a d r e s s e . c r e a t e I n s t a n c e ( { i d : ” 04 a b f 8 5 b c 9 ” , numeroRue : ”2 b i s ” , r u e : ”Rue de l ’ a Paix ” , complementAdresse : ”Bâ t i m e n t 3D” , c o d e P o s t a l : ” 63000 ” , v i l l e : ” Clermont−Ferrand ” , pays : ” France ” }) ; 54 Chapitre 3 : Constructeurs, Prototype et Patterns Associés 37 38 39 40 41 42 var t e s t I n s t a n c e I n t e r f a c e monObjet ) ; if ( testInstanceInterface document . w r i t e ( ”<p>” + } else { document . w r i t e ( ”<p>L ’ r e q u i s e s .</p>” ) ; } }) ; = metierCommonInstanceMethods . isImplementedBy ( !== true ) { t e s t I n s t a n c e I n t e r f a c e + ”</p>” ) ; o b j e t s e m b l e b i e n i m p l é menter l e s mé t h o d e s 43 44 45 46 // Exé c u t i o n de l a mé t h o d e mainFunction 47 myApp . mainFunction ( ) ; 48 </s c r i p t> 49 </body> 50 </html> 3.5 Patterns pseudo-classique (à éviter) Dans l’exemple suivant, nous créons une classe Personne qui hérite des propriétés de la classe Adresse. Pour celà : 1. le constructeur d’Adresse est appelé explicitement dans le constructeur de Personne ; 2. la classe Adresse est déclarée comme superclass de la classe Personne ; 3. Les méthodes qui existent au niveau du prototype de la classe Adresse et qui doivent être spécifiées pour des personnes sont surchargées au niveau du prototype de la classe Personne. Dans l’exemple suivant, nous surchargeons l’accesseur de la propriété ville et la méthode toString. exemples/vieux/objet.vieux/ex14_extension_de_classe.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function A d r e s s e ( numeroRue , rue , complement , c o d e P o s t a l , v i l l e ) { i f ( numeroRue . match ( /^ ( [ 0 − 9 ] * ) (([0 −9]+) (\ ?) ( ( ( b i s ) | ( t e r ) ) ) ?) $/ ) ) { t h i s . numeroRue = numeroRue . r e p l a c e ( / \ s+/g , ’ ’ ) ; } else { throw new E r r o r ( ”Numé ro de l a rue i n v a l i d e . ” ) ; } i f ( r u e . match ( /^ ( ( ( [ a−zA−Zê é è öàöÉÈÊÀÖË\ \− \.\ ,0 −9\ ] ) | ( \ ” ) ) | ( \ ’ ) ) {1 ,300} $/ ) ) { t h i s . r u e = r u e . r e p l a c e ( / \ s+/g , ’ ’ ) ; ; } else { throw new E r r o r ( ”Nom de l a rue / p l a c e i n v a l i d e . ” ) ; } i f ( complement . match ( /^ ( ( ( [ a−zA−Zê é è öàöÉÈÊÀÖË\ \− \.\ ,0 −9\ ] ) | ( \ ” ) ) | ( \ ’ ) ) {0 ,300} $/ ) ) { t h i s . complement = complement . r e p l a c e ( / \ s+/g , ’ ’ ) ; } else { 55 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 throw new E r r o r ( ” Complement d ’ a d r e s s e i n v a l i d e . ” ) ; } i f ( c o d e P o s t a l . match ( /^ [0 −9]{5} $/ ) ) { this . codePostal = codePostal ; } else { throw new E r r o r ( ”Code p o s t a l i n v a l i d e . ” ) ; } i f ( v i l l e . match ( /^ ( ( ( [ a−zA−Zê é è öàöÉÈÊÀÖË\ \− \.\ ,0 −9\ ] ) | ( \ ” ) ) | ( \ ’ ) ) {0 ,300} $/ ) ) { t h i s . v i l l e = v i l l e . r e p l a c e ( / \ s+/g , ’ ’ ) ; } else { throw new E r r o r ( ”Nom de v i l l e i n v a l i d e . ” ) ; } } A d r e s s e . p r o t o t y p e . g e t V i l l e = function ( ) { return t h i s . v i l l e ; } A d r e s s e . p r o t o t y p e . t o S t r i n g = function ( ) { var r e s u l t a t = t h i s . numeroRue ; i f ( t h i s . numeroRue != ” ” ) r e s u l t a t += ” , ” ; r e s u l t a t += t h i s . r u e + ” , ” ; r e s u l t a t += t h i s . complement ; i f ( t h i s . complement != ” ” ) r e s u l t a t += ” , ” ; r e s u l t a t += t h i s . v i l l e + ”<br />” ; return r e s u l t a t ; } function Personne (nom , prenom , numeroRue , rue , complement , c o d e P o s t a l , v i l l e ) { A d r e s s e . c a l l ( this , numeroRue , rue , complement , c o d e P o s t a l , v i l l e ) ; t h i s . nom = nom ; t h i s . prenom = prenom ; } Personne . s u p e r c l a s s = A d r e s s e ; Personne . p r o t o t y p e . g e t V i l l e = function ( ) { return A d r e s s e . p r o t o t y p e . g e t V i l l e . c a l l ( t h i s ) ; } Personne . p r o t o t y p e . t o S t r i n g = function ( ) { return t h i s . nom+” , ”+t h i s . prenom+” , ”+A d r e s s e . p r o t o t y p e . t o S t r i n g . c a l l ( t h i s ) ; } exemples/vieux/objet.vieux/ex14_extention_de_classe.html 1 2 3 4 5 6 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>Chainage de c o n s t r u c t e u r s</ t i t l e> <s c r i p t s r c=” . / e x 1 4 _ e x t e n s i o n _ d e _ c l a s s e . j s ”></s c r i p t> 56 Chapitre 3 : Constructeurs, Prototype et Patterns Associés 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 </head> <body> <p> <s c r i p t> try { var p e r s = new Personne ( ” Dujardin ” , ” Jean ” , ” 10 t e r ” , ” rue de l ’ a v e n i r ” , ’ ”Le Rastou ” ’ , ” 86098 ” , ” Les F l o t s B l e u s ” ) ; document . w r i t e ( p e r s ) ; document . w r i t e ( ”<br />L ’ a d r e s s e s e t r o u v e dans l a v i l l e de \” ”+p e r s . g e t V i l l e ( )+ ” \”. ”) ; } catch ( e r r ) { alert ( err ) ; } </s c r i p t> <p> </body> </html> 57 Chapitre 4 Interfaces Hommes Machines (IHM ) 4.1 Filtrage Basique des Inputs d’un Formulaire L’exemple suivant montre comment filtrer les attributs d’un formulaires côté client en affichant immédiatement un message d’erreur lors de la saisie d’une valeur incorrecte. On associe à chaque événement onchange de chaque attribut une fonction JavaScript qui réalisera le filtrage. exemples/gui/ex01_basicForm.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 // a l i a s v e r s l e module d ’ e x p r e s s i o n s r é g u l i è r e s var r e g e x U t i l = myApp . m e t i e r . r e g e x U t i l ; /* * * G e s t i o n n a i r e d ’ é v é nement onchange de l ’ i n p u t d ’ ID ” mainForm_titre ” . * C e t t e mé t h o d e e f f e c t u e l e f i l t r a g e par e x e p r e s s i o n r é g u l i è r e . * @method f i l t e r T i t r e */ var f i l t e r T i t r e = function ( ) { var t i t r e V a l u e = $ ( ”#mainForm_titre ” ) . v a l ( ) ; // E x p r e s s i o n s du l a n g a g e c o u r a n t e t c h i f f r e s var r e s u l t R e g e x T e s t = r e g e x U t i l . t e s t R e g e x L a t i n 1 W i t h D i g i t s ( { chaine : titreValue , minLength : 1 }) ; // M o d i f i c a t i o n du contenu du span d ’ ID ” error_mainForm_titre ” i f ( r e s u l t R e g e x T e s t === true ) { $ ( ”#error_mainForm_titre ” ) . empty ( ) ; } else { $ ( ”#error_mainForm_titre ” ) . html ( ” Erreur : l e t i t r e ne d o i t c o n t e n i r que l e s l e t t r e s e t c h i f f r e s<br />” ) ; } }; /* * * G e s t i o n n a i r e d ’ é v é nement onchange de l ’ i n p u t d ’ ID ”mainForm_resume ” . * C e t t e mé t h o d e e f f e c t u e l e f i l t r a g e par e x e p r e s s i o n r é g u l i è r e . * @function f i l t e r Titre */ var f i l t e r Resume = function ( ) { var t i t r e V a l u e = $ ( ”#mainForm_resume” ) . v a l ( ) ; // E x p r e s s i o n s du l a n g a g e c o u r a n t e t c h i f f r e s e t p o n c t u a t i o n var r e s u l t R e g e x T e s t = r e g e x U t i l . t e s t R e g e x L a t i n 1 W i t h D i g i t s P u n c t u a t i o n ( { chaine : titreValue , 58 Chapitre 4 : Interfaces Hommes Machines (IHM ) 34 35 36 37 38 39 40 41 42 43 44 minLength : 1 }) ; // M o d i f i c a t i o n du contenu du span d ’ ID ” error_mainForm_resume ” i f ( r e s u l t R e g e x T e s t === true ) { $ ( ”#error_mainForm_resume ” ) . empty ( ) ; } else { $ ( ”#error_mainForm_resume ” ) . html ( ” Erreur : l e r ésumé ne d o i t c o n t e n i r que l e s l e t t r e s e t c h i f f r e s ” + ” ou d e s c a r a c t è r e s de p o n c t u a t i o n<br />” ) ; } }; exemples/gui//ex01_basicForm.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <!doctype HTML> <html lang=” f r ”> <head> <meta charset=”UTF−8” /> <t i t l e>F i l t r a g e d ’ i n p u t s</ t i t l e> <l i nk rel=” s t y l e s h e e t ” href=” b a s i c S t y l e . c s s ” /> </head> <body> <h1>S a i s i e d ’ un f i l m</h1> <form id=”mainForm” action=” p o s t ”> <!−− i n p u t a v e c g e s t i o n n a i r e de l ’ é v é nement onchange −−> <span id=” error_mainForm_titre ” class= ” errorMsg ”></span> <l a b e l for=” mainForm_titre ”>T i t r e :</ l a b e l> <i nput type=” t e x t ” id=” mainForm_titre ” s i z e=” 15 ” placeholder=” T i t r e du f i l m ” onchange= ” f i l t e r T i t r e ( ) ” /><br /> <!−− t e x t a r e a a v e c g e s t i o n n a i r e de l ’ é v é nement onchange −−> 59 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 19 <span id=” error_mainForm_resume ” class= ” errorMsg ”></span> 20 <l a b e l for=”mainForm_resume”>Résumé :</ l a b e l> 21 <textarea id=”mainForm_resume” rows=” 10 ” cols=” 50 ” 22 placeholder=” S a i s i s s e z v o t r e r ésumé ” onchange= ” f i l t e r Resume ( ) ”></textarea> 23 24 </form> 25 <!−− I n c l u s i o n de l a s t r u c t u r e d ’ a p p l i c a t i o n e t du module r e g e x U t i l −−> 26 <s c r i p t src=” m o d u l e s M e t i e r . j s ”></s c r i p t> 27 <!−− I n c l u s i o n de jQuery pour l e s é v é nements e t m a n i p u l a t i o n du DOM −−> 28 <s c r i p t src=” j q u e r y . j s ”></s c r i p t> 29 <s c r i p t src=” e x 0 2 _ m e d i a t o r I n p u t F i l t e r . j s ”></s c r i p t> 30 <s c r i p t src=” ex01_basicForm . j s ”></s c r i p t> 31 </body> 32 </html> 4.2 Pattern Mediator pour le filtrage d’attributs L’inconvénient du filtrage présenté dans la partie 4.1 est que, dans le code HTML d’un champs du formulaire lui-même, on doit déclarer une méthode de filtrage spécifique pour ce champs (attribut onchange de l’élément HTML input ou textarea). Dans l’architecture d’application que nous proposons par la suite, la méthode de filtrage ne sera pas codée en dûr dans le module chargé de générer le formulaire, mais plutôt dans les tests d’expressions régulières effectués dans les modules métier. En particulier, la méthode précise dépendra de l’instance et de la propriété considérée, ce qui entraînerai un fort couplage (interdépendance) des méthodes chargées de l’IHM et des classes métier (ou du modèle). Nous savons par expérience que ce type de couplage va provoquer des difficultés pour la maintenance et l’évolution de notre application (comme par exemple la migration de nos objet métier côté serveur avec NodeJS). Nous allons maintenant introduire un pattern qui a pour vocation de découpler le déclenchement des événements (via, en l’occurence, des événements utilisateurs onchange) de l’implémentation des opérations correspondantes sur les données métier, ou les données du modèle. Ce pattern est une généralisation du pattern Observer. Dans notre exemple, un module Mediator va enregistrer les méthodes callbacks (qui ne sont que des fonctions JavaScript) associées à des événements. L’exécution des ces claabacks (en l’occurence la réaction à un événement onchange) sera déclenchée par la publication de l’événement en question par l’intermédiaire du Mediator. exemples/gui/ex02_mediatorInputFilter.js 1 2 /* * 3 * Ajout d ’ un module c t r l ( c o n t r ô l e u r s ) à l ’ a p p l i c a t i o n . 4 * @module c t r l 5 * @augments myApp 6 */ 7 myApp . addModule . apply (myApp, [ ” c t r l ” , { } ] ) ; 8 9 /* * 10 * Impl é me n ta ti on du p a t t e r n ”Mé d i a t o r ” pour g é r e r l e f i l t r a g e d e s i n p u t s de formulaires . 11 * @module m e d i a t o r I n p u t F i l t e r 12 * @augments myApp . c t r l 13 */ 60 Chapitre 4 : Interfaces Hommes Machines (IHM ) 14 myApp . addModule . apply (myApp . c t r l , [ ” m e d i a t o r I n p u t F i l t e r ” , function ( ) { 15 16 // ////////////////////////////////////////////// 17 // P r o p r i é t é s e t mé t h o d e s ” s t a t i q u e s ” p r i v é e s 18 19 /* * 20 * C o l l e c t i o n , i n d e x é e par ID de f o r m u l a i r e de c a l l b a c k s d ’ é v é nements l i é s à d i f f é r e n s f o r m u l a i r e s ( t y p i q u e m e n t : é v é nement onchange d ’ un i n p u t ) . 21 * @private 22 */ 23 var m _ s u b s c r i p t i o n L i s t s ; 24 25 /* * 26 * I n i t i a l i s e ( ou r é i n i t i a l i s e ) l ’ e n s e m b l e d e s l i s t e s d ’ é v é nements à l a col le cti on vide . 27 * @private 28 */ 29 var i n i t = function ( ) { 30 m _ s u b s c r i p t i o n L i s t s ={} ; 31 }; 32 33 // I n n i t i a l i s e r une f o i s l ’ e n s e m b l e d e s l i s t e s d ’ é v é nements à l a c o l l e c t i o n vide . 34 init () ; 35 36 // ////////////////////////////////////////////// 37 // I n t e r f a c e p u b l i q u e du module 38 39 /* * 40 * Cré a t i o n d ’ un o b j e t c o n t e n a n t l e s donn é e s e t mé t h o d e s p u b l i q u e s 41 * ( l e s p r o p r i é t é s p u b l i q u e s s o n t r e t o u r n é e s par l a f o n c t i o n ” module ” ) . 42 */ 43 var p u b l i c I n t e r f a c e M e d i a t o r = { 44 45 /* * 46 * A j o u t e un f o r m u l a i r e e t l a l i s t e ( i n i t i a l e m e n t v i d e ) de s e s c a l l b a c k s associ és . 47 * S i l e f o r m u l a i r e e s t d é j à g é r é , l a l i s t e de s e s c a l l b a c k s a s s o c i é s e s t supprim é e e t r é i n i t i a l i s é e à l a l i s t e v i d e . 48 * @param { s t r i n g } formId − l ’ I d du f o r m u l a i r e ( en t a n t qu ’ é l é ment HTML) 49 */ 50 addForm : function ( formId ) { 51 m _ s u b s c r i p t i o n L i s t s [ formId ] = {} 52 }, 53 54 /* * 55 * Supprime un f o r m u l a i r e e t s e s c a l l b a c k s a s s o c i é s 56 * @param { s t r i n g } formId − l ’ I d du f o r m u l a i r e ( en t a n t qu ’ é l é ment HTML) 57 */ 58 removeForm : function ( formId ) { 59 i f ( ! m _ s u b s c r i p t i o n L i s t s . hasOwnProperty ( formId ) ) { 60 return f a l s e ; 61 } 62 delete m _ s u b s c r i p t i o n L i s t s [ formId ] ; 63 return true ; 64 }, 61 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 65 66 67 /* * * Ajout d ’ un é v é nement a s s o c i é à un a t t r i b u t de f o r m u l a i r e e t de sa fonction callback . * S i l ’ é v é nement e x i s t a i t d é j à pour c e t i n p u t , i l e s t é c r a s é . * @param { s t r i n g } formId − l ’ I d du f o r m u l a i r e ( en t a n t qu ’ é l é ment HTML) * @param { s t r i n g } inputName − l e nom de l ’ i n p u t ( ou de l a p r o p r i é t é de l ’ o b j e t mé t i e r a s s o c i é ) . * @param { f u n c t i o n } c a l l b a c k F u n c t i o n − l a f o n c t i o n ( c a l l b a c k ) à a p p e l e r en c a s de p u b l i c a t i o n de l ’ é v é nement . */ s u b s c r i b e : function ( formId , inputName , c a l l b a c k F u n c t i o n ) { i f ( m _ s u b s c r i p t i o n L i s t s . hasOwnProperty ( formId ) ) { m _ s u b s c r i p t i o n L i s t s [ formId ] [ inputName ] = { c a l l b a c k : c a l l b a c k F u n c t i o n } ; } else { throw {name : ” I l l e g a l A r g u m e n t E x c e p t i o n ” , message : ” Cat é g o r i e d ’ é v é nements ” + eventCateg + ” inconnue du mé diateur ” }; } }, 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 /* * * P u b l i c a t i o n d ’ un é v é nement a s s o c i é à un a t t r i b u t de f o r m u l a i r e p r o v o q u a n t l ’ ex é c u t i o n de l a f o n c t i o n c a l l b a c k a s s o c i é e * @param { s t r i n g } formId − l ’ I d du f o r m u l a i r e ( en t a n t qu ’ é l é ment HTML) * @param { s t r i n g } inputName − l e nom de l ’ i n p u t ( ou de l a p r o p r i é t é de l ’ o b j e t mé t i e r a s s o c i é ) . */ p u b l i s h : function ( formId , inputName ) { 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 i f ( m _ s u b s c r i p t i o n L i s t s . hasOwnProperty ( formId ) ) { i f ( m _ s u b s c r i p t i o n L i s t s [ formId ] . hasOwnProperty ( inputName ) ) { // On a p p e l l e l e c a l l b a k a v e c son m _ s u b s c r i p t i o n L i s t s [ formId ] [ inputName ] . c a l l b a c k ( ) ; } } else { throw {name : ” I l l e g a l A r g u m e n t E x c e p t i o n ” , message : ” For mul aire d ’ ID ” + formId + ” inconnu du mé d i a t e u r ” }; } }, /* * * Ré i n i t i a l i s e l a c o l l e c t i o n d e s f o r m u l a i r e s g é r é s à une c o l l e c t i o n v i d e . */ empty : function ( ) { init () ; } }; return p u b l i c I n t e r f a c e M e d i a t o r ; }() ] ) ; exemples/gui/ex02_mediatorInputFilter.html 62 Chapitre 4 : Interfaces Hommes Machines (IHM ) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>F i l t r a g e d ’ i n p u t s</ t i t l e> <l i nk r e l=” s t y l e s h e e t ” h r e f=” b a s i c S t y l e . c s s ” /> </head> <body> <h1>S a i s i e d ’ un f i l m</h1> <form i d=”mainForm” a c t i o n=” p o s t ”> <!−− i n p u t avec g e s t i o n n a i r e de l ’ é v é nement onchange −−> <span i d=” error_mainForm_titre ” c l a s s=” errorMsg ”></span> <l a b e l for=” mainForm_titre ”>T i t r e :</ l a b e l> <i nput type=” t e x t ” i d=” mainForm_titre ” s i z e=” 15 ” p l a c e h o l d e r=” T i t r e du f i l m ” onchange=” f i l t e r Data ( ’ mainForm ’ , ’ t i t r e ’ ) ” /><b r /> <!−− t e x t a r e a avec g e s t i o n n a i r e de l ’ é v é nement onchange −−> <span i d=” error_mainForm_resume ” c l a s s=” errorMsg ”></span> <l a b e l for=”mainForm_resume”>Résumé :</ l a b e l> <textarea i d=”mainForm_resume” rows=” 10 ” c o l s=” 50 ” p l a c e h o l d e r=” S a i s i s s e z v o t r e r ésumé ” onchange=” f i l t e r Data ( ’ mainForm ’ , ’ resume ’ ) ”></textarea> </form> <!−− I n c l u s i o n de l a s t r u c t u r e d ’ a p p l i c a t i o n e t du module r e g e x U t i l −−> <s c r i p t s r c=” m o d u l e s M e t i e r . j s ”></s c r i p t> <!−− I n c l u s i o n de jQuery pour l e s é v é nements e t m a n i p u l a t i o n du DOM −−> <s c r i p t s r c=” j q u e r y . j s ”></s c r i p t> <s c r i p t s r c=” e x 0 2 _ m e d i a t o r I n p u t F i l t e r . j s ”></s c r i p t> <s c r i p t s r c=” ex01_basicForm . j s ”></s c r i p t> <s c r i p t> // Ajout du f o r m u l a i r e ”mainForm” au mé d i a t e u r q u i g è r e r a s e s é v é nements . myApp . c t r l . m e d i a t o r I n p u t F i l t e r . addForm ( ’ mainForm ’ ) ; // E n r e g i s t r e m e n t du c a l l b a c k a s s c o c i é à l ’ é v é nement onchange du t i t r e myApp . c t r l . m e d i a t o r I n p u t F i l t e r . s u b s c r i b e ( ’ mainForm ’ , ’ t i t r e ’ , f i l t e r T i t r e ) ; // E n r e g i s t r e m e n t du c a l l b a c k a s s c o c i é à l ’ é v é nement onchange du r ésumé myApp . c t r l . m e d i a t o r I n p u t F i l t e r . s u b s c r i b e ( ’ mainForm ’ , ’ resume ’ , f i l t e r Resume ) ; /* * * P u b l i e l ’ é v é nement onchange d ’ un i n p u t a u p r è s du mé d i a t e u r , p r o v o q u a n t l ’ ex é c u t i o n du c a l l b a c k e n r e g i s t r é pour c e t é v é nement . * @ f u n c t i o n f i l t e r Data */ var f i l t e r Data = function ( formId , inputName ) { myApp . c t r l . m e d i a t o r I n p u t F i l t e r . p u b l i s h ( formId , inputName ) ; }; </s c r i p t> </body> </html> 63 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 4.3 Exemple : génération automatique de formulaire d’adresse 4.3.1 Avec l’interface d’objets métier sans prototype Dans l’exemple suivant, des méthodes d’un module myApp.gui permettent de générer automatiquement les inputs d’un formulaire permettant de saisir les propriétés (ici supposées de type texte) d’un objte qui implémente des interface qui apparaissent dans l’exemple de la partie 3.3. Nous appliquons cette méthode pour afficher et filtrer automatiquement un formulaire de saisie d’une adresse. exemples/gui/ex03_formsGui.js 1 // Ajout d ’ un module ” g u i ” à n o t r e a p p l i c a t i o n myApp 2 myApp . addModule . apply (myApp, [ ” g u i ” , { } ] ) ; 3 4 /* * 5 * F on ct i o n de g éné r a t i o n de l ’ ID d ’ un é l é ment HTML de t y p e i n p u t pr é f i x é par l ’ ID du f o r m u l a i r e 6 * @method myApp . g u i . g e t I n p u t I d 7 * @param { O b j e c t } i n p u t S p e c c o n t i e n t l e s sp é c i f i c a t i o n s de l ’ i n p u t 8 * @param { s t r i n g } i n p u t S p e c . formId i d du f o r m u l a i r e dans l e q u e l l ’ i n p u t s e r a ins éré 9 * @param { s t r i n g } i n p u t S p e c . propertyName nom de l a p r o p r i é t é de i n p u t S p e c . o b j e t M e t i e r à s a i s i r dans l ’ i n p u t 10 */ 11 myApp . addModule . apply (myApp . gui , [ ” g e t I n p u t I d ” , function ( i n p u t S p e c ) { 12 return i n p u t S p e c . formId + ”_” + i n p u t S p e c . propertyName ; 13 } ] ) ; 14 15 /* * 16 * P u b l i e a u p r è s du Mediator un é v é nement onchange d ’ un I n p u t 17 * @param { s t r i n g } formId i d du f o r m u l a i r e dans l e q u e l l ’ i n p u t s e r a i n s é r é 18 * @param { s t r i n g } propertyName nom de l a p r o p r i é t é de i n p u t S p e c . o b j e t M e t i e r à s a i s i r dans l ’ i n p u t 19 */ 20 myApp . addModule . apply (myApp . gui , [ ” p u b l i s h I n p u t C h a n g e ” , function ( formId , propertyName ) { 21 myApp . c t r l . m e d i a t o r I n p u t F i l t e r . p u b l i s h ( formId , propertyName ) ; 22 } ] ) ; 23 24 /* * 25 * Géné r a t i o n du code HTML d ’ un i n p u t . 26 * @method myApp . g u i . g e t T e x t I n p u t C o d e 27 * @param { O b j e c t } i n p u t S p e c c o n t i e n t l e s sp é c i f i c a t i o n s de l ’ i n p u t 28 * @param { O b j e c t } i n p u t S p e c . o b j e t M e t i e r i n t a n c e d ’ un module mé t i e r ( par exemple i n s t a n c e d ’ a d r e s s e , de per son n e . . . ) . 29 * c e t o b j e t d o i t i m p l é menter d e s i n t e r f a c e s pr é c i s e s ( g e t P r o p e r t y ( ) , getModule ( ) , e t c . ) 30 * @param { s t r i n g } i n p u t S p e c . formId i d du f o r m u l a i r e dans l e q u e l l ’ i n p u t s e r a ins éré 31 * @param { s t r i n g } i n p u t S p e c . propertyName nom de l a p r o p r i é t é de i n p u t S p e c . o b j e t M e t i e r à s a i s i r dans l ’ i n p u t 32 * @param { s t r i n g } [ i n p u t S p e c . t y p e=t e x t ] t y p e de l ’ i n p u t 33 * @param {number} [ i n p u t S p e c . i n p u t S i z e =10] t a i l l e de l ’ i n p u t ( nombre de caractères ) 34 */ 64 Chapitre 4 : Interfaces Hommes Machines (IHM ) 35 myApp . addModule . apply (myApp . gui , [ ” g e t I n p u t C o d e ” , function ( i n p u t S p e c ) { 36 // C a l c u l de l ’ ID de l ’ i n p u t : 37 var i n p u t I d = myApp . g u i . g e t I n p u t I d ( i n p u t S p e c ) ; 38 39 // Valeur de l a p r o p r i é t é de l ’ o b j e t pour l ’ a t t r i b u t v a l u e de l ’ i n p u t 40 var p r o p e r t y V a l u e = i n p u t S p e c . o b j e t M e t i e r . g e t P r o p e r t y ( i n p u t S p e c . propertyName ) | | ”” ; 41 // Cré a t i o n d ’ un é v e n t u e l message s i l ’ o b j e t c o m p o r t a i t d é j à une e r r e u r 42 var e r r o r M e s s a g e = i n p u t S p e c . o b j e t M e t i e r . g e t E r r o r M e s s a g e ( i n p u t S p e c . propertyName ) !== u n d e f i n e d 43 ? i n p u t S p e c . o b j e t M e t i e r . g e t E r r o r M e s s a g e ( i n p u t S p e c . propertyName ) + ”<br />” : ” ” ; 44 45 var moduleMetier = i n p u t S p e c . o b j e t M e t i e r . getModule ( ) ; // r a c c o u r c i 46 47 // //////////////////////////////////////////////////////////////////// 48 // C a l l b a c k de g e s t i o n du f i l t r a g e de l ’ i n p u t : 49 myApp . c t r l . m e d i a t o r I n p u t F i l t e r . s u b s c r i b e ( i n p u t S p e c . formId , i n p u t S p e c . propertyName , function ( ) { 50 51 // S i aucun t e s t d ’ e x p r e s s i o n r é g u l i è r e n ’ e s t pr é vu 52 i f ( moduleMetier === u n d e f i n e d | | 53 moduleMetier . t e s t R e g e x === u n d e f i n e d ) { 54 return true ; // a c c e p t e r l a v a l e u r 55 } 56 57 var r e s u l t a t T e s t R e g e x = moduleMetier . t e s t R e g e x ( i n p u t S p e c . propertyName , 58 document . getElementById ( i n p u t I d ) . v a l u e ) ; 59 i f ( r e s u l t a t T e s t R e g e x !== true ) { 60 document . getElementById ( ” error_ ”+i n p u t I d ) . innerHTML = r e s u l t a t T e s t R e g e x +”<br />” ; 61 } else { 62 document . getElementById ( ” error_ ”+i n p u t I d ) . innerHTML = ” ” ; 63 } 64 } ) ; // f i n du c a l l b a c k ////////////////////////////////////////////// 65 66 var inputType = i n p u t S p e c . inputType === u n d e f i n e d ? ” t e x t ” : i n p u t S p e c . inputType ; 67 var i n p u t S i z e = i n p u t S p e c . i n p u t S i z e === u n d e f i n e d ? ” 15 ” : i n p u t S p e c . inputSize ; 68 var l a b e l T e x t = moduleMetier . g e t L a b e l T e x t ( i n p u t S p e c . propertyName ) ; 69 // r e t o u r du code HTML de l ’ i n p u t 70 return ”<span c l a s s =\”errorMsg \” i d =\”error_ ”+i n p u t I d+” \”>” + e r r o r M e s s a g e + ”</span>” + 71 ”<l a b e l f o r =\”” + i n p u t S p e c . propertyName + ” \”>” + l a b e l T e x t + ”</ l a b e l> ” + 72 ”<i nput t y p e =\”” + inputType + ” \” name=\”” + i n p u t S p e c . propertyName + 73 ” \” i d =\”” + i n p u t I d + ” \” ” + ” v a l u e =\”” + p r o p e r t y V a l u e + ” \” ” + 74 ” s i z e =\”” + i n p u t S i z e + ” \” ” + 75 ” onchange=\”myApp . g u i . p u b l i s h I n p u t C h a n g e ( ’ ” + i n p u t S p e c . formId + ” ’ , ’ ” + i n p u t S p e c . propertyName + ” ’ ) \” ” + ”/>” ; 76 } ] ) ; 77 78 /* * 79 * Géné r a t i o n du code HTML de l ’ e n s e m b l e d e s i n p u t s d ’ un f o r m u l a i r e . 65 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 * @method myApp . g u i . getHtmlFormInputs * @param { O b j e c t } o b j e t M e t i e r i n t a n c e d ’ un module mé t i e r ( par exemple i n s t a n c e d ’ a d r e s s e , de pe r so nn e . . . ) . * c e t o b j e t d o i t i m p l é menter d e s i n t e r f a c e s pr é c i s e s ( g e t P r o p e r t y ( ) , getModule ( ) , e t c . ) * @param { s t r i n g } formId i d du f o r m u l a i r e dans l e q u e l l ’ i n p u t s e r a i n s é r é * @retuern { s t r i n g } l e code HTML d e s t o u s l e s i n p u t s c o r r e s p o n d a n t aux p r o p r i é t é s de l ’ o b j e t mé t i e r . */ myApp . addModule . apply (myApp . gui , [ ” getHtmlFormInputs ” , function ( o b j e t M e t i e r , formId ) { // Dé f i n i t i o n de l ’ i n t e r f a c e commune aux modules mé t i e r ( a d r e s s e , personne , etc .) var metierCommonMethods = new I n t e r f a c e ( [ ” getPropertyList ” , ” getLabelText ” , ” testRegex ” , ” createInstance ” ] ) ; // Dé f i n i t i o n de l ’ i n t e r f a c e commune aux i n s t a n c e s d e s modules mé t i e r ( a d r e s s e , personne , e t c . ) var metierCommonInstanceMethods = new I n t e r f a c e ( [ ” getModule ” , ” g e t P r o p e r t y ” , ” s e t P r o p e r t y ” , ” h a s E r r o r ” , ” g e t E r r o r M e s s a g e ” , ” getErrorList ” ]) ; var t e s t I n t e r f a c e = metierCommonInstanceMethods . isImplementedBy ( o b j e t M e t i e r ) ; var message ; i f ( t e s t I n t e r f a c e !== true ) { message = t e s t I n t e r f a c e ; } else { message = metierCommonMethods . isImplementedBy ( o b j e t M e t i e r . getModule ( ) ) ; i f ( message !== true ) { throw new E r r o r ( message ) ; } } // Ajour du f o r m u l a i r e ”mainForm” au mé d i a t e u r q u i g è r e r a s e s é v é nements . myApp . c t r l . m e d i a t o r I n p u t F i l t e r . addForm ( formId ) ; var htmlCode = ” ” ; var p r o p e r t y L i s t = o b j e t M e t i e r . getModule ( ) . g e t P r o p e r t y L i s t ( ) ; // Tous l e s i n p u t s s o n t de t y p e t e x t e , donc on p e u t // f a i r e une b o u c l e a u t o m a t i q u e s u r l e s p r o p r i é t é s . fo r ( var i =0 ; i < p r o p e r t y L i s t . l e n g t h ; i ++){ var propertyName = p r o p e r t y L i s t [ i ] ; // l ’ u t i l i s a t e u r ne p e u t pas m o d i f i e r l ’ ID : i f ( propertyName != ” i d ” ) { // Concat é n a t i o n du code HTML de l ’ i n p u t htmlCode += myApp . g u i . getInputCode ( { objetMetier : objetMetier , propertyName : p r o p e r t y L i s t [ i ] , formId : formId } ) + ”<br />” ; } } return htmlCode ; }]) ; 66 Chapitre 4 : Interfaces Hommes Machines (IHM ) exemples/gui//ex03_formsGui.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <!doctype HTML> <html lang=” f r ”> <head> <meta charset=”UTF−8” /> <t i t l e>F i l t r a g e d ’ i n p u t s</ t i t l e> <l i nk rel=” s t y l e s h e e t ” href=” b a s i c S t y l e . c s s ” /> </head> <body> <h1>S a i s i e d ’ une a d r e s s e</h1> <!−− I n c l u s i o n de l a s t r u c t u r e d ’ a p p l i c a t i o n e t du module r e g e x U t i l −−> <s c r i p t src=” m o d u l e s M e t i e r . j s ”></s c r i p t> <!−− I n c l u s i o n de jQuery pour l e s é v é nements e t m a n i p u l a t i o n du DOM −−> <s c r i p t src=” j q u e r y . j s ”></s c r i p t> <s c r i p t src=” e x 0 2 _ m e d i a t o r I n p u t F i l t e r . j s ”></s c r i p t> <s c r i p t src=” ex03_formsGui . j s ”></s c r i p t> <s c r i p t src=” . / e x 1 1 _ i n t e r f a c e I m p l e m e n t a t i o n . j s ”></s c r i p t> <s c r i p t> // Ajout d ’ une mé t h o d e mainFunction myApp . addModule ( ” mainFunction ” , function ( ) { // c r é a t i o n d ’ une i n s t a n c e var a d r e s s e = myApp . m e t i e r . a d r e s s e . c r e a t e I n s t a n c e ( { id : ” 04 a b f 8 5 b c 9 ” , numeroRue : ”2 bis@ ” , r u e : ”Rue de l ’ a Paix ” , complementAdresse : ”Bâ t i m e n t 3D” , c o d e P o s t a l : ” 63000 ” , v i l l e : ” Clermont−Ferrand ” , pays : ” France ” }) ; // Géné r a t i o n du f o r m u l a i r e a v e c l e s c a l l b a c k s document . w r i t e ( ”<form id= \”mainForm\” method= \” p o s t \”>” + myApp . g u i . getHtmlFormInputs ( a d r e s s e , ”mainForm” ) + ”<l a b e l></ l a b e l><i nput type= \” s u b m i t \” value= \” v a l i d e r \” />” + 67 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 37 ”</form>” ) ; 38 }) ; 39 40 // Exé c u t i o n de l a mé t h o d e mainFunction 41 myApp . mainFunction ( ) ; 42 </s c r i p t> 43 </body> 44 </html> 4.3.2 Avec l’interface d’objets métier utilisant le prototype En utilisant le fabrique d’instances d’objets métier de la partie 3.4, le code de génération du formulaire est un peu plus simple (une seule interface à tester et suppression des appels de la méthode getModule : exemples/gui/ex04_formsGuiPrototype.js 1 // Ajout d ’ un module ” g u i ” à n o t r e a p p l i c a t i o n myApp 2 myApp . addModule . apply (myApp, [ ” g u i ” , { } ] ) ; 3 4 /* * 5 * F on ct i o n de g éné r a t i o n de l ’ ID d ’ un é l é ment HTML de t y p e i n p u t pr é f i x é par l ’ ID du f o r m u l a i r e 6 * @method myApp . g u i . g e t I n p u t I d 7 * @param { O b j e c t } i n p u t S p e c c o n t i e n t l e s sp é c i f i c a t i o n s de l ’ i n p u t 8 * @param { s t r i n g } i n p u t S p e c . formId i d du f o r m u l a i r e dans l e q u e l l ’ i n p u t s e r a ins éré 9 * @param { s t r i n g } i n p u t S p e c . propertyName nom de l a p r o p r i é t é de i n p u t S p e c . o b j e t M e t i e r à s a i s i r dans l ’ i n p u t 10 */ 11 myApp . addModule . apply (myApp . gui , [ ” g e t I n p u t I d ” , function ( i n p u t S p e c ) { 12 return i n p u t S p e c . formId + ”_” + i n p u t S p e c . propertyName ; 13 } ] ) ; 14 15 /* * 16 * P u b l i e a u p r è s du Mediator un é v é nement onchange d ’ un I n p u t 17 * @param { s t r i n g } formId i d du f o r m u l a i r e dans l e q u e l l ’ i n p u t s e r a i n s é r é 18 * @param { s t r i n g } propertyName nom de l a p r o p r i é t é de i n p u t S p e c . o b j e t M e t i e r à s a i s i r dans l ’ i n p u t 19 */ 20 myApp . addModule . apply (myApp . gui , [ ” p u b l i s h I n p u t C h a n g e ” , function ( formId , propertyName ) { 21 myApp . c t r l . m e d i a t o r I n p u t F i l t e r . p u b l i s h ( formId , propertyName ) ; 22 } ] ) ; 23 24 /* * 25 * Géné r a t i o n du code HTML d ’ un i n p u t . 26 * @method myApp . g u i . g e t T e x t I n p u t C o d e 27 * @param { O b j e c t } i n p u t S p e c c o n t i e n t l e s sp é c i f i c a t i o n s de l ’ i n p u t 28 * @param { O b j e c t } i n p u t S p e c . o b j e t M e t i e r i n t a n c e d ’ un module mé t i e r ( par exemple i n s t a n c e d ’ a d r e s s e , de per son n e . . . ) . 29 * c e t o b j e t d o i t i m p l é menter d e s i n t e r f a c e s pr é c i s e s ( g e t P r o p e r t y ( ) , getModule ( ) , e t c . ) 30 * @param { s t r i n g } i n p u t S p e c . formId i d du f o r m u l a i r e dans l e q u e l l ’ i n p u t s e r a ins éré 68 Chapitre 4 : Interfaces Hommes Machines (IHM ) 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 * @param { s t r i n g } i n p u t S p e c . propertyName nom de l a p r o p r i é t é de i n p u t S p e c . o b j e t M e t i e r à s a i s i r dans l ’ i n p u t * @param { s t r i n g } [ i n p u t S p e c . t y p e=t e x t ] t y p e de l ’ i n p u t * @param {number} [ i n p u t S p e c . i n p u t S i z e =10] t a i l l e de l ’ i n p u t ( nombre de caractères ) */ myApp . addModule . apply (myApp . gui , [ ” g e t I n p u t C o d e ” , function ( i n p u t S p e c ) { // C a l c u l de l ’ ID de l ’ i n p u t : var i n p u t I d = myApp . g u i . g e t I n p u t I d ( i n p u t S p e c ) ; // Valeur de l a p r o p r i é t é de l ’ o b j e t pour l ’ a t t r i b u t v a l u e de l ’ i n p u t var p r o p e r t y V a l u e = i n p u t S p e c . o b j e t M e t i e r . g e t P r o p e r t y ( i n p u t S p e c . propertyName ) | | ”” ; // Cré a t i o n d ’ un é v e n t u e l message s i l ’ o b j e t c o m p o r t a i t d é j à une e r r e u r var e r r o r M e s s a g e = i n p u t S p e c . o b j e t M e t i e r . g e t E r r o r M e s s a g e ( i n p u t S p e c . propertyName ) !== u n d e f i n e d ? i n p u t S p e c . o b j e t M e t i e r . g e t E r r o r M e s s a g e ( i n p u t S p e c . propertyName ) + ”<br />” : ” ” ; // //////////////////////////////////////////////////////////////////// // C a l l b a c k de g e s t i o n du f i l t r a g e de l ’ i n p u t : myApp . c t r l . m e d i a t o r I n p u t F i l t e r . s u b s c r i b e ( i n p u t S p e c . formId , i n p u t S p e c . propertyName , function ( ) { var r e s u l t a t T e s t R e g e x = i n p u t S p e c . o b j e t M e t i e r . t e s t R e g e x ( i n p u t S p e c . propertyName , document . getElementById ( i n p u t I d ) . v a l u e ) ; i f ( r e s u l t a t T e s t R e g e x !== true ) { document . getElementById ( ” error_ ”+i n p u t I d ) . innerHTML = r e s u l t a t T e s t R e g e x +”<br />” ; } else { document . getElementById ( ” error_ ”+i n p u t I d ) . innerHTML = ” ” ; } } ) ; // f i n du c a l l b a c k ////////////////////////////////////////////// var inputType = i n p u t S p e c . inputType === u n d e f i n e d ? ” t e x t ” : i n p u t S p e c . inputType ; var i n p u t S i z e = i n p u t S p e c . i n p u t S i z e === u n d e f i n e d ? ” 10 ” : i n p u t S p e c . i n p u t S i z e ; var l a b e l T e x t = i n p u t S p e c . o b j e t M e t i e r . g e t L a b e l T e x t ( i n p u t S p e c . propertyName ) ; // r e t o u r du code HTML de l ’ i n p u t return ”<span c l a s s =\”errorMsg \” i d =\”error_ ”+i n p u t I d+” \”>” + e r r o r M e s s a g e + ” </span>” + ”<l a b e l f o r =\”” + i n p u t S p e c . propertyName + ” \”>” + l a b e l T e x t + ”</ l a b e l>” + ”<i nput t y p e =\”” + inputType + ” \” name=\”” + i n p u t S p e c . propertyName + ” \” i d =\”” + i n p u t I d + ” \” ” + ” v a l u e =\”” + p r o p e r t y V a l u e + ” \” ” + ” s i z e =\”” + i n p u t S i z e + ” \” ” + ” onchange=\”myApp . g u i . p u b l i s h I n p u t C h a n g e ( ’ ” + i n p u t S p e c . formId + ” ’ , ’ ” + i n p u t S p e c . propertyName + ” ’ ) \” ” + ” />” ; }]) ; /* * * Géné r a t i o n du code HTML de l ’ e n s e m b l e d e s i n p u t s d ’ un f o r m u l a i r e . 69 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 * @method myApp . g u i . getHtmlFormInputs * @param { O b j e c t } o b j e t M e t i e r i n t a n c e d ’ un module mé t i e r ( par exemple i n s t a n c e d ’ a d r e s s e , de pe r so nn e . . . ) . * c e t o b j e t d o i t i m p l é menter d e s i n t e r f a c e s pr é c i s e s ( g e t P r o p e r t y ( ) , getModule ( ) , e t c . ) * @param { s t r i n g } formId i d du f o r m u l a i r e dans l e q u e l l ’ i n p u t s e r a i n s é r é * @retuern { s t r i n g } l e code HTML d e s t o u s l e s i n p u t s c o r r e s p o n d a n t aux p r o p r i é t é s de l ’ o b j e t mé t i e r . */ myApp . addModule . apply (myApp . gui , [ ” getHtmlFormInputs ” , function ( o b j e t M e t i e r , formId ) { // Dé f i n i t i o n de l ’ i n t e r f a c e commune aux i n s t a n c e s d e s modules mé t i e r ( a d r e s s e , personne , e t c . ) var metierCommonInstanceMethods = new I n t e r f a c e ( [ ” getPropertyList ” , ” getLabelText ” , ” testRegex ” , ” getProperty ” , ” setP ro pe rty ” , ” hasError ” , ” getErrorMessage ” , ” g e t E r r o r L i s t ” ] ) ; var t e s t I n t e r f a c e = metierCommonInstanceMethods . isImplementedBy ( o b j e t M e t i e r ) ; var message ; i f ( t e s t I n t e r f a c e !== true ) { throw new E r r o r ( t e s t I n t e r f a c e ) ; } // Ajour du f o r m u l a i r e ”mainForm” au mé d i a t e u r q u i g è r e r a s e s é v é nements . myApp . c t r l . m e d i a t o r I n p u t F i l t e r . addForm ( formId ) ; var htmlCode = ” ” ; var p r o p e r t y L i s t = o b j e t M e t i e r . g e t P r o p e r t y L i s t ( ) ; // Tous l e s i n p u t s s o n t de t y p e t e x t e , donc on p e u t // f a i r e une b o u c l e a u t o m a t i q u e s u r l e s p r o p r i é t é s . fo r ( var i =0 ; i < p r o p e r t y L i s t . l e n g t h ; i ++){ var propertyName = p r o p e r t y L i s t [ i ] ; // l ’ u t i l i s a t e u r ne p e u t pas m o d i f i e r l ’ ID : i f ( propertyName != ” i d ” ) { // Concat é n a t i o n du code HTML de l ’ i n p u t htmlCode += myApp . g u i . getInputCode ( { objetMetier : objetMetier , propertyName : p r o p e r t y L i s t [ i ] , labelText : objetMetier . getLabelText ( propertyList [ i ] ) , formId : formId } ) + ”<br />” ; } } // champs cach é r e p r é s e n t a n t l ’ ID de l ’ i n s t a n c e htmlCode += ”<i nput t y p e =\” h i d d e n \” i d =\”” + formId + ” _id \” v a l u e =\”” + o b j e t M e t i e r . g e t P r o p e r t y ( ” i d ” ) + ” \” />” ; return htmlCode ; }]) ; 70 Chapitre 5 Exemple d’Application Interactive 5.1 Principe de l’application et analyse fonctionnelle Notre application, qui possède un modèle constitué d’une collection de personnes, permet (voir les storyboards sur la figure 5.1) : • D’afficher la liste des noms de personnes (items) ; • De sélectionner une personne en cliquant sur l’item correspondant (l’item est alors surligné et les détails concernant cette personne sont affichés) ; • De modifier les données de la personnes (en l’occurence le nom) en cliquant sur un bouton ”Modifier”. • D’ajouter une personne ; • De supprimer la personne sélectionnée. • d’ajouter, de supprimer ou de modifier une adresse pour la personne sélectionnée. Comme on peut le voir, nous avons une agrégation entre les personnes et les adresses, une personne pouvant avoir plusieurs adresses. En recenssant les événements (clics de boutons d’items, liens) possibles sur les storyboards de la figure 5.1, on dresse le diagramme de cas d’utilisation représenté sur la figure 5.2. 5.2 Modèle de donnée Dans notre modèle de données, une classe personne comporte un nom et une composition avec des instances d’adresse. Nous créons, pour le moment, quelques instances ”en dur”, dans un tableau personnes, avec chacune une adresse. Une autre propriété selectedPersonne contient une référence vers l’instance de personne sélectionnée (item surligné et détails affichés). exemples/ihm/ex00_modelModule.js 1 2 myApp . addModule . apply (myApp, [ ” modele ” , { 3 s e l e c t e d P e r s o n n e : null , 4 personnes : [ ] , 71 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery (a) Sélection d’une personne (item surligné à gauche) (b) Ajout d’une adresse pour la personne sélectionnée (c) Ajout d’une personne (d) Après ajout d’une personne Figure 5.1 : Captures d’écran de notre application 72 Chapitre 5 : Exemple d’Application Interactive Figure 5.2 : Diagramme de cas d’utilisation de notre application avec son IHM 5 }]) ; 6 7 myApp . modele . p e r s o n n e s . push (myApp . m e t i e r . p e r s o n n e . c r e a t e I n s t a n c e ( { 8 i d : ” 0123 a b c d e f ” , 9 nom : ” Toto l e Hé ro ” , 10 a d r e s s e : myApp . m e t i e r . a d r e s s e . c r e a t e I n s t a n c e ( { 11 i d : ” 04 a b f 8 5 b c 9 ” , 12 numeroRue : ”2 b i s ” , 13 r u e : ”Rue de l ’ a Paix ” , 14 complementAdresse : ” D a l l e ” , 15 c o d e P o s t a l : ” 630000 ” , 16 v i l l e : ” Clermont−Ferrand ” , 17 pays : ” France 2 ” 18 }) 19 }) ) ; 20 21 myApp . modele . p e r s o n n e s . push (myApp . m e t i e r . p e r s o n n e . c r e a t e I n s t a n c e ( { 22 i d : ” 0123 abcd12 ” , 23 nom : ” T i t i l e t o u t p ’ t i t ” , 24 a d r e s s e : myApp . m e t i e r . a d r e s s e . c r e a t e I n s t a n c e ( { 25 i d : ” 04 a b f 8 5 b b 5 ” , 26 numeroRue : ”4 t e r ” , 27 r u e : ”Rue de l ’ e n f e r ” , 28 complementAdresse : ” c ’ e s t l e s a u t r e s . . . ” , 29 c o d e P o s t a l : ” 75000 ” , 30 v i l l e : ” Ris pas ” , 31 pays : ” France ” 32 }) 73 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 33 }) ) ; 34 35 myApp . modele . p e r s o n n e s . push (myApp . m e t i e r . p e r s o n n e . c r e a t e I n s t a n c e ( { 36 i d : ” 0123 abcd01 ” , 37 nom : ” Toutou c ’ e s t par où ” , 38 a d r e s s e : myApp . m e t i e r . a d r e s s e . c r e a t e I n s t a n c e ( { 39 i d : ” 04 a b f 8 5 b a 4 ” , 40 numeroRue : ”1” , 41 r u e : ” P l a c e de l ’ A l t e r n a t i v e ” , 42 complementAdresse : ” Pourquoi pas ” , 43 c o d e P o s t a l : ” 63123 ” , 44 v i l l e : ” Les Paumiers ” , 45 pays : ” France ” 46 }) 47 }) ) ; 5.3 Pattern Mediator : centraliser les événements Notre module mediator va nous permettre : • De découpler l’implémentation de la réaction aux événements utilisateurs (modification du modèle, mise à jour des vues) de la gestion de ces événements utilisateurs via la technologie jQuery, qui, de ce fait, se trouve circonscrite à une seule classe (Wrapper). • D’éliminer les dépendances cycliques entre les modules de notre application ; • De recenser les événements utilisateurs de manière lisible dans un module centralisé ; • De provoquer des mises à jour de panneaux de la vue qui observent des propriétés du modèle. Contrairement au médiateur spécialisé dans le filtrage des attributs de formulaires décrit dans la partie 4.2, le module mediator va nous permettre d’exécuter plusieurs callback en réaction à un même événement (par exemple pour mettre à jour différentes parties de la vue après une modification du modèle). exemples/ihm/ex01_mediator.js 1 2 /* * * Impl é me n ta ti on du p a t t e r n ”Mé d i a t o r ” pour l a g e s t i o n d e s é v é nements utilisateurs , 3 * e t l a mise à j o u r d e s v u e s ( ou d e s sous−a r b r e s du DOM) 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” m e d i a t o r ” , function ( ) { 6 7 /* * 8 * L i s t e d e s é v é nements pour l e s q u e l s une l i s t e de c a l l b a c k s p e u t ê t r e enregistr ée 9 * @private 10 */ 11 var m _ s u b s c r i p t i o n L i s t s ; 12 13 /* * 74 Chapitre 5 : Exemple d’Application Interactive 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 * I n i t i a l i s e l a l i s t e d e s é v é nements , a v e c pour chacun , une l i s t e de callbacks vide . * @method i n i t */ var i n i t = function ( ) { m_subscriptionLists = { // Opé r a t i o n s CRUD s u r l e s p e r s o n n e s ” p er son ne / re ad ” : [ ] , // L i r e t o u t e s l e s p e r s o n n e s pour ( r e ) o n s t r u i r e l e modèle ” p er son ne / u p d a t e ” : [ ] , // v a l i d a t i o n du f o r m u l a i r e de mise à j o u r de l a pe rs o nn e s é l e c t i o n n é e . ” p er son ne / c r e a t e ” : [ ] , // v a l i d a t i o n du f o r m u l a i r e d ’ a j o u t d ’ une p e r s o nn e . ” p er son ne / d e l e t e ” : [ ] , // S u p p r e s s i o n d ’ une pe r s o n n e // Opé r a t i o n s CRUD s u r l e s a d r e s s e s ” a d r e s s e / c r e a t e ” : [ ] , // v a l i d a t i o n du f o r m u l a i r e d ’ a j o u t d ’ une a d r e s s e . ” a d r e s s e / u p d a t e ” : [ ] , // mise à j o u r d ’ une a d r e s s e ” a d r e s s e / d e l e t e ” : [ ] , // S u p p r e s s i o n d ’ une a d r e s s e // A c t i o n s U t i l i s a t e u r donnant l i e u à un changement de l e vue ” p er son ne / s e l e c t D e t a i l s ” : [ ] , // Sé l e c t i o n d ’ une p e r s o n n e pour v o i r l e s d é tails ” p er son ne / e d i t ” : [ ] , // c l i c k s u r l a m o d i f i c a t i o n de l a p e r s o n n e s é lectionn ée ” p er son ne / s a i s i e ” : [ ] , // c l i c k s u r l a m o d i f i c a t i o n de l a pe r s o n n e s é lectionn ée ” a d r e s s e / e d i t ” : [ ] , // S u p p r e s s i o n d ’ une a d r e s s e ” a d r e s s e / s a i s i e ” : [ ] , // c l i c k s u r l a m o d i f i c a t i o n de l a p e r s o n n e s é lectionn ée // N o t i f i c a t i o n s de m o d i f i c a t i o n du modèle pour r e q u ê t e AJAX e t /ou mise à j o u r de l a vue ” p er son ne / changed ” : [ ] , // mise à j o u r d ’ une p e r s o n n e ” p er son ne / c r e a t e d ” : [ ] , // mise à j o u r d ’ une p e r s o n n e ” p er son ne / d e t a i l s C h a n g e d ” : [ ] , // Mise à j o u r r e q u i s e du panneau d e s d é tails // N o t i f i c a t i o n s de m o d i f i c a t i o n du modèle pour r e q u ê t e AJAX e t /ou mise à j o u r de l a vue ” a d r e s s e / changed ” : [ ] , // mise à j o u r d ’ une a d r e s s e ” a d r e s s e / c r e a t e d ” : [ ] , // mise à j o u r d ’ une a d r e s s e // Demande de r é−e n r e g i s t r e m e n t d ’ é v é nements u t i l i s a t e u r s s u i t e à r e c o n s t r u c t i o n d ’ é l é ments HTML ” p er son ne / h t m l L i s t e I t e m R e b u i l t ” : [ ] , // Ré e n r e g i s t r e m e n t d e s é v é nements de c l i c k sur l e s items // s u i t e à r e c o n s t r u c t i o n c o m p l è t e du code HTML d e s i t e m s . ” p er son ne / d e t a i l s R e b u i l t ” : [ ] , // Ré e n r e g i s t r e m e n t d e s é v é nements de c l i c k s u r l e s b o u t o n s ” Supprimer ” , ” M o d i f i e r ” // s u i t e à r e c o n s t r u c t i o n du code HTML des dé t a i l s . 75 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 }; }; // Appel de l a mé t h o d e d ’ i n i t i a l i s a t i o n init () ; /* * * I n t e r f a c e p u b l i q u e du module m e d i a t o r */ var p u b l i c I n t e r f a c e M e d i a t o r = { /* * * E n r e g i s t r e m e n t d ’ un c a l l b a c k s u r un é v é nement . * I l p e u t y a v o i r p l u s i e u r s c a l l b a c k s s u r un même é v é nement * ( par exemple : mise à j o u r de deux p a r t i e s d i s t i n c t e s de l a vue ) * @param { s t r i n g } e v e n t C a t e g é v é nement , q u i d o i t ê t r e un nom de p r o p r i é t é de m _ s u b s c r i p t i o n L i s t s * @param { f u n c t i o n } c a l l b a c k F u n c t i o n l a f o n c t i o n q u i s e r a a p p e l é e en r é a c t i o n à l ’ é v é nement . */ s u b s c r i b e : function ( eventCateg , c a l l b a c k F u n c t i o n ) { i f ( m _ s u b s c r i p t i o n L i s t s . hasOwnProperty ( eventCateg ) ) { m _ s u b s c r i p t i o n L i s t s [ eventCateg ] . push ( { c a l l b a c k : c a l l b a c k F u n c t i o n } ) ; } else { throw new E r r o r ( ” Cat é g o r i e d ’ é v é nements ” + eventCateg + ” inconnue du m é diateur ”) ; } }, 72 73 74 75 76 77 78 79 80 81 82 83 /* * * P u b l i c a t i o n d ’ un é v é nement s u r v e n u e t ex é c u t i o n de t o u s l e s c a l l b a c k s correspondants . * @param { s t r i n g } e v e n t C a t e g é v é nement , q u i d o i t ê t r e un nom de p r o p r i é t é de m _ s u b s c r i p t i o n L i s t s * @param { O b j e c t } c o n t e x t A r g argument o p t i o n n e l à t r a n s m e t t r e au c a l l b a c k ( exemple : item c l i q u é . . . ) */ p u b l i s h : function ( eventCateg , c o n t e x t A rg ) { var i ; i f ( m _ s u b s c r i p t i o n L i s t s . hasOwnProperty ( eventCateg ) ) { f or ( i =0 ; i< m _ s u b s c r i p t i o n L i s t s [ eventCateg ] . l e n g t h ; ++i ) { // On a p p e l l e l e c a l l b a k a v e c son m _ s u b s c r i p t i o n L i s t s [ eventCateg ] [ i ] . c a l l b a c k ( c o n t e x t A r g ) ; } } else { throw new E r r o r ( ” Cat é g o r i e d ’ é v é nements ” + eventCateg + ” inconnue du m é diateur ”) ; } }, 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 // Ré i n i t i a l i s e l e s l i s t e s de c a l l b a c k s à v i d e . empty : function ( ) { init () ; } }; 76 Chapitre 5 : Exemple d’Application Interactive 104 105 106 return p u b l i c I n t e r f a c e M e d i a t o r ; }() ] ) ; 5.4 Événements concernant les personnes 5.4.1 Enregistrement des événements utilisateurs via jQuery Tous les événements recensés dans le diagramme de cas d’utilisation (voir la figure 5.2) se verront ici attribué un gestionnaire qui, généralement, ne fera que publier l’événement auprès de mediator (partie 5.3). Les éléments HTML constants de la vue (<span>, <button>, <div>, <p>, etc.) sur lesquels ces événements seront appliqués sont définis dans le fichier HTML principal décrit dans la partie 5.4.9. Ces événements utilisateurs doivent parfois être réenregistrés suite à la reconstruction des éléments HTML concernés. Les événements sont alors détruits (méthodes jQuery.off(), ou jQuery.empty(), ou encore jQuery.remove()), puis, le code HTML est regénéré, et enfin, les événements utilisateur sont ré-enregistrés (méthode jQuery.on()). S’il faut prévoir de ré-enregistrer un gestionnaire d’événement utilisateur, nous allons permettre de déclencher ce ré-enregistrement via le mediator. Ceci permet d’éviter notamment des problèmes de dépendance cyclique des fonctions JavaScript ou modules, par exemples du fait que les événements jQuery doivent être initialisés après la génération de la vue. exemples/ihm/ex02_guiJQueryEventsPersonne.js 1 /* * 2 * Mé t h o d e d ’ i n i t i a l i s a t i o n d e s é v é nements u t i l i s a t e u r s J a v a S c r i p t . 3 * E n r e g i s t r e m e n t d e s g e s t i o n n a i r e s de c e s é v é nements v i a jQuery . 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” i n i t J Q u e r y E v e n t s P e r s o n n e ” , function ( ) { 6 7 // /////////////////////////////////////////////////////// 8 // c l i c k s u r l e bouton ” A j o u t e r une p er s on n e ” f a i s a n t s o r t i r l e f o r m u l a i r e 9 10 /* * 11 * G e s t i o n n a i r e c l i c k s u r l e bouton f a i s a n t s o r t i r l e f o r m u l a i r e 12 */ 13 var c l i c k B o u t o n S a i s i e P e r s o n n e = function ( e v e n t ) { 14 // p u b l i c a t i o n a u p r è s du mé d i a t o r 15 myApp . g u i . m e d i a t o r . p u b l i s h ( ” per s on n e / s a i s i e ” , { 16 p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e 17 }) ; 18 }; 19 20 // E n r e g i s t r e m e n t du Handler du c l i c k pour m o d i f i e r l e s d é t a i l s de l ’ item s é l e c t i o n n é v i a jQuery 21 $ ( ”#b o u t o n A j o u t e r P e r s o n n e ” ) . on ( ” c l i c k ” , c l i c k B o u t o n S a i s i e P e r s o n n e ) ; 22 23 // /////////////////////////////////////////////////////// 24 // c l i c k s u r l e bouton ” M o d i f i e r l e nom” f a i s a n t s o r t i r l e f o r m u l a i r e 25 26 /* * 27 * G e s t i o n n a i r e c l i c k s u r l e bouton f a i s a n t s o r t i r l e f o r m u l a i r e 77 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 */ var c l i c k B o u t o n M o d i f i e r P e r s o n n e = function ( e v e n t ) { // p u b l i c a t i o n a u p r è s du mé d i a t o r myApp . g u i . m e d i a t o r . p u b l i s h ( ” per s on n e / e d i t ” , { p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e }) ; }; // /////////////////////////////////////////////////////// // c l i c k s u r l e bouton ” Supprimer l a pe r s o n n e ” f a i s a n t s o r t i r l e f o r m u l a i r e /* * * G e s t i o n n a i r e c l i c k s u r l e bouton f a i s a n t s o r t i r l e f o r m u l a i r e */ var c l i c k B o u t o n S u p p r i m e r P e r s o n n e = function ( e v e n t ) { // p u b l i c a t i o n a u p r è s du mé d i a t o r myApp . g u i . m e d i a t o r . p u b l i s h ( ” per s on n e / d e l e t e ” , { p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e }) ; }; // ////////////////////////////////////////////////////// // G e s t i o n n a i r e de s u b m i t f o r m u l a i r e de m o d i f i c a t i o n de p e r s o n n e . /* * * G e s t i o n n a i r e de l ’ é v é nement s u b m i t du f o r m u l a i r e . * @param { Event } jQuery e v e n t c o r r e s p o n d a n t au h a n d l e r . */ var formHandlerModifPersonne = function ( e v e n t ) { // É v i t e r d ’ a p p e l e r l ’” a c t i o n ” par d é f a u t ( ) s c r i p t PHP, e t c . . . ) // du f o r m u l a i r e l o r s du s u b m i t event . preventDefault ( ) ; // p u b l i c a t i o n a u p r è s du mé d i a t o r myApp . g u i . m e d i a t o r . p u b l i s h ( ” pe rs o n n e / u p d a t e ” , { p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e }) ; } // f i n du g e s t i o n n a i r e formHandlerModif ( ) // E n r e g i s t r e m e n t du Handler du s u b m i t du f o r m u l a i r e v i a jQuery $ ( ”#modifierPersonneForm ” ) . on ( ” s u b m i t ” , formHandlerModifPersonne ) ; // ///////////////////////////////////////////////////// // G e s t i o n n a i r e de s u b m i t f o r m u l a i r e d ’ a j o u t de p e r s o n n e . /* * * G e s t i o n n a i r e de l ’ é v é nement s u b m i t du f o r m u l a i r e . * @param { Event } jQuery e v e n t c o r r e s p o n d a n t au h a n d l e r . */ var formHandlerAjoutPersonne = function ( e v e n t ) { // É v i t e r d ’ a p p e l e r l ’” a c t i o n ” par d é f a u t ( ) s c r i p t PHP, e t c . . . ) 78 Chapitre 5 : Exemple d’Application Interactive 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 // du f o r m u l a i r e l o r s du s u b m i t event . preventDefault ( ) ; // p u b l i c a t i o n a u p r è s du mé d i a t o r myApp . g u i . m e d i a t o r . p u b l i s h ( ” pe rs o n n e / c r e a t e ” , { p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e }) ; } // f i n du g e s t i o n n a i r e formHandlerAjout ( ) // E n r e g i s t r e m e n t du Handler du s u b m i t du f o r m u l a i r e v i a jQuery $ ( ”#ajouterPersonneForm ” ) . on ( ” s u b m i t ” , formHandlerAjoutPersonne ) ; /* * * E n r e g i s t r e l e s é v é nements de c l i c k s s u r l e s b o u t o n s ” M o d i f i e r ” e t ” Supprimer ” * l a per s onn e s é l e c t i o n n é e . * C e t t e f o n c t i o n d o i t ê t r e i n v o q u é e en c a s de s é l e c t i o n d ’ une n o u v e l l e pe rs o nne * ( r e c o n s t r u c t i o n deu code HTML du panneau d e s d é t a i l s . */ var r e g i s t e r B u t t o n C l i c k E v e n t s = function ( ) { // E n r e g i s t r e m e n t du Handler du c l i c k pour m o d i f i e r l e s d é t a i l s de l ’ item s é l e c t i o n n é v i a jQuery $ ( ”#b o u t o n M o d i f i e r P e r s o n n e ” ) . on ( ” c l i c k ” , c l i c k B o u t o n M o d i f i e r P e r s o n n e ) ; // E n r e g i s t r e m e n t du Handler du c l i c k pour su p p r i m e r l ’ item s é l e c t i o n n é v i a jQuery $ ( ”#boutonSupprimerPersonne ” ) . on ( ” c l i c k ” , c l i c k B o u t o n S u p p r i m e r P e r s o n n e ) ; } // /////////////////////////////////////////////////////// // C l i c k s s u r l e s é l é ments de l a l i s t e d ’ i t e m s /* * Mé t h o d e q u i permet de c r é e r un g e s t i o n n a i r e d ’ é v é nement de c l i c k * s u r chaque nom de p e r s o n n e s ( s é l e c t i o n d e s d é t a i l s ) * Ces g e s t i o n n a i r e s p u b l i e n t l ’ é v é nnement ” n o u v e l l e p e r s o n n e s é l e c t i o n n é e ” a u p r è s du mé d i a t o r . * @param { i n t } i n d e x i n d i c e de l ’ item pour l e q u e l on e n r e g i s t r e l ’ é v é nement . */ var r e g i s t e r H e l p e r S e l e c t D e t a i l s = function ( i n d e x ) { return function ( ) { myApp . g u i . m e d i a t o r . p u b l i s h ( ” pe rs o n n e / s e l e c t D e t a i l s ” , { p e r s o n n e : myApp . modele . p e r s o n n e s [ i n d e x ] }) ; }; }; /* * * E n r e g i s t r e l e s é v é nements j a v a s c r i p t de c l i c k s u r l e s é l é ments de l a l i s t e * ( noms d e s p e r s o n n e s ) . * C e t t e mé t h o d e d o i t ê t r e a p p e l é e l o r s de l a r e g éné r a t i o n du code de l a l i s t e . * @method r e g i s t e r L i s t e P e r s o n n e s C l i c k s * @param { O b j e c t } c o n t e x t A r g s non u t i l i s é * @return { f u n c t i o n } une f o n c t i o n c a l l b a c k q u i g è r e s l e c l i c k s u r l ’ item 79 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 index */ var r e g i s t e r L i s t e P e r s o n n e s C l i c k s = function ( c o n t e x t A r g s ) { fo r ( var i =0 ; i<myApp . modele . p e r s o n n e s . l e n g t h ; ++i ) { $ ( ”#master_ ”+ myApp . modele . p e r s o n n e s [ i ] . g e t I d ( ) ) . on ( ” click ” , registerHelperSelectDetails ( i ) ) ; } }; // E n r e g i s t r e r l e s c l i c k s l o r s de l ’ i n i t i a l i s a t i o n registerButtonClickEvents () ; registerListePersonnesClicks () ; // Permet à l a mé t h o d e q u i r e g é nè re t o u t e l a l i s t e d e s i t e m s // de r e c r é er , v i a l e mé d i a t o r , l e s é v é nements ” c l i c k ” s u r l e s i t e m s . myApp . g u i . m e d i a t o r . s u b s c r i b e ( ” per s on n e / h t m l L i s t e I t e m R e b u i l t ” , registerListePersonnesClicks ) ; // Permet à l a mé t h o d e q u i r e g é nè re l e panneau d e s d é t a i l s de r e c r é er , // v i a l e mé d i a t o r , l e s é v é nements ” c l i c k ” s u r l e s b o u t o n s dans l e panneau d e s dé t a i l s . myApp . g u i . m e d i a t o r . s u b s c r i b e ( ” per s on n e / d e t a i l s R e b u i l t ” , registerButtonClickEvents ) ; }]) ; 5.4.2 Mise à jour du panneau des détails Le panneau des détails de l’item sélectionné doit être mis à jour lors de la modification de la personne par validation du formulaire, ou lors du changement de l’item sélectionné (click sur un autre item). dans ce cas, les événements utilisateurs sur les éléments HTML qui sont générés dynamiquement sur le panneau des détails doivent aussi être reconstruit (événement personne/detailsRebuilt du mediator). exemples/ihm/ex03_guiDetailsChanged.js 1 2 /* * * Dé f i n i t i o n e t e n r e g i s t r e m e n t d e s c a l l b a c k s de mise à j o u r d e s d é t a i l s de l ’ item s é l e c t i o n n é e . 3 */ 4 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s U p d a t e D e t a i l s ” , function ( ) { 5 6 /* * 7 * Géné r a t i o n du code HTML d e s d é t a i l s de l a p e r s o n n e s é l e c t i o n n é e . 8 */ 9 var getHtmlCodeDetail = function ( ) { 10 var htmlCode = ”<span c l a s s =\” p a n e l \”>” + 11 ”<p><s trong>Nom :</s trong> ” + myApp . modele . s e l e c t e d P e r s o n n e . getNom ( ) + ”</p>” + 12 ”<button i d =\” b o u t o n M o d i f i e r P e r s o n n e \”>M o d i f i e r</button><br />” + 13 ”<button i d =\” boutonSupprimerPersonne \”>Supprimer</button><br />” + 14 ”<button i d =\” b o u t o n A j o u t e r A d r e s s e \”>A j o u t e r une a d r e s s e</button>” ; 15 f o r ( var i n d e x = 0 ; i n d e x < myApp . modele . s e l e c t e d P e r s o n n e . g et Nb Ad re ss es ( ) ; ++i n d e x ) { 16 htmlCode += ”<p>” + 80 Chapitre 5 : Exemple d’Application Interactive 17 myApp . view . a d r e s s e . getHtmlDevelopped (myApp . modele . selectedPersonne . getAdresse ( index ) ) + ”<br /><button i d =\” boutonSupprimerAdresse_ ” + myApp . modele . s e l e c t e d P e r s o n n e . g e t A d r e s s e ( i n d e x ) . g e t P r o p e r t y ( ’ id ’ ) +” \”>Supprimer l ’ a d r e s s e</button>” + ”<br /><button i d =\” b o u t o n M o d i f i e r A d r e s s e _ ” + myApp . modele . s e l e c t e d P e r s o n n e . g e t A d r e s s e ( i n d e x ) . g e t P r o p e r t y ( ’ id ’ ) +” \”>M o d i f i e r l ’ a d r e s s e</button>” + ”</p>” ; 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 } htmlCode += ”</span>” ; return htmlCode ; }; /* * * R e d e s s i n e l e s d é t a i l s d ’ une per s on n e s u i t e à sa s é l e c t i o n ou sa modification . * @param { O b j e c t } c o n t e x t A r g non u t i l i s é . */ var r e p a i n t D e t a i l = function ( c o n te x t A r g ) { $ ( ”#modifierPersonneForm ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant $ ( ”#ajouterPersonneForm ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant $ ( ”#aj o ut e rA dre sse Form ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant $ ( ”#modifierAdresseForm ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 $ ( ”#v u e D e t a i l ” ) . empty ( ) ; // Vider l e s d é t a i l s de l ’ item s é l e c t i o n n é $ ( ”#v u e D e t a i l ” ) . html ( getHtmlCodeDetail ( ) ) ; // Géné r a t i o n du code HTML // Recr é e r l e s é v é nements de c l i c k s s u r l e s b o u t o n s ” m o d i f i e r ” , ” s u p p r i m e r ” , etc . myApp . g u i . m e d i a t o r . p u b l i s h ( ” per s on n e / d e t a i l s R e b u i l t ” ) ; }; // E n r e g i s t r e m e n t du c a l l b a c k de l ’ é v é nement d é d i é (m. a . j . d e s d é t a i l s ) myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” per s on n e / d e t a i l s C h a n g e d ” , r e p a i n t D e t a i l ) ; // E n r e g i s t r e m e n t du c a l l b a c k de l ’ é v é nement de mise à j o u r de l a pe r s o n n e myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” per s on n e / changed ” , r e p a i n t D e t a i l ) ; }() ] ) ; 5.4.3 Mise à jour du panneau des items Le panneau qui affiche la liste des l’items doit être mis à jour lors de la modification de la personne par validation du formulaire (le nom de la personne peut changer), ou lors du changement de l’item sélectionné, celui-ci étant surligné. En cas de changement de l’item sélectionné, la propriété selectedPersonne du modèle sera 81 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery modifiée, et le rafraîchissement du panneau des détails sera ensuite provoqué. Lors de la création d’une nouvelle personne, celle-ci sera automatiquement sélectionnée. exemples/ihm/ex04_guiPersonneChanged.js 1 /* * 2 * Dé f i n i t i o n e t abonnement d e s c a l l b a c k s de mise à j o u r de l a 3 * l i s t e c l i c k a b l e d e s items , s o i t l o r s de l a m o d i f i c a t i o n 4 * du modèle , s o i t l o r s du changement de p er s o n n e s é l e c t i o n n é e . 5 */ 6 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s M a i n L i s t U p d a t e ” , function ( ) { 7 8 /* * 9 * A c t i v e ou d é s a c t i v e l e s u r l i g n a g e ( s t y l e CSS) d ’ un item de l a l i s t e . 10 * @param { pe r so nne } per son ne item de l a l i s t e à m o d i f i e r ( v i a l ’ ID de l ’ é l é ment HTML) . 11 * @param { b o o l e a n } h i l g h l i g h t e d t r u e s i on d o i t s u r l i g n e r , f a l s e pour r e m e t t r e l e s t y l e par d é f a u t . 12 */ 13 var s e t H i g h l i g h t e d = function ( personne , h i g h l i g h t e d ) { 14 if ( highlighted ){ 15 // Met tre l e s t y l e s u r l i g n é s u r l ’ item de l a l i s t e 16 $ ( ”#master_ ”+ p e r s o n n e . g e t I d ( ) ) . c s s ( ”background−color ” , ”#333” ) 17 . c s s ( ” color ” , ”#e e e ” ) 18 . c s s ( ”border−r a d i u s ” , ”4 px ” ) 19 . c s s ( ”padding” , ”2 px ” ) ; 20 } else { 21 // Remettre l e s t y l e normal s u r l ’ item de l a l i s t e 22 $ ( ”#master_ ”+ p e r s o n n e . g e t I d ( ) ) . c s s ( ”background−color ” , ”#e e e ” ) 23 . c s s ( ” color ” , ”#333” ) 24 . c s s ( ”border−r a d i u s ” , ”4 px ” ) 25 . c s s ( ”padding” , ”2 px ” ) ; 26 } 27 } 28 29 /* * 30 * Géné r a t i o n du code HTML de l a l i s t e de p e r s o n n e s 31 */ 32 var g e t H t m l C o d e L i s t e P e r s o n n e s = function ( ) { 33 var htmlCode = ” ” ; 34 f o r ( i =0 ; i<myApp . modele . p e r s o n n e s . l e n g t h ; ++i ) { 35 htmlCode += 36 ”<p i d =\”master_ ”+ myApp . modele . p e r s o n n e s [ i ] . g e t I d ( ) + ” \”>” + 37 ”<s trong>Nom :</s trong> ” + myApp . modele . p e r s o n n e s [ i ] . getNom ( ) + ”</p>” ; 38 } 39 return htmlCode ; 40 }; 41 42 /* * 43 * R a f f r a i c h i s s e m e n t ( ou a f f i c h a g e ) de t o u t e l a vue . 44 * @param c o n t e x t A r g non u t i l i s é . 45 */ 46 var r e p a i n t V u e = function ( c o nt e x tA r g ) { 47 48 $ ( ”#l i s t e P e r s o n n e s ” ) . empty ( ) ; // Vider l a l i s t e e t s e s é v é nements 49 $ ( ”#l i s t e P e r s o n n e s ” ) . html ( g e t H t m l C o d e L i s t e P e r s o n n e s ( ) ) ; // a f f i c h e r 50 82 Chapitre 5 : Exemple d’Application Interactive 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 // A p p l i q u e r l e s t y l e par d é f a u t s u r t o u s l e s i t e m s fo r ( var i =0 ; i < myApp . modele . p e r s o n n e s . l e n g t h ; ++i ) { s e t H i g h l i g h t e d (myApp . modele . p e r s o n n e s [ i ] , f a l s e ) ; } // S u r l i g n e r l ’ item s é l e c t i o n n é s e t H i g h l i g h t e d (myApp . modele . s e l e c t e d P e r s o n n e , true ) ; // Recr é e r l e s é v é nements de c l i c k s s u r l e s i t e m s de l a l i s t e myApp . g u i . m e d i a t o r . p u b l i s h ( ” pe rs o n n e / h t m l L i s t e I t e m R e b u i l t ” ) ; }; /* * * Changer l ’ item s é l e c t i o n n é en r é a c t i o n à un c l i c k . * @param { O b j e c t } c o n t e x t A r g argument i n d i q u a n t l a n o u v e l l e p e r s o n n e s é lectionn ée . * @param { pe r so nne } c o n t e x t A r g . pe rs o n n e n o u v e l l e p e r s o n n e s é l e c t i o n n é e . */ var s e l e c t P e r s o n n e = function ( c o n t e x t A r g ) { // Supprimer l e s u r l i g n a g e de l ’ a n c i e n n e p e r s o n n e s é l e c t i o n n é e s e t H i g h l i g h t e d (myApp . modele . s e l e c t e d P e r s o n n e , f a l s e ) ; // Changer l ’ item s é l e c t i o n n é myApp . modele . s e l e c t e d P e r s o n n e = c o n t e x tA r g . p e r s o n n e ; // Me ttr e l e s t y l e s u r l i g n é s u r l ’ item s é l e c t i o n n é de l a l i s t e s e t H i g h l i g h t e d (myApp . modele . s e l e c t e d P e r s o n n e , true ) ; // Provoquer l a mise à j o u r du panneau d e s d é t a i l s myApp . g u i . m e d i a t o r . p u b l i s h ( ” per s on n e / d e t a i l s C h a n g e d ” , { p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e }) ; }; /* * * Changer l ’ item s é l e c t i o n n é s u i t e à c r é a t i o n d ’ une p e r s o n n e e t mise à j o u r de l a vue . * @param { O b j e c t } c o n t e x t A r g argument i n d i q u a n t l a n o u v e l l e p e r s o n n e s é lectionn ée . * @param { pe r so nne } c o n t e x t A r g . pe rs o n n e n o u v e l l e p e r s o n n e s é l e c t i o n n é e . */ var s e l e c t P e r s o n n e A n R e p a i n t = function ( c o nt e x t A r g ) { s e l e c t P e r s o n n e ( co n t ex t Ar g ) ; repaintVue ( ) ; } // E n r e g i s t r e m e n t du c a l l b a c k de m o d i f i c a t i o n de l a p e r s o n n e myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” per s on n e / changed ” , r e p a i n t V u e ) ; // E n r e g i s t r e m e n t du c a l l b a c k de c r é a t i o n de l a p e r s o n n e myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” per s on n e / c r e a t e d ” , s e l e c t P e r s o n n e A n R e p a i n t ) ; // E n r e g i s t r e m e n t du c a l l b a c k de s é l e c t i o n d ’ une n o u v e l l e pe r s o n n e . myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” per s on n e / s e l e c t D e t a i l s ” , s e l e c t P e r s o n n e ) ; }() ] ) ; 83 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 5.4.4 Bouton ”Supprimer” Lorsque l’utilisateur clique sur ”Supprimer”, la personne sélectionnée est supprimée du modèle. Une nouvelle personne est sélectionnée (personne par défaut) et la vue est réinitialisée. exemples/ihm/ex07_guiBoutonSupprimerPersonne.js 1 2 /* * * Dé f i n i t i o n e t e n r e g i s t r e m e n t d e s c a l l b a c k s a p p e l é s à g é r e r l e c l i c s u r l e bouton 3 * ” m o d i f i e r ” l a pe rso nn e s é l e c t i o n n é e . 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s C l i c k S u p p r i m e r ” , function ( ) { 6 7 /* * 8 * C a l l b a c k q u i supprime l a pe rs o n n e p a s s é e dans l ’ o b j e t p a s s é en argument . 9 * @param { O b j e c t } c o n t e x t A r g argument i n d i q u a n t l a p e r s o n n e à s u p p r i m e r . 10 * @param { pe r so nne } c o n t e x t A r g . pe rs o n n e r é f é r e n c e de l ’ i n s t a n c e de p e r s o n n e à s up p ri me r dans l e modèle . 11 */ 12 var d e l e t e P e r s o n n e = function ( c o n t e x t A r g ) { 13 // I n d i c e dans l e t a b l e a u de l a p er s o n n e à su p p r i m e r . 14 var i n d e x S e l e c t e d P e r s o n n e = myApp . modele . p e r s o n n e s . indexOf ( c o n t e x t A r g . personne ) ; 15 // S u p p r e s s i o n de l a pers onn e dans l e modèle 16 myApp . modele . p e r s o n n e s . s p l i c e ( i n d e x S e l e c t e d P e r s o n n e , 1 ) ; 17 // Personne s é l e c t i o n n é e par d é f a u t 18 myApp . modele . s e l e c t e d P e r s o n n e = myApp . modele . p e r s o n n e s [ 0 ] ; 19 20 // Provoquer l a mise à j o u r de l a vue : 21 myApp . g u i . m e d i a t o r . p u b l i s h ( ” per s on n e / changed ” , { 22 p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e 23 }) ; 24 } 25 26 // E n r e g i s t r e m e n t du c a l l b a c k 27 myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” per s on n e / d e l e t e ” , d e l e t e P e r s o n n e ) ; 28 29 } ( ) ] ) ; 5.4.5 Bouton ”Modifier” et affichage du formulaire Lorsque l’utilisateur clique sur ”Modifier”, le formulaire doit être affiché avec les données de la personnes dans les inputs. exemples/ihm/ex06_guiBoutonModifierPersonne.js 1 2 /* * * Dé f i n i t i o n e t e n r e g i s t r e m e n t d e s c a l l b a c k s a p p e l é s à g é r e r l e c l i c s u r l e bouton 3 * ” m o d i f i e r ” l a pe rso nn e s é l e c t i o n n é e . 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s C l i c k M o d i f i e r P e r s o n n e ” , function ( ) { 6 7 /* * 8 * Géné r a t i o n du code HTML du f o r m u l a i r e de m o d i f i c a t i o n de l a p e r s o n n e s é lectionn ée . 84 Chapitre 5 : Exemple d’Application Interactive 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 */ var getHtmlFormInputs = function ( ) { return ”<span s t y l e =\”width :360 px ; display : i n l i n e −b l o c k ; vertical −align : top ; \ ”>” + myApp . g u i . getHtmlFormInputs (myApp . modele . s e l e c t e d P e r s o n n e , ” modifierPersonneForm ” ) + ”<l a b e l></ l a b e l><i nput t y p e =\” s u b m i t \” v a l u e =\” V a l i d e r \”></ i nput>” + ”</span>” ; } /* * * C a l l b a c k d ’ A f f i c h a g e ( v i a l e DOM) du f o r m u l a i r e dans l ’ é l é ment d ’ ID ” modifierPersonneForm ” * @param { O b j e c t } c o n t e x t A r g non u t i l i s é . */ var r e p a i n t F o r m I n p u t s = function ( co n te x t A r g ) { $ ( ”#modifierPersonneForm ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant $ ( ”#ajouterPersonneForm ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant $ ( ”#aj o ut e rA dre sse Form ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant $ ( ”#modifierAdresseForm ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant 26 27 28 29 30 31 32 33 34 $ ( ”#modifierPersonneForm ” ) . append ( getHtmlFormInputs ( ) ) ; // a j o u t e r l e s nouveaux i n p u t s }; // E n r e g i s t r e m e n t du c a l l b a c k myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” per s on n e / e d i t ” , r e p a i n t F o r m I n p u t s ) ; }() ] ) ; 5.4.6 Bouton ”Ajouter une personne” Lorsque l’utilisateur clique sur ”Ajouter une personne”, le formulaire doit être affiché avec les valeurs par défaut (typiquement des champs vides) dans les inputs. Pour cela, on utilise la possibilité offerte par la fabrique de nos modules métier (partie 3.4) de créer une objet par défaut en passant null en argument de la fabrique. Ceci permet de ne pas générer de messages d’erreur en cas de champs obligatoire initialement vide. Après validation du formulaire, la personne est ajoutée dans le modèle, elle est automatiquement sélectionnée, et la vue est mise à jour. exemples/ihm/ex05_guiBoutonAjouterPersonne.js 1 2 /* * * Dé f i n i t i o n e t e n r e g i s t r e m e n t d e s c a l l b a c k s a p p e l é s à g é r e r l e c l i c s u r l e bouton 3 * ” m o d i f i e r ” l a pe rso nn e s é l e c t i o n n é e . 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s C l i c k A j o u t e r ” , function ( ) { 6 7 /* * 85 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 * Géné r a t i o n du code HTML du f o r m u l a i r e de m o d i f i c a t i o n de l a p e r s o n n e s é lectionn ée . */ var getHtmlFormInputs = function ( ) { return ”<span s t y l e =\”width :360 px ; display : i n l i n e −b l o c k ; vertical −align : top ; \ ”>” + ”<s trong s t y l e =\”width : 360 px ; display : i n l i n e −b l o c k ; text −align : c e n t e r ; padding : 15 px ; \ ”>S a i s i e d ’ une n o u v e l l e p e r s o n n e</s trong> ” + myApp . g u i . getHtmlFormInputs (myApp . m e t i e r . p e r s o n n e . c r e a t e I n s t a n c e ( null ) , ” ajouterPersonneForm ” ) + ”<l a b e l></ l a b e l><i nput t y p e =\” s u b m i t \” v a l u e =\” V a l i d e r \”></ i nput>” + ”</span>” ; } /* * * C a l l b a c k d ’ A f f i c h a g e ( v i a l e DOM) du f o r m u l a i r e dans l ’ é l é ment d ’ ID ” mainForm” * @param { O b j e c t } c o n t e x t A r g non u t i l i s é . */ var r e p a i n t F o r m I n p u t s = function ( co n te x t A r g ) { $ ( ”#modifierPersonneForm ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant $ ( ”#ajouterPersonneForm ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant $ ( ”#aj o ut e rA dre sse Form ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant $ ( ”#modifierAdresseForm ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant 27 28 29 30 31 32 33 34 $ ( ”#ajouterPersonneForm ” ) . append ( getHtmlFormInputs ( ) ) ; // a j o u t e r l e s nouveaux i n p u t s }; // E n r e g i s t r e m e n t du c a l l b a c k myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” per s on n e / s a i s i e ” , r e p a i n t F o r m I n p u t s ) ; }() ] ) ; 5.4.7 Validation du formulaire de modification Lors de la validation (événement submit) du formulaire de modification, les données de la personne sélectionnée doivent être mises à jour à partir des valeurs saisies dans le formulaire. Les panneaux potentiellement impactés (liste des items, panneau des détails) sont alors mis à jour. exemples/ihm/ex09_guiModifierPersonneFormValidate.js 1 /* * 2 * Dé f i n i t i o n e t e n r e g i s t r e m e n t du c a l l b a c k r é a g i s s s a n t à l a v a l i d a t i o n ( s u b m i t ) 3 * du f o r m u l a i r e de m o d i f i c a t i o n d ’ une pe r s o n n e . 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s V a l i d a t e M o d i f i e r F o r m ” , function ( ) { 6 // F or mul ai r e de m o d i f i c a t i o n d ’ une p er s on n e 7 86 Chapitre 5 : Exemple d’Application Interactive 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 /* * * M o d i f i e l e modèle à p a r t i r d e s donn é e s s a i s i e s dans l e f o r m u l a i r e */ var updateModel = function ( ) { // 1) Mise à j o u r d e s donn é e s du modèle // à p a r t i r d e s v a l e u r s d e s i n p u t s du f o r m u l a i r e var propertyName , inputId ; // Pour chaque p r o p r i é t é ( chaque i n p u t du f o r m u l a i r e ) f o r ( var j =0 ; j< myApp . m e t i e r . p e r s o n n e . g e t P r o p e r t y L i s t ( ) . l e n g t h ; ++j ) { propertyName = myApp . m e t i e r . p e r s o n n e . g e t P r o p e r t y L i s t ( ) [ j ] ; i f ( propertyName != ” i d ” ) { // c a l c u l de l ’ ID de l ’ i n p u t i n p u t I d = myApp . g u i . g e t I n p u t I d ( { propertyName : propertyName , formId : ” modifierPersonneForm ” }) ; // M o d i f i c a t i o n de l a p r o p r i é t é de l a p e r s o n n e // a v e c l a v a l e u r s a i s i e dans l ’ i n p u t . myApp . modele . s e l e c t e d P e r s o n n e . s e t P r o p e r t y ( propertyName , document . getElementById ( i n p u t I d ) . v a l u e ); } } // Provoquer l a mise à j o u r d e s é l é ments de l a vue o b s e r v a n t l a pe r s o n n e myApp . g u i . m e d i a t o r . p u b l i s h ( ” per s on n e / changed ” , { p e r s o n n e : null }) ; }; // E n r e g i s t r e m e n t du c a l l b a c k de l ’ é v é nement de v a l i d a t i o n du f o r m u l a i r e myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” per s on n e / u p d a t e ” , updateModel ) ; }() ] ) ; 5.4.8 Validation du formulaire d’ajout d’une personne Lors de la validation (événement submit) du formulaire d’ajout, une personne doit être ajoutée au modèle à partir des valeurs saisies dans le formulaire. Les panneaux potentiellement impactés (liste des items, panneau des détails) sont alors mis à jour. exemples/ihm/ex08_guiAjouterPersonneFormValidate.js 1 /* * 2 * Dé f i n i t i o n e t e n r e g i s t r e m e n t du c a l l b a c k r é a g i s s s a n t à l a v a l i d a t i o n ( s u b m i t ) 3 * du f o r m u l a i r e de m o d i f i c a t i o n d ’ une pe r s o n n e . 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s V a l i d a t e A j o u t e r F o r m ” , function ( ) { 6 7 /* * 8 * M o d i f i e l e modèle à p a r t i r d e s donn é e s s a i s i e s dans l e f o r m u l a i r e 9 */ 10 var updateModel = function ( ) { 87 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 // 1) Mise à j o u r d e s donn é e s du modèle // à p a r t i r d e s v a l e u r s d e s i n p u t s du f o r m u l a i r e var propertyName , inputId ; // Ajout d ’ un pe rso nne v i d e dans l a c o l l e c t i o n var n o u v e l l e P e r s o n n e = myApp . m e t i e r . p e r s o n n e . c r e a t e I n s t a n c e ( null ) ; myApp . modele . p e r s o n n e s . push ( n o u v e l l e P e r s o n n e ) ; // Pour chaque p r o p r i é t é ( chaque i n p u t du f o r m u l a i r e ) f o r ( var j =0 ; j< myApp . m e t i e r . p e r s o n n e . g e t P r o p e r t y L i s t ( ) . l e n g t h ; ++j ) { propertyName = myApp . m e t i e r . p e r s o n n e . g e t P r o p e r t y L i s t ( ) [ j ] ; i f ( propertyName != ” i d ” ) { // c a l c u l de l ’ ID de l ’ i n p u t i n p u t I d = myApp . g u i . g e t I n p u t I d ( { propertyName : propertyName , formId : ” ajouterPersonneForm ” }) ; // M o d i f i c a t i o n de l a p r o p r i é t é de l a p e r s o n n e // a v e c l a v a l e u r s a i s i e dans l ’ i n p u t . n o u v e l l e P e r s o n n e . s e t P r o p e r t y ( propertyName , document . getElementById ( i n p u t I d ) . v a l u e ); } } // Provoquer l a s é l e c t i o n de l a n o u v e l l e p e r s o n n e ( e t par s u i t e l a mise à j o u r de l a vue ) myApp . g u i . m e d i a t o r . p u b l i s h ( ” per s on n e / c r e a t e d ” , { personne : nouvellePersonne }) ; }; // E n r e g i s t r e m e n t du c a l l b a c k de l ’ é v é nement de v a l i d a t i o n du f o r m u l a i r e myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” per s on n e / c r e a t e ” , updateModel ) ; }() ] ) ; 5.4.9 Code HTML de la vue et invocation des méthodes Il faut surtout penser à inclure jquery.js le plus tard possible et à invoquer la méthode d’enregistrement des événements utilisateurs après la génération de la vue, qui crée les éléments HTML sur lesques on applique ces événements. exemples/ihm/ex10_demoIHM.html 1 2 3 4 5 6 7 8 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>A p p l i c a t i o n i n t e r a c t i v e</ t i t l e> <l i nk r e l=” s t y l e s h e e t ” h r e f=” b a s i c S t y l e . c s s ” /> </head> <body> 88 Chapitre 5 : Exemple d’Application Interactive 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 <s c r i p t <s c r i p t <s c r i p t <s c r i p t <s c r i p t <s c r i p t <s c r i p t <s c r i p t <s c r i p t <s c r i p t <s c r i p t <s c r i p t <s c r i p t <s c r i p t <s c r i p t <s c r i p t <s c r i p t <s c r i p t <s c r i p t <!−− s r c=” . / m o d u l e s M e t i e r P r o t o t y p e . j s ”></s c r i p t> s r c=” . / m e d i a t o r I n p u t F i l t e r . j s ”></s c r i p t> s r c=” . / f o r m s G u i P r o t o t y p e . j s ”></s c r i p t> s r c=” . / i n t e r f a c e I m p l e m e n t a t i o n . j s ”></s c r i p t> s r c=” . / personneModule . j s ”></s c r i p t> s r c=” . / modelModule . j s ”></s c r i p t> s r c=” . / m e d i a t o r . j s ”></s c r i p t> s r c=” . / g u i B o u t o n M o d i f i e r P e r s o n n e . j s ”></s c r i p t> s r c=” . / guiBoutonSupprimerPersonne . j s ”></s c r i p t> s r c=” . / g u i B o u t o n A j o u t e r P e r s o n n e . j s ”></s c r i p t> s r c=” . / g u i B o u t o n A j o u t e r A d r e s s e . j s ”></s c r i p t> s r c=” . / g u i B o u t o n M o d i f i e r A d r e s s e . j s ”></s c r i p t> s r c=” . / guiBoutonSupprimerAdresse . j s ”></s c r i p t> s r c=” . / g u i M o d i f i e r P e r s o n n e F o r m V a l i d a t e . j s ”></s c r i p t> s r c=” . / g u i A j o u t e r P e r s o n n e F o r m V a l i d a t e . j s ”></s c r i p t> s r c=” . / g u i A j o u t e r A d r e s s e F o r m V a l i d a t e . j s ”></s c r i p t> s r c=” . / g u i M o d i f i e r A d r e s s e F o r m V a l i d a t e . j s ”></s c r i p t> s r c=” . / g u i D e t a i l s C h a n g e d . j s ”></s c r i p t> s r c=” . / guiPersonneChanged . j s ”></s c r i p t> Code HTML de l a vue −− S t r u c t u r e g én é r a l e de l a page HTML −−> <button i d=” b o u t o n A j o u t e r P e r s o n n e ”>A j o u t e r une p e r s o n n e</button><br /> <span i d=” l i s t e P e r s o n n e s ” c l a s s=” p a n e l ”></span> <span c l a s s=” p a n e l ”> <span i d=” v u e D e t a i l ”> </span><br /><br /> </span> <span i d=”spanMainForm” c l a s s=” p a n e l ”> <form i d=” ajouterPersonneForm ” method=” p o s t ” ></form> <form i d=” modifierPersonneForm ” method=” p o s t ” ></form> <form i d=” a jo ut e r A dre sseFor m ” method=” p o s t ” ></form> <form i d=” modifierAdresseForm ” method=” p o s t ” ></form> </span> <!−− I n c l u s i o n de jQuery l e p l u s t r a d p o s s i b l e −−> <s c r i p t s r c=” . / j q u e r y . j s ”></s c r i p t> <s c r i p t s r c=” . / guiJQueryEventsPersonne . j s ”></s c r i p t> <s c r i p t s r c=” . / g u i J Q u e r y E v e n t s A d r e s s e . j s ”></s c r i p t> <!−− Ajout d ’ un main e t ex é c u t i o n −−> <s c r i p t> /* * * Sé r i e d ’ i n s t r u c t i o n s e f f e c t u é e s pour i n i t i a l i s e r l ’ a p p l i c a t i o n / * @method mainFunction * @augments myApp */ myApp . addModule ( ” mainFunction ” , function ( ) { // Personne s é l e c t i o n n é e par d é f a u t myApp . modele . s e l e c t e d P e r s o n n e = myApp . modele . p e r s o n n e s [ 0 ] ; // Provoquer l e p remier a f f i c h a g e de l a vue : myApp . g u i . m e d i a t o r . p u b l i s h ( ” pe rs o n n e / changed ” , { p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e }) ; 89 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 65 66 // E n r e g i s t r e m e n t d e s é v é nements u t i l i s a t e u r s g é r é s par jQuery 67 myApp . g u i . i n i t J Q u e r y E v e n t s P e r s o n n e ( ) ; 68 myApp . g u i . i n i t J Q u e r y E v e n t s A d r e s s e ( ) ; 69 70 }) ; 71 72 // /////////////////////////////////////////////////// 73 // Exé c u t i o n du Main a v e c un t e s t d ’ e x c e p t i o n 74 // t r y { 75 // Exé c u t i o n de l a mé t h o d e mainFunction 76 myApp . mainFunction ( ) ; 77 // } c a t c h ( e ) { 78 // a l e r t ( e . message ) ; 79 // } 80 </s c r i p t> 81 </body> 82 </html> 5.5 Événements concernant les Adresses 5.5.1 Enregistrement des événements utilisateurs via jQuery De même que pour les personnes, l’utilisation de jQuery est limitée à un module Wrapper, qui va définir tous les handler. Comme il peut y avoir plusieurs adresses, dont les éléments HTML sont générés dynamiquement, sur le panneau des détails, les événements concernant les adresses doivent pouvoir être reconstruits dans le cas d’une reconstruction du panneau des détails de la vue (événement personne/detailsRebuilt du mediator. De plus, nous devons créer un handler pour chacune des adresses de la personne sélectionnée. Ces handler seront créés grâce à des helpers. exemples/ihm/ex11_guiJQueryEventsAdresse.js 1 /* * 2 * Mé t h o d e d ’ i n i t i a l i s a t i o n d e s é v é nements u t i l i s a t e u r s J a v a S c r i p t . 3 * E n r e g i s t r e m e n t d e s g e s t i o n n a i r e s de c e s é v é nements v i a jQuery . 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” i n i t J Q u e r y E v e n t s A d r e s s e ” , function ( ) { 6 7 /* * 8 * G e s t i o n n a i r e c l i c k s u r l e bouton f a i s a n t s o r t i r l e f o r m u l a i r e 9 */ 10 var c l i c k B o u t o n S a i s i e A d r e s s e = function ( e v e n t ) { 11 // p u b l i c a t i o n a u p r è s du mé d i a t o r 12 myApp . g u i . m e d i a t o r . p u b l i s h ( ” a d r e s s e / s a i s i e ” , { 13 p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e 14 }) ; 15 }; 16 17 /* * Mé t h o d e q u i permet de c r é e r un g e s t i o n n a i r e d ’ é v é nement de c l i c k 18 * du bouton de s u p p r e s s i o n s u r chaque a d r e s s e de l a p e r s o n n e s é l e c t i o n n é e . 19 * Ces g e s t i o n n a i r e s p u b l i e n t l ’ é v é nnement ” n o u v e l l e p e r s o n n e s é l e c t i o n n é e ” a u p r è s du mé d i a t o r . 90 Chapitre 5 : Exemple d’Application Interactive 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 * @param { i n t } i n d e x i n d i c e de l ’ a d r e s s e pour l e q u e l on e n r e g i s t r e l ’ é v é nement . */ var r e g i s t e r H e l p e r S u p p r i m e r A d r e s s e = function ( i n d e x ) { return function ( ) { myApp . g u i . m e d i a t o r . p u b l i s h ( ” a d r e s s e / d e l e t e ” , { p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e , a d r e s s e : myApp . modele . s e l e c t e d P e r s o n n e . g e t A d r e s s e ( i n d e x ) }) ; }; }; /* * Mé t h o d e q u i permet de c r é e r un g e s t i o n n a i r e d ’ é v é nement de c l i c k * du bouton de s u p p r e s s i o n s u r chaque a d r e s s e de l a p e r s o n n e s é l e c t i o n n é e . * Ces g e s t i o n n a i r e s p u b l i e n t l ’ é v é nnement ” n o u v e l l e p e r s o n n e s é l e c t i o n n é e ” a u p r è s du mé d i a t o r . * @param { i n t } i n d e x i n d i c e de l ’ a d r e s s e pour l e q u e l on e n r e g i s t r e l ’ é v é nement . */ var r e g i s t e r H e l p e r M o d i f i e r A d r e s s e = function ( i n d e x ) { return function ( ) { myApp . g u i . m e d i a t o r . p u b l i s h ( ” a d r e s s e / e d i t ” , { p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e , a d r e s s e : myApp . modele . s e l e c t e d P e r s o n n e . g e t A d r e s s e ( i n d e x ) }) ; }; }; /* * * E n r e g i s t r e l e s é v é nements de c l i c k s s u r l e s b o u t o n s ” A j o u t e r une a d r e s s e ” et * l e s b o u t o n s ” Supprimer ” ou m o d i f i e r de t o u t e s l e s a d r e s s e s de l a p e r s o n n e s é lectionn ée . * C e t t e f o n c t i o n d o i t ê t r e i n v o q u é e en c a s de s é l e c t i o n d ’ une n o u v e l l e pe rs o nne * ( r e c o n s t r u c t i o n deu code HTML du panneau d e s d é t a i l s ) . */ var r e g i s t e r B u t t o n C l i c k E v e n t s = function ( ) { var idBoutonSupprimerAdresse , idBoutonModifierAdresse ; // E n r e g i s t r e m e n t du Handler du c l i c k pour a j o u t e r une a d r e s s e $ ( ”#b o u t o n A j o u t e r A d r e s s e ” ) . on ( ” c l i c k ” , c l i c k B o u t o n S a i s i e A d r e s s e ) ; f o r ( var i =0 ; i < myApp . modele . s e l e c t e d P e r s o n n e . ge tN bA dr es se s ( ) ; ++i ) { idBoutonSupprimerAdresse = ” boutonSupprimerAdresse_ ” + myApp . modele . s e l e c t e d P e r s o n n e . g e t A d r e s s e ( i ) . g e t P r o p e r t y ( ’ i d ’ ) ; $ ( ”#” + idBoutonSupprimerAdresse ) . on ( ” c l i c k ” , registerHelperSupprimerAdresse ( i ) ) ; idBoutonModifierAdresse = ” boutonModifierAdresse_ ” + myApp . modele . s e l e c t e d P e r s o n n e . g e t A d r e s s e ( i ) . g e t P r o p e r t y ( ’ i d ’ ) ; $ ( ”#” + i d B o u t o n M o d i f i e r A d r e s s e ) . on ( ” c l i c k ” , r e g i s t e r H e l p e r M o d i f i e r A d r e s s e (i)); } 63 64 65 66 67 } 91 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 // ///////////////////////////////////////////////////// // G e s t i o n n a i r e de s u b m i t f o r m u l a i r e d ’ a j o u t de a d r e s s e . /* * * G e s t i o n n a i r e de l ’ é v é nement s u b m i t du f o r m u l a i r e . * @param { Event } jQuery e v e n t c o r r e s p o n d a n t au h a n d l e r . */ var f o rm H a n d l e r A j o u t A d r es s e = function ( e v e n t ) { // É v i t e r d ’ a p p e l e r l ’” a c t i o n ” par d é f a u t ( ) s c r i p t PHP, e t c . . . ) // du f o r m u l a i r e l o r s du s u b m i t event . preventDefault ( ) ; // p u b l i c a t i o n a u p r è s du mé d i a t o r myApp . g u i . m e d i a t o r . p u b l i s h ( ” a d r e s s e / c r e a t e ” , { p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e }) ; } // f i n du g e s t i o n n a i r e formHandlerAjout ( ) // E n r e g i s t r e m e n t du Handler du s u b m i t du f o r m u l a i r e v i a jQuery $ ( ”#a j out erAd re sseFo r m ” ) . on ( ” s u b m i t ” , f o rm H a n d l e r A j o u t A d r e s s e ) ; // ///////////////////////////////////////////////////// // G e s t i o n n a i r e de s u b m i t f o r m u l a i r e d ’ a j o u t de a d r e s s e . /* * * G e s t i o n n a i r e de l ’ é v é nement s u b m i t du f o r m u l a i r e . * @param { Event } jQuery e v e n t c o r r e s p o n d a n t au h a n d l e r . */ var f o r m H a n d l e r M o d i f i e r A d r e s s e = function ( e v e n t ) { // É v i t e r d ’ a p p e l e r l ’” a c t i o n ” par d é f a u t ( ) s c r i p t PHP, e t c . . . ) // du f o r m u l a i r e l o r s du s u b m i t event . preventDefault ( ) ; // p u b l i c a t i o n a u p r è s du mé d i a t o r myApp . g u i . m e d i a t o r . p u b l i s h ( ” a d r e s s e / u p d a t e ” , { p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e }) ; } // f i n du g e s t i o n n a i r e formHandlerAjout ( ) // E n r e g i s t r e m e n t du Handler du s u b m i t du f o r m u l a i r e v i a jQuery $ ( ”#modifierAdresseForm ” ) . on ( ” s u b m i t ” , f o r m H a n d l e r M o d i f i e r A d r e s s e ) ; // E n r e g i s t r e r l e s c l i c k s l o r s de l ’ i n i t i a l i s a t i o n registerButtonClickEvents () ; // Permet à l a mé t h o d e q u i r e g é nè re l e panneau d e s d é t a i l s de r e c r é er , // v i a l e mé d i a t o r , l e s é v é nements ” c l i c k ” s u r l e s b o u t o n s dans l e panneau d e s dé t a i l s . myApp . g u i . m e d i a t o r . s u b s c r i b e ( ” per s on n e / d e t a i l s R e b u i l t ” , registerButtonClickEvents ) ; }]) ; 92 Chapitre 5 : Exemple d’Application Interactive 5.5.2 Boutons d’ajout, de suppression, et de modification Le bouton d’ajout d’une adresse, qui existe un un seul exemplaire car il dépend uniquement de la personne, est le plus simple. Il faut créer un formulaire vierge pour la saisie d’une adresse. Comme pour une personne, on utilise la possibilité de passer null comme argument de la fabrique d’adresse, qui crée alors une adresse par défaut, sans créer d’erreurs pour les champs vides (même pour les champs obligatoires). exemples/ihm/ex12_guiBoutonAjouterAdresse.js 1 2 /* * * Dé f i n i t i o n e t e n r e g i s t r e m e n t d e s c a l l b a c k s a p p e l é s à g é r e r l e c l i c s u r l e bouton 3 * ” m o d i f i e r ” l a pe rso nn e s é l e c t i o n n é e . 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s C l i c k A j o u t e r ” , function ( ) { 6 7 /* * 8 * Géné r a t i o n du code HTML du f o r m u l a i r e de m o d i f i c a t i o n de l a p e r s o n n e s é lectionn ée . 9 */ 10 var getHtmlFormInputs = function ( ) { 11 return ”<span s t y l e =\”width :360 px ; display : i n l i n e −b l o c k ; vertical −align : top ; \ ”>” + 12 ”<p s t y l e =\”width : 360 px ; display : i n l i n e −b l o c k ; text −align :c e n t e r ; padding : 15 px ; \ ”>” + 13 ”<tr ong>S a i s i e d ’ une n o u v e l l e a d r e s s e</s trong>” + 14 ”<br />pour ” + myApp . modele . s e l e c t e d P e r s o n n e . g e t P r o p e r t y ( ”nom” ) + ”< /p>” + 15 myApp . g u i . getHtmlFormInputs (myApp . m e t i e r . a d r e s s e . c r e a t e I n s t a n c e ( null ) , ” a jo ut erA dresseF or m ” ) + 16 ”<l a b e l></ l a b e l><i nput t y p e =\” s u b m i t \” v a l u e =\” V a l i d e r \”></ i nput>” + 17 ”</span>” ; 18 } 19 20 /* * 21 * C a l l b a c k d ’ A f f i c h a g e ( v i a l e DOM) du f o r m u l a i r e dans l ’ é l é ment d ’ ID ” mainForm” 22 * @param { O b j e c t } c o n t e x t A r g non u t i l i s é . 23 */ 24 var r e p a i n t F o r m I n p u t s = function ( co n te x t A r g ) { 25 $ ( ”#modifierPersonneForm ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant 26 $ ( ”#ajouterPersonneForm ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant 27 $ ( ”#aj o ut e rA dre sse Form ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant 28 $ ( ”#modifierAdresseForm ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant 29 30 $ ( ”#aj o ut e rA dre sse Form ” ) . append ( getHtmlFormInputs ( ) ) ; // a j o u t e r l e s nouveaux i n p u t s 31 }; 32 33 // E n r e g i s t r e m e n t du c a l l b a c k 34 myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” a d r e s s e / s a i s i e ” , r e p a i n t F o r m I n p u t s ) ; 35 93 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 36 }() ] ) ; Les boutons de modification et de suppression des adresse doivent exister en autant d’exemplaire qu’il y a d’adresse. On crée donc un helper chargé de créer le callback correspondant à chaque adresse. exemples/ihm/ex13_guiBoutonModifierAdresse.js 1 2 /* * * Dé f i n i t i o n e t e n r e g i s t r e m e n t d e s c a l l b a c k s a p p e l é s à g é r e r l e c l i c s u r l e bouton 3 * ” m o d i f i e r ” l a pe rso nn e s é l e c t i o n n é e . 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s C l i c k M o d i f i e r P e r s o n n e ” , function ( ) { 6 7 /* * 8 * Géné r a t i o n du code HTML du f o r m u l a i r e de m o d i f i c a t i o n de l a p e r s o n n e s é lectionn ée . 9 */ 10 var getHtmlFormInputs = function ( a d r e s s e ) { 11 return ”<span s t y l e =\”width :360 px ; display : i n l i n e −b l o c k ; vertical −align : top ; \ ”>” + 12 myApp . g u i . getHtmlFormInputs ( a d r e s s e , ” modifierAdresseForm ” ) + 13 ”<l a b e l></ l a b e l><i nput t y p e =\” s u b m i t \” v a l u e =\” V a l i d e r \”></ i nput>” + 14 ”</span>” ; 15 } 16 17 /* * 18 * C a l l b a c k d ’ A f f i c h a g e ( v i a l e DOM) du f o r m u l a i r e dans l ’ é l é ment d ’ ID ” modifierPersonneForm ” 19 * @param { O b j e c t } c o n t e x t A r g non u t i l i s é . 20 */ 21 var r e p a i n t F o r m I n p u t s = function ( co n te x t A r g ) { 22 $ ( ”#modifierPersonneForm ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant 23 $ ( ”#ajouterPersonneForm ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant 24 $ ( ”#aj o ut e rA dre sse Form ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant 25 $ ( ”#modifierAdresseForm ” ) . empty ( ) ; // Vider l e s i n p u t s e t l e s é v é nements JS existant 26 27 28 $ ( ”#modifierAdresseForm ” ) . append ( getHtmlFormInputs ( c o n t e x t A r g . a d r e s s e ) ) ; // a j o u t e r l e s nouveaux i n p u t s 29 }; 30 31 // E n r e g i s t r e m e n t du c a l l b a c k 32 myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” a d r e s s e / e d i t ” , r e p a i n t F o r m I n p u t s ) ; 33 34 } ( ) ] ) ; exemples/ihm/ex14_guiBoutonSupprimerAdresse.js 1 2 /* * * Dé f i n i t i o n e t e n r e g i s t r e m e n t d e s c a l l b a c k s a p p e l é s à g é r e r l e c l i c s u r l e bouton 94 Chapitre 5 : Exemple d’Application Interactive 3 * ” modifier ” la adresse s é lectionn ée . 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s C l i c k S u p p r i m e r A d r e s s e ” , function ( ) { 6 7 /* * 8 * C a l l b a c k q u i supprime l a a d r e s s e p a s s é e dans l ’ o b j e t p a s s é en argument . 9 * @param { O b j e c t } c o n t e x t A r g argument i n d i q u a n t l a a d r e s s e à su p p r i m e r . 10 * @param { a d r e s s e } c o n t e x t A r g . a d r e s s e r é f é r e n c e de l ’ i n s t a n c e de a d r e s s e à s u ppr i me r dans l e modèle . 11 */ 12 var d e l e t e A d r e s s e = function ( c o n t e x t A r g ) { 13 14 // S u p p r e s s i o n de l ’ a d r e s s e dans l a pe r so n n e 15 c o n t ex t Ar g . p e r s o n n e . d e l e t e A d r e s s e ( c o n t e xt A r g . a d r e s s e ) ; 16 17 // Provoquer l a mise à j o u r de l a vue : 18 myApp . g u i . m e d i a t o r . p u b l i s h ( ” per s on n e / d e t a i l s C h a n g e d ” , { 19 a d r e s s e : myApp . modele . s e l e c t e d P e r s o n n e 20 }) ; 21 } 22 23 // E n r e g i s t r e m e n t du c a l l b a c k 24 myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” a d r e s s e / d e l e t e ” , d e l e t e A d r e s s e ) ; 25 26 } ( ) ] ) ; 5.5.3 Création d’une nouvelle adresse L’adresse est automatiquement ajoutée à la personne sélectionnée, et son ID est généré automatiquement. Comme dans le cas d’une personne, les propriétés de l’adresse (autre que l’ID) sont récupérées à partir des valeurs des inputs du formulaire. exemples/ihm/ex15_guiAjouterAdresseFormValidate.js 1 /* * 2 * Dé f i n i t i o n e t e n r e g i s t r e m e n t du c a l l b a c k r é a g i s s s a n t à l a v a l i d a t i o n ( s u b m i t ) 3 * du f o r m u l a i r e de m o d i f i c a t i o n d ’ une a d r e s s e . 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s V a l i d a t e A j o u t e r A d r e s s e F o r m ” , function ( ) { 6 7 8 /* * 9 * M o d i f i e l e modèle à p a r t i r d e s donn é e s s a i s i e s dans l e f o r m u l a i r e 10 */ 11 var updateModel = function ( ) { 12 // 1) Mise à j o u r d e s donn é e s du modèle 13 // à p a r t i r d e s v a l e u r s d e s i n p u t s du f o r m u l a i r e 14 var propertyName , 15 inputId ; 16 17 // Ajout d ’ un a d r e s s e v i d e dans l a c o l l e c t i o n 18 var n o u v e l l e A d r e s s e = myApp . m e t i e r . a d r e s s e . c r e a t e I n s t a n c e ( null ) ; 19 myApp . modele . s e l e c t e d P e r s o n n e . addAdresse ( n o u v e l l e A d r e s s e ) ; 20 95 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 // Pour chaque p r o p r i é t é ( chaque i n p u t du f o r m u l a i r e ) f o r ( var j =0 ; j< myApp . m e t i e r . a d r e s s e . g e t P r o p e r t y L i s t ( ) . l e n g t h ; ++j ) { propertyName = myApp . m e t i e r . a d r e s s e . g e t P r o p e r t y L i s t ( ) [ j ] ; i f ( propertyName != ” i d ” ) { // c a l c u l de l ’ ID de l ’ i n p u t i n p u t I d = myApp . g u i . g e t I n p u t I d ( { propertyName : propertyName , formId : ” ajou terA d res s e Fo r m ” }) ; // M o d i f i c a t i o n de l a p r o p r i é t é de l a a d r e s s e // a v e c l a v a l e u r s a i s i e dans l ’ i n p u t . n o u v e l l e A d r e s s e . s e t P r o p e r t y ( propertyName , document . getElementById ( i n p u t I d ) . v a l u e ); } } // Provoquer l a mise à j o u r de l a vue ( panneau d e s d é t a i l s ) myApp . g u i . m e d i a t o r . p u b l i s h ( ” per s on n e / d e t a i l s C h a n g e d ” , { p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e }) ; // Provoquer l a r e q u ê t e AJAX pour l ’ i m p l é me n t a t i o n de l a p e r s i s t a n c e myApp . g u i . m e d i a t o r . p u b l i s h ( ” a d r e s s e / c r e a t e d ” , { p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e , adresse : nouvelleAdresse }) ; }; // E n r e g i s t r e m e n t du c a l l b a c k de l ’ é v é nement de v a l i d a t i o n du f o r m u l a i r e myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” a d r e s s e / c r e a t e ” , updateModel ) ; }() ] ) ; 5.5.4 Modification d’une adresse La modification d’une adresse après modification présente la difficulté suivante : il faut retrouver l’instance d’adresse à modifier, parmis les adresses de la personne sélectionnée. Nous avons choisi de mettre un champs caché avec l’ID dans le formulaire (voir la partie 4.3.2). Il nous faut alors rechercher l’ID de l’adresse dans les instances d’adresse de la personne sélectionnée. Nous aurions aussi pu ajouter une référence vers l’adresse éditée dans le modèle. exemples/ihm/ex16_guiModifierAdresseFormValidate.js 1 /* * 2 * Dé f i n i t i o n e t e n r e g i s t r e m e n t du c a l l b a c k r é a g i s s s a n t à l a v a l i d a t i o n ( s u b m i t ) 3 * du f o r m u l a i r e de m o d i f i c a t i o n d ’ une pe r s o n n e . 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s V a l i d a t e M o d i f i e r A d r e s s e F o r m ” , function ( ) { 6 // F or mul ai r e de m o d i f i c a t i o n d ’ une p er s on n e 7 8 /* * 9 * M o d i f i e l e modèle à p a r t i r d e s donn é e s s a i s i e s dans l e f o r m u l a i r e 10 */ 96 Chapitre 5 : Exemple d’Application Interactive 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 var updateModel = function ( ) { // 1) Mise à j o u r d e s donn é e s du modèle // à p a r t i r d e s v a l e u r s d e s i n p u t s du f o r m u l a i r e var propertyName , inputId ; // Recherche de l ’ a d r e s s e q u i a é t é m o d i f i é e à p a r t i r de son ID u n i q u e // L ’ ID s e t r o u v e en champs cach é du f o r m u l a i r e . var i n p u t I d _ i d = myApp . g u i . g e t I n p u t I d ( { propertyName : ” i d ” , formId : ” modifierAdresseForm ” }) ; // ID u n i q u e de l ’ a d r e s s e var i d A d r e s s e = document . getElementById ( i n p u t I d _ i d ) . v a l u e ; var a d r e s s e E n Q u e s t i o n ; f o r ( var i = 0 ; i < myApp . modele . s e l e c t e d P e r s o n n e . g et Nb Ad re ss es ( ) ; ++i ) { i f ( i d A d r e s s e == myApp . modele . s e l e c t e d P e r s o n n e . g e t A d r e s s e ( i ) . g e t P r o p e r t y ( ’ id ’ ) ){ a d r e s s e E n Q u e s t i o n = myApp . modele . s e l e c t e d P e r s o n n e . g e t A d r e s s e ( i ) ; } } i f ( a d r e s s e E n Q u e s t i o n === u n d e f i n e d ) { throw new E r r o r ( ” Adresse i n t r o u v a b l e ( ID i n e x i s t a n t ) ” ) ; } // Pour chaque p r o p r i é t é ( chaque i n p u t du f o r m u l a i r e ) f o r ( var j =0 ; j< myApp . m e t i e r . a d r e s s e . g e t P r o p e r t y L i s t ( ) . l e n g t h ; ++j ) { propertyName = myApp . m e t i e r . a d r e s s e . g e t P r o p e r t y L i s t ( ) [ j ] ; i f ( propertyName != ” i d ” ) { // c a l c u l de l ’ ID de l ’ i n p u t i n p u t I d = myApp . g u i . g e t I n p u t I d ( { propertyName : propertyName , formId : ” modifierAdresseForm ” }) ; // M o d i f i c a t i o n de l a p r o p r i é t é de l a p e r s o n n e // a v e c l a v a l e u r s a i s i e dans l ’ i n p u t . a d r e s s e E n Q u e s t i o n . s e t P r o p e r t y ( propertyName , document . getElementById ( i n p u t I d ) . v a l u e ); } } // Provoquer l a mise à j o u r d e s é l é ments de l a vue o b s e r v a n t l a pe r s o n n e myApp . g u i . m e d i a t o r . p u b l i s h ( ” per s on n e / d e t a i l s C h a n g e d ” , { p e r s o n n e : null }) ; // Provoquer l a mise à j o u r d e s é l é ments de l a vue o b s e r v a n t l a pe r s o n n e myApp . g u i . m e d i a t o r . p u b l i s h ( ” a d r e s s e / changed ” , { p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e , adresse : adresseEnQuestion }) ; }; 97 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 66 67 68 69 70 // E n r e g i s t r e m e n t du c a l l b a c k de l ’ é v é nement de v a l i d a t i o n du f o r m u l a i r e myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” a d r e s s e / u p d a t e ” , updateModel ) ; }() ] ) ; 98 Chapitre 6 Requêtes Asynchrones et API Restful 6.1 Qu’est-ce qu’une requête asynchrone ? Les requêtes asynchrones XMLHttpRequest permettent d’exécuter (suite à une événement côté client) une requête HTTP (exécution d’un CGI, par exemple en PHP) sur le serveur. On parle de requête asynchrone car le client n’est pas bloqué en attendant la réponse du serveur : le déroulement du programme côté client peut se poursuivre, et la réponse du serveur est gérée par des callbacks. Malgré le nom XMLHttpRequest, les requêtes asynchrones permettent d’échanger avec le serveur d’autres types de données que du XML. Nous utiliserons dans ce cours des données JSON. Le codage JSON perrmet de coder sous forme de chaîne de caractères des collections d’objets. Ainsi, on pourra, par exemple, coder en JSON une collection d’objets en PHP (tableau associatif), puis transmettre la chaîne JSON via une requête asynchrone, et enfin reconstituer une collection d’objets en JavaScript pour générer, par exemple, une mise en forme HTML dans le document. Voici un exemple de code JSON d’un tableau associatif (qui contient lui-même un tableau de descriptions de formats) : { "id": 654, "denomination": "Tutoriel JavaScript", "prix unitaire": 0.50, "formats": ["PDF","Postscript","HTML","ePub"] } On peut, par exemple, générer un tel tableau en PHP par le code suivant : 1 <?php 2 $myArray = a r r a y ( ” i d ” => 6 5 4 , 3 ” denomination ” => ” T u t o r i e l J a v a S c r i p t ” , 4 ” p r i x u n i t a i r e ” => 0 . 5 0 , 5 ” f o r m a t s ” => a r r a y ( ”PDF” , ” P o s t s c r i p t ” , ”HTML” , ” ePub ” ) ) ; 6 7 echo json_encode ( $myArray ) ; 8 ?> 99 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 6.2 Requête Ajax Le méthode ajax de jQuery perrmet d’effectuer une requête XMLHttpRequest qui transmet des paramètres (un objet JavaScript) à un CGI (ici en PHP), via une URL. Dans notre exemple, le serveur reçoit lui-même un objet (propriété data) côdé en JSON, et génère lui-même du code JSON. Le programme client récupère du code JSON générée sur la sortie standard du CGI, et reconstitue une objet JavaScript. Voici notre exemple où le code JavaScript côté client récupère une collection d’objet créée par le CGI et la met en forme en HTML. Trois boutons permettent de tester : • Un cas sans erreur ; • Un cas où la gestion d’erreur est implémentée en PHP côté serveur ; • Un cas où la requête AJAX elle-même échoue. Les trois callbacks suivants sont utilisés pour gérer la requête : • success en cas de succès de la requête ; • error en cas d’échec de la requête • complete, ici utilisé pour mettre à jour la vue, que ce soit en cas de succès ou en cas d’échec de la requête. Je programme en JavaScript côté client est le suivant : exemples/ajax/ex02_ajax.html 1 <!doctype html> 2 <html lang=” f r ”> 3 <head> 100 Chapitre 6 : Requêtes Asynchrones et API Restful 4 5 6 7 8 9 10 11 12 13 <meta charset=” utf−8 ”> <t i t l e>Requ ê t e Ajax</ t i t l e> <s c r i p t src=” . / j q u e r y . j s ”></s c r i p t> <l i nk rel=” s t y l e s h e e t ” href=” b a s i c S t y l e . c s s ” /> </head> <body> <h1>Opé r a t i o n <i>GET</ i> Codé avec JSON</h1> <p><button o n c l i c k = ” l a n c e r R e q u e t e ( 1 ) ”>C l i q u e z i c i</button> pour l a n c e r l a requ ê t e s a n s e r r e u r</p> 14 <p><button o n c l i c k = ” l a n c e r R e q u e t e ( 0 ) ”>C l i q u e z i c i</button> pour l a n c e r l a requ ê t e avec une e r r e u r g é r é e par l e s e r v e u r</p> 15 <p><button o n c l i c k = ” l a n c e r R e q u e t e (−1) ”>C l i q u e z i c i</button> pour l a n c e r une requ ê t e q u i é choue</p> 16 17 <p id=” o u t p u t P a r a g r a p h ”></p> 18 <s c r i p t> 19 20 var model = { 21 paragraphText : ” ” , 22 error : null , 23 g e t E r r o r M e s s a g e : function ( ) { 24 return t h i s . error !== null ? ”<br />” + t h i s . error : ” ” ; 25 } 26 }; 27 28 /** 29 * f o n c t i o n c a l l b a c k ex é c u t é e en c a s de s u c c è s de l a r e q u ê t e AJAX. 30 * La mé t h o d e p a r c o u r t l e s donn é e s r e t o u r n é e s par l e s e r v e u r au fo r m at JSON, 31 * e t c o n c a t è n e l e t e x t e dans l e modèle . 32 * @param { O b j e c t } r e t r i e v e d D a t a : c o l l e c t i o n d e s donn é e s d é cod é e s à p a r t i r du JSON . 33 * La donn é e p e u t ê t r e un message d ’ e r r e u r . 34 */ 35 var a j a x C a l l b a c k S u c c e s s = function ( r e t r i e v e d D a t a ) { 36 model . e r r o r = n u l l ; 37 model . paragraphText = ” ” ; 38 // Parcours e t a f f i c h a g e d e s donn é e s de l ’ o b j e t 39 f o r ( var key in r e t r i e v e d D a t a ) { 40 model . paragraphText += key + ” −−−− > ” + r e t r i e v e d D a t a [ key ] +’<br /> ’ ; 41 } 42 }; 43 44 /** 45 * f o n c t i o n c a l l b a c k ex é c u t é e en c a s d ’ é c h e c de l a r e q u ê t e AJAX. 46 * Une e r r e u r e s t a j o u t é e dans l e modèle e t l e t e x t e du p a r a g r a p h e e s t mis à vide . 47 */ 48 var a j a x C a l l b a c k E r r o r = function ( ) { 49 model . paragraphText = ” ” ; 50 model . error = ” Erreur : é c h e c de l a r e q u ê t e AJAX” ; 51 }; 52 53 /** 54 * f o n c t i o n c a l l b a c k ex é c u t é e l o r s q u e l a r e q u ê t e AJAX s e t e r m i n e . 101 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 * Ce c a l l b a c k e s t a p p e l é en c a s d ’ é c h e c ET en c a s de s u c c è s de l a r e q u ê t e AJAX. * I c i , l a mé t h o d e met à j o u r l a vue en a f f i c h a n t l e t e x t e e t une é v e n t u e l l e erreur . */ var a j a x C a l l b a c k C o m p l e t e = function ( ) { $ ( ”#o u t p u t P a r a g r a p h ” ) . append ( ”<p>” + model . paragraphText + model . g e t E r r o r M e s s a g e ( ) + ”</p>” ) ; } /** * G e s t i o n n a i r e de c l i c k s u r l e s boutons , q u i d é c l e n c h e une r e q u ê t e AJAX. * @param { i n t } s i m p l e T e s t V a l u e donn é e t r a n s m i s e au s e r v e u r v i a l a p r o p r i é t é simpleTest * s i s i m p l e T e s t V a l u e e s t né g a t i f , une URL du s e r v e u r i n e x i s t a n t e est u t i l i s ée , * p r o v o q u a n t l ’ é c h e c de l a r e q u ê t e ( c ’ e s t j u s t e pour l ’ exemple ...) . */ var l a n c e r R e q u e t e = function ( s i m p l e T e s t V a l u e ) { var u r l S e r v e u r = ” h t t p :/ / p r o g j s / e x e m p l e s / a j a x / e x 0 1 _ e n c o d e _ j s o n . php ” ; // Pour p r o v o q u e r une r e q u ê t e q u i é choue complètement i f ( s i m p l e T e s t V a l u e <0 ) { // URL q u i n ’ e x i s t e pas u r l S e r v e u r = ” h t t p :/ / p r o g j s / e x e m p l e s / a j a x / b i d o n . php ” ; } // Lancement d ’ une r e q u ê t e AJAX a v e c donn é e s (POST) cod é e en JSON var j q x h r = $ . a j a x ( { // Envoyer l e s donn é e s de l a p er s on n e a v e c l e f o r ma t JSON dataType : ” j s o n ” , u r l : u r l S e r v e u r , // URL du s e r v e u r method : ’ post ’ , // Envoyer l e s donn é e s dans l e t a b l e a u $_POST contentType : ’ application/x−www−form−urlencoded ’ , // donn é e s à t r a n s m e t t r e au s e r v e u r data : { simpleTest : simpleTestValue }, // Mé t h o d e c a l l b a c k q u i r e c o n s t r u i t l e modèle en c a s de s u c c è s success : ajaxCallbackSuccess , // Mé t h o d e c a l l b a c k q u i g è r e une é v é e n t u e l l e e r r e u r dans l a r e q u ê t e error : a j a x C a l l b a c k E r r o r , // Mé t h o d e c a l l b a c k q u i met à j o u r s l a vue l a vue en c a s de s u c c è s ou d ’ erreur complete : ajaxCallbackComplete }) ; 97 98 99 } 100 101 </s c r i p t> 102 </body> Le programme en PHP côté serveur est le suivant : 102 Chapitre 6 : Requêtes Asynchrones et API Restful workspace_progWeb_2a/ajax/ex01_encode_json.php 1 <?php 2 i f ( i s s e t ($_REQUEST[ ’ s i m p l e T e s t ’ ] ) && $_REQUEST[ ’ s i m p l e T e s t ’ ] == 1 ) { 3 $myArray = array ( ’ key1 ’ => 1 1 , ’ key2 ’ => 2 2 , ’ key3 ’ => 3 3 , ’ key4 ’ => 4 4 ) ; 4 } else { 5 $myArray = array ( ’ e r r o r ’ => ”<i>PHP</ i> Cannot r e t r i e v e d a t a ” ) ; 6 } 7 8 // Header HTTP 9 header ( ’ content−t y p e : a p p l i c a t i o n / j s o n ; c h a r s e t=u t f −8 ’ ) ; 10 echo json_encode ( $myArray ) ; 11 ?> 6.3 Qu’est-ce qu’une API REST (ou systèmes Restful) ? L’arcitecture REST (representational state transfer) est, dans notre cadre, une architecture d’application client-serveur, qui permet le lien entre une application côté client en Javascript et un serveur web sur lequel s’exécutent des CGI. Côté serveur, notre application permettra : • Opération GET. De lire toutes les ressources (ici d’une table de base de données) ; • Opération GET. De lire une ressource identifiée (ici une ligne d’une table de base de données) de manière unique (par un identifiant unique) ; • Opération POST. De créer une ressource (ici une ligne d’une table de base de données) avec son identifiant unique ; • Opération PUT. De modifier une ressource identifiée (ici une ligne d’une table de base de données) de manière unique (par un identifiant unique) ; • Opération DELETE. De détruire une ressource identifiée (ici une ligne d’une table de base de données) de manière unique (par un identifiant unique) ; En utilisant cette interface (service web), l’application côté client pourra accéder à la couche persistance du serveur. Des problèmes de sécurité peuvent se poser. Aussi, les opérations ne sont généralement pas toutes acessibles à un même utilisateur. En général, l’opération GET ne permet pas de modifier les données et est moins sensible en matière de sécurité. On aura éventuellement recours aux mêmes techniques d’authentification des utilisateurs ou des programmes clients que dans la sécurisation des sites webs côté serveur à base de CGI uniquement (exemple : gestion avancée du numéro de session). L’exemple qui suit dans ce chapitre ne gère pas les problèmes d’authentification et ne pourra donc pas être utilisé directement en production sans réflexion sur les besoins de sécurité. 6.4 Exemple d’API Restful Nous développons ici l’organisation d’une API Restful qui nous permettra d’implémenter la persistance pour notre application Web en JavaScript présentée au chapitre 5. 103 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery L’implémentation de l’API s’appuie sur le cours de programmation Web côté serveur sur : http://malgouyres.org/progweb 6.4.1 Le Front Controller Le Front Controller nous permet d’identifier l’action, de déterminer si l’utilisateur a des droits suffisants pour exécuter l’action, et d’appeler, en tenant compte du rôle de l’utilisateur et de l’action, le contrôleur dédié qui va implémenter l’action. La gestion des erreurs (comme l’action non définie) sera vue dans la partie 6.4.3. exemples/clientServer/ex01_controleurFront.php 1 <?php 2 /* * 3 * @ b r i e f La c l a s s e C o n t r o l e u r i d e n t i f i e l ’ a c t i o n e t a p p e l l e l a mé t h o d e 4 * pour c o n s t r u i r e l e modèle c o r r e s p o n d a n t à l ’ a c t i o n . 5 * Le c o n t r o l e u r a p p e l l e a u s s i l a vue c o r r e s p o n d a n t e . 6 * I l g è r e a u s s i l e s e x c e p t i o n s e t a p p e l l e l e c a s é ch é ant une vue d ’ e r r e u r . 7 */ 8 c l a s s ControleurFront { 9 10 /* * 11 * @ b r i e f C ’ e s t dans l e c o n t r u c t e u r que l e c o n t r ô l e u r f a i t son t r a v a i l . 12 */ 13 f u n c t i o n __construct ( ) { 14 try { 15 // Ré cup é r a t i o n de l ’ a c t i o n 16 $ a c t i o n = i s s e t ($_REQUEST[ ’ a c t i o n ’ ] ) ? $_REQUEST[ ’ a c t i o n ’ ] : ” ” ; 17 18 // L ’ u t i l i s a t e u r e s t − i l i d e n t i f i é ? S i oui , q u e l e s t son r ô l e ? 19 $modele = A u t h e n t i c a t i o n : : r e s t o r e S e s s i o n ( ) ; 20 // $ r o l e = ( $modele−>g e t E r r o r ( ) === f a l s e ) ? $modele−>g e t R o l e ( ) : ” ” ; 21 // La g e s t i o n d e s r ô l e s au p r o c h a i n é p i s o d e . . . 22 $ r o l e = ” admin ” ; 23 24 // On d i s t i n g u e d e s c a s d ’ u t i l i s a t i o n , s u i v a n t l ’ a c t i o n 25 switch ( $ a c t i o n ) { 26 27 // 1) A c t i o n s c o n c e r n a n t l ’ a u t h e n t i f i c a t i o n : 28 case ” a u t h ” : // Vue de s a i s i e du l o g i n / password 29 case ” v a l i d a t e A u t h ” : // V a l i d a t i o n du l o g i n / password 30 $ a u t h C t r l = new ControleurAuth ( $ a c t i o n ) ; 31 break ; 32 33 // 2) A c t i o n s a c c e s s i b l e s uniquement aux a d m i n i s t r a t e u r s : 34 35 case ” a d r e s s e −u p d a t e ” : // Met à j o u r une Adresse dans l a BD 36 case ” a d r e s s e −c r e a t e ” : // C r a t i o n d ’ une n o u v e l l e Adresse dans l a BD 37 case ” a d r e s s e −d e l e t e ” : // S u p r e s s i o n d ’ une Adresse à p a r t i r de son ID 38 i f ( $ r o l e == ” admin ” ) { 39 $adminCtrl = new ControleurAdminAdresse ( $ a c t i o n ) ; 40 } else { 41 $modele = new Model ( array ( ’ a u t h ’ => ” P e r m i s s i o n non a c c o r d é e ” ) ) ; 42 require ( C o n f i g : :getJsonOutput ( ) [ ” e r r o r H a n d l e d ” ] ) ; 43 } 44 break ; 104 Chapitre 6 : Requêtes Asynchrones et API Restful 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 case case case if ” personne−u p d a t e ” : // Met à j o u r une Adresse dans l a BD ” personne−c r e a t e ” : // C r a t i o n d ’ une n o u v e l l e Adresse dans l a BD ” personne−d e l e t e ” : // S u p r e s s i o n d ’ une Adresse à p a r t i r de son ID ( $ r o l e == ” admin ” ) { $adminCtrl = new ControleurAdminPersonne ( $ a c t i o n ) ; } else { $modele = new Model ( array ( ’ a u t h e n t i c a t i o n ’ => ” P e r m i s s i o n Denied ” ) ) ; require ( C o n f i g : :getJsonOutput ( ) [ ” e r r o r H a n d l e d ” ] ) ; } break ; // 3) A c t i o n s a c c e s s i b l e s aux v i s i t e u r s e t aux a d m i n i s t r a t e u r s : case ” a d r e s s e −g e t ” : // A f f i c h a g e d ’ une Adresse à p a r t i r de son ID case ” a d r e s s e −g e t − a l l ” : // A f f i c h a g e de t o u t e s l e s Adresse ’ s // I c i , l ’ i m p l é me nt at io n ( donc l e c o n t r ô l e u r ) d é pend pas du r ô l e $ p u b l i c C t r l = new C o n t r o l e u r V i s i t o r A d r e s s e ( $ a c t i o n ) ; break ; case ” personne−g e t − a l l ” : // A f f i c h a g e de t o u t e s l e s Personne ’ s $ p u b l i c C t r l = new C o n t r o l e u r V i s i t o r P e r s o n n e ( $ a c t i o n ) ; break ; default : $modele = new Model ( array ( ’ a c t i o n ’ => ” Action non d é f i n i e ( r e s s o u r c e ( s ) introuvables )”) ) ; require ( C o n f i g : :getJsonOutput ( ) [ ” e r r o r H a n d l e d ” ] ) ; } } c a t c h ( E x c e p t i o n $e ) { // Page d ’ e r r e u r par d é f a u t $modele = new Model ( array ( ’ e x c e p t i o n ’ => $e−>getMessage ( ) ) ) ; require ( C o n f i g : :getJsonOutput ( ) [ ” e r r o r H a n d l e d ” ] ) ; } } } ?> 6.4.2 Récupération des données venant du client Les données client sont issues d’une requête AJAX présentée dans les parties 6.5.2 et 6.5.2. Dans le cas d’une personnes, on a envoyé une objet dans une propriété personne. exemples/clientServer/ex02_validationPersonne.php 1 <?php 2 // Les p r o p r i é t é s de l a pe rso nne s o n t dans un t a b l e a u a s s o c i a t i f $_REQUEST[ ’ pe rs o n n e ’ ] 3 // ( v o i r l a p r o p r i é t é d a t a d e s p r o p r i é t é s de l a r e q u ê t e AJAX 4 5 $ i d=” ” ; 6 i f ( i s s e t ($_REQUEST[ ’ per son ne ’ ] [ ’ i d ’ ] ) ) { 7 $ i d = f i l t e r _var ($_REQUEST[ ’ p er s on n e ’ ] [ ’ i d ’ ] , FILTER_SANITIZE_STRING) ; 8 } 9 10 $nom=” ” ; 11 i f ( i s s e t ($_REQUEST[ ’ per son ne ’ ] [ ’nom ’ ] ) ) { 12 $nom = f i l t e r _var ($_REQUEST[ ’ p er s on n e ’ ] [ ’nom ’ ] , FILTER_SANITIZE_STRING) ; 13 } 105 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 14 ?> Dans le cas d’une personnes, on a envoyé une objet dans une propriété personne, qui est de type tableau associatif. exemples/clientServer/ex02_zValidationAdresse.php 1 <?php 2 // Les p r o p r i é t é s de l a pe rso nne s o n t dans un t a b l e a u a s s o c i a t i f $_REQUEST[ ’ adresse ’] 3 // ( v o i r l a p r o p r i é t é d a t a d e s p r o p r i é t é s de l a r e q u ê t e AJAX 4 5 $ i d=” ” ; 6 i f ( i s s e t ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ i d ’ ] ) ) { 7 $ i d = f i l t e r _var ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ i d ’ ] , FILTER_SANITIZE_STRING) ; 8 } 9 $ i d P e r s o n n e=” ” ; 10 i f ( i s s e t ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ i d P e r s o n n e ’ ] ) ) { 11 $ i d P e r s o n n e = f i l t e r _var ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ i d P e r s o n n e ’ ] , FILTER_SANITIZE_STRING) ; 12 } 13 $numeroRue=” ” ; 14 i f ( i s s e t ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ numeroRue ’ ] ) ) { 15 $numeroRue = f i l t e r _var ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ numeroRue ’ ] , FILTER_SANITIZE_STRING) ; 16 } 17 $ r u e=” ” ; 18 i f ( i s s e t ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ rue ’ ] ) ) { 19 $ r u e = f i l t e r _var ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ rue ’ ] , FILTER_SANITIZE_STRING) ; 20 } 21 $complementAdresse=” ” ; 22 i f ( i s s e t ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ complementAdresse ’ ] ) ) { 23 $complementAdresse = f i l t e r _var ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ complementAdresse ’ ] , FILTER_SANITIZE_STRING) ; 24 } 25 $ c o d e P o s t a l=” ” ; 26 i f ( i s s e t ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ c o d e P o s t a l ’ ] ) ) { 27 $ c o d e P o s t a l = f i l t e r _var ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ c o d e P o s t a l ’ ] , FILTER_SANITIZE_STRING) ; 28 } 29 $ v i l l e=” ” ; 30 i f ( i s s e t ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ v i l l e ’ ] ) ) { 31 $ v i l l e = f i l t e r _var ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ v i l l e ’ ] , FILTER_SANITIZE_STRING) ; 32 } 33 $pays=” France ” ; 34 i f ( i s s e t ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ pays ’ ] ) && $_REQUEST[ ’ a d r e s s e ’ ] [ ’ pays ’ ] != ” ” ) { 35 $pays = f i l t e r _var ($_REQUEST[ ’ a d r e s s e ’ ] [ ’ pays ’ ] , FILTER_SANITIZE_STRING) ; 36 } 37 ?> 6.4.3 Génération des données JSON représentant le modèle La classe Config définit les URL des fichiers de génération de JSON, pour éviter les URL en dûr, comme pour les vues d’un CGI. 106 Chapitre 6 : Requêtes Asynchrones et API Restful exemples/clientServer/ex03_config.php 1 <?php 2 /* * 3 * @ b r i e f C l a s s e de c o n f i g u r a t i o n 4 * Donne a c c è s aux paramères sp é c i f i q u e s c o n c e r n a n t l ’ a p p l i c a t i o n 5 * t e l l e s que l e s chemins v e r s l e s vues , l e s v u e s d ’ e r r e u r , 6 * l e s hash pour l e s ID de s e s s i o n s , e t c . 7 */ 8 c l a s s Config 9 { 10 /* * 11 * @ b r i e f r e t o u r n e l e t a b l e a u d e s ( chemins v e r s l e s ) f i c h i e r s de g éné r a t i o n de JSON 12 */ 13 p u b l i c s t a t i c f u n c t i o n getJsonOutput ( ) { 14 // Racine du s i t e 15 global $rootDirectory ; 16 // Ré p e r t o i r e c o n t e n a n t l e s f i c h i e r s de g éné r a t i o n de JSON 17 $ j s o n D i r e c t o r y = $ r o o t D i r e c t o r y . ” json /” ; 18 r e t u r n array ( 19 ” c o l l e c t i o n P e r s o n n e ” => $ j s o n D i r e c t o r y . ” j s o n C o l l e c t i o n P e r s o n n e . php ” , 20 ” i n s t a n c e P e r s o n n e ” => $ j s o n D i r e c t o r y . ” j s o n I n s t a n c e P e r s o n n e . php ”, 21 ” s u c c e s s ” => $ j s o n D i r e c t o r y . ” j s o n S u c c e s s . php ” , 22 ” e r r o r H a n d l e d ” => $ j s o n D i r e c t o r y . ” j s o n E r r o r H a n d l e d . php ” , 23 ); 24 } 25 26 } 27 ?> Pour chaque classe métier, un utilitaire permet de convertir les instances, ou les collections d’instances, en tableaux associatifs. exemples/clientServer/ex05_jsonAdresseJsonUtils.php 1 <?php 2 /* * 3 @ b r i e f Impl é mente l a c o n v e r s i o n d ’ i n s t a n c e s d ’ Adresse e t de c o l l e c t i o n s d ’ adresses 4 * v e r s d e s donn é e s s o u s l a forme de t a b l e a u x a s s o c i a t i f s dans l e b u t de g éné r e r un 5 * codage JSON de c e s donn é e s ( par exemple a v e c l a f o n c t i o n json_encode ( ) ) 6 */ 7 class AdresseJsonUtils { 8 9 /* * 10 * @ b r i e f r e t o u r n e une r e p r é s e n t a t i o n d e s a t t r i b u t s d ’ une i n s t a n c e s o u s forme de t a b l e a u a s s o c i a t i f . 11 * @param a d r e s s e un i n s t a n c e d ’ Adresse à c o n v e r t i r 12 * @return l a r e p r é s e n t a t i o n d e s donn é e s s o u s forme d ’ a r r a y . 13 */ 14 publi c s t a t i c f u n c t i o n instanceToArray ( $adresse ) { 15 $arrayData = array ( 16 ” i d ” => $ a d r e s s e −>g e t I d ( ) , 17 ”numeroRue” => $ a d r e s s e −>getNumeroRue ( ) , 107 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 ” rue ” => $ a d r e s s e −>getRue ( ) , ” complementAdresse ” => $ a d r e s s e −>getComplementAdresse ( ) , ” c o d e P o s t a l ” => $ a d r e s s e −>g e t C o d e P o s t a l ( ) , ” v i l l e ” => $ a d r e s s e −>g e t V i l l e ( ) , ” pays ” => $ a d r e s s e −>getPays ( ) ); r e t u r n $arrayData ; } /* * * @ b r i e f r e t o u r n e une r e p r é s e n t a t i o n d e s a t t r i b u t s d e s i n s t a n c e s s o u s forme de t a b l e a u a s s o c i a t i f . * @param c o l l e c t i o n A d r e s s e un c o l l e c t i o n d ’ Adresse ( s ) à c o n v e r t i r * @return l a r e p r é s e n t a t i o n d e s donn é e s s o u s forme d ’ a r r a y . */ public s t a t i c function collectionToArray ( $collectionAdresses ){ $arrayData = array ( ) ; foreach ( $ c o l l e c t i o n A d r e s s e s a s $ a d r e s s e ) { // Ajout d ’ un é l é ment au t a b l e a u $arrayData [ ] = s e l f : :i n s t a n c e T o A r r a y ( $ a d r e s s e ) ; } r e t u r n $arrayData ; } } // end o f c l a s s AdresseView ?> exemples/clientServer/ex04_jsonPersonneJsonUtils.php 1 <?php 2 /* * 3 @ b r i e f Impl é mente l a c o n v e r s i o n d ’ i n s t a n c e s de Personne e t de c o l l e c t i o n s d ’ adresses 4 * v e r s d e s donn é e s s o u s l a forme de t a b l e a u x a s s o c i a t i f s dans l e b u t de g éné r e r un 5 * codage JSON de c e s donn é e s ( par exemple a v e c l a f o n c t i o n json_encode ( ) ) 6 */ 7 c l a s s PersonneJsonUtils { 8 9 /* * 10 * @ b r i e f r e t o u r n e une r e p r é s e n t a t i o n d e s a t t r i b u t s d ’ une i n s t a n c e s o u s forme de t a b l e a u a s s o c i a t i f . 11 * @param a d r e s s e un i n s t a n c e de Personne à c o n v e r t i r 12 * @return l a r e p r é s e n t a t i o n d e s donn é e s s o u s forme d ’ a r r a y . 13 */ 14 publi c s t a t i c f u n c t i o n instanceToArray ( $personne ) { 15 $arrayData = array ( 16 ” i d ” => $personne −>g e t I d ( ) , 17 ”nom” => $personne −>getNom ( ) , 18 ” a d r e s s e s ” => A d r e s s e J s o n U t i l s : : c o l l e c t i o n T o A r r a y ( $personne −>g e t A d r e s s e s ( ) ) 19 ); 20 21 r e t u r n $arrayData ; 22 } 108 Chapitre 6 : Requêtes Asynchrones et API Restful 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 /* * * @ b r i e f r e t o u r n e une r e p r é s e n t a t i o n d e s a t t r i b u t s d e s i n s t a n c e s s o u s forme de t a b l e a u a s s o c i a t i f . * @param c o l l e c t i o n A d r e s s e un c o l l e c t i o n de Personne ( s ) à c o n v e r t i r * @return l a r e p r é s e n t a t i o n d e s donn é e s s o u s forme d ’ a r r a y . */ public s t a t i c function collectionToArray ( $collectionPersonnes ){ $arrayData = array ( ) ; foreach ( $ c o l l e c t i o n P e r s o n n e s a s $ p e r s o n n e ) { // Ajout d ’ un é l é ment au t a b l e a u $arrayData [ ] = s e l f : :i n s t a n c e T o A r r a y ( $ p e r s o n n e ) ; } r e t u r n $arrayData ; } } ?> À la place des vues dans un CGI, un fichier génère les données JSON correspondant au modèle (ici une collection de personnes). exemples/clientServer/ex06_jsonCollectionPersonne.php 1 <?php 2 $arrayData = array ( ” e r r o r ” => n u l l , ” d a t a ” => P e r s o n n e J s o n U t i l s : : c o l l e c t i o n T o A r r a y ( $modele−>getData ( ) ) ) ; 3 4 header ( ’ content−t y p e : a p p l i c a t i o n / j s o n ; c h a r s e t=u t f −8 ’ ) ; 5 echo json_encode ( $arrayData ) ; 6 ?> Un fichier spécifique permet de renvoyer vers le client les messages correspondant aux erreurs détectées par le serveur (erreurs d’accès au serveur de base de données, données de forme incorrecte, etc.) exemples/clientServer/ex06_jsonErrorHandled.php 1 <?php 2 // On r e t o u r n e l e t a b l e a u a s s o c i a t i f d e s e r r e u r s 3 header ( ’ content−t y p e : a p p l i c a t i o n / j s o n ; c h a r s e t=u t f −8 ’ ) ; 4 echo json_encode ( array ( ” e r r o r ” => $modele−>g e t E r r o r ( ) , ” d a t a ” => array ( ) ) ) ; 5 ?> Un autre fichier permet, dans le cas où aucune donnée n’est attendue du client (comme par exemple la suppression d’une personne) d’indiquer qu’aucune erreur n’a été détectée. exemples/clientServer/ex06_jsonSuccess.php 1 <?php 2 // On r e t o u r n e une e r r e u r n u l l e t un o b j e t dada v i d e 3 header ( ’ content−t y p e : a p p l i c a t i o n / j s o n ; c h a r s e t=u t f −8 ’ ) ; 4 echo json_encode ( array ( ” e r r o r ” => n u l l , ” d a t a ” => array ( ) ) ) ; 5 ?> 109 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 6.4.4 Implémentation des actions des contrôleurs exemples/clientServer/ex07_ControleurVisitorPersonne.php 1 <?php 2 /* * 3 * @ b r i e f La c l a s s e C o n t r o l e u r i d e n t i f i e l ’ a c t i o n e t a p p e l l e l a mé t h o d e 4 * pour c o n s t r u i r e l e modèle c o r r e s p o n d a n t à l ’ a c t i o n . 5 * Le c o n t r o l e u r a p p e l l e a u s s i l a vue c o r r e s p o n d a n t e . 6 * I l g è r e a u s s i l e s e x c e p t i o n s e t a p p e l l e l e c a s é ch é ant une vue d ’ e r r e u r . 7 */ 8 class ControleurVisitorPersonne { 9 10 /* * 11 * @ b r i e f C ’ e s t dans l e c o n t r u c t e u r que l e c o n t r ô l e u r f a i t son t r a v a i l . 12 */ 13 f u n c t i o n __construct ( $ a c t i o n ) { 14 // On d i s t i n g u e d e s c a s d ’ u t i l i s a t i o n , s u i v a n t l ’ a c t i o n 15 switch ( $ a c t i o n ) { 16 case ” personne−g e t ” : // A f f i c h a g e d ’ une Personne à p a r t i r de son ID 17 $ t h i s −>a c t i o n G e t ( ) ; 18 break ; 19 case ” personne−g e t − a l l ” : // A f f i c h a g e de t o u t e s l e s Personne ’ s 20 $ t h i s −>a c t i o n G e t A l l ( ) ; 21 break ; 22 default : // L ’ a c t i o n i n d é f i n i e ( page par d é f a u t , i c i ac cue i l ) 23 require ( C o n f i g : :getVues ( ) [ ” d e f a u l t ” ] ) ; 24 break ; 25 } 26 } 27 28 /* * 29 * @ b r i e f Implemente l ’ a c t i o n ” g e t − a l l ” ( r é c u p è r e t o u t e s l e s i n s t a n c e s ) 30 */ 31 private function actionGetAll () { 32 $modele = M o d e l C o l l e c t i o n P e r s o n n e : :g e t M o d e l P e r s o n n e A l l ( ) ; 33 i f ( $modele−>g e t E r r o r ( ) === f a l s e ) { 34 require ( C o n f i g : :getJsonOutput ( ) [ ” c o l l e c t i o n P e r s o n n e ” ] ) ; 35 } else { 36 require ( C o n f i g : :getJsonOutput ( ) [ ” e r r o r H a n d l e d ” ] ) ; 37 } 38 } 39 40 /* * 41 * @ b r i e f Implemente l ’ a c t i o n ” g e t ” ( r é c u p è r e une i n s t a n c e à p a r t i r de ID ) 42 */ 43 private function actionGet () { 44 // ID de l ’ i n s t a n c e à r é cup é r e r 45 $rawId = i s s e t ($_REQUEST[ ’ i d ’ ] ) ? $_REQUEST[ ’ i d ’ ] : ” ” ; 46 $ i d = f i l t e r _var ( $rawId , FILTER_SANITIZE_STRING) ; 47 $modele = ModelPersonne : :getModelPersonne ( $ i d ) ; 48 i f ( $modele−>g e t E r r o r ( ) === f a l s e ) { 49 require ( C o n f i g : :getJsonOutput ( ) [ ” i n s t a n c e P e r s o n n e ” ] ) ; 50 } else { 51 require ( C o n f i g : :getJsonOutput ( ) [ ” e r r o r H a n d l e d ” ] ) ; 52 } 53 } 110 Chapitre 6 : Requêtes Asynchrones et API Restful 54 55 } ?> exemples/clientServer/ex08_ControleurAdminPersonne.php 1 <?php 2 /* * 3 * @ b r i e f La c l a s s e C o n t r o l e u r i d e n t i f i e l ’ a c t i o n e t a p p e l l e l a mé t h o d e 4 * pour c o n s t r u i r e l e modèle c o r r e s p o n d a n t à l ’ a c t i o n . 5 * Le c o n t r o l e u r a p p e l l e a u s s i l a vue c o r r e s p o n d a n t e . 6 * I l g è r e a u s s i l e s e x c e p t i o n s e t a p p e l l e l e c a s é ch é ant une vue d ’ e r r e u r . 7 */ 8 c l a s s ControleurAdminPersonne { 9 10 /* * 11 * @ b r i e f C ’ e s t dans l e c o n t r u c t e u r que l e c o n t r ô l e u r f a i t son t r a v a i l . 12 */ 13 f u n c t i o n __construct ( $ a c t i o n ) { 14 // On d i s t i n g u e d e s c a s d ’ u t i l i s a t i o n , s u i v a n t l ’ a c t i o n 15 switch ( $ a c t i o n ) { 16 case ” personne−u p d a t e ” : // Met à j o u r une Adresse dans l a BD 17 $ t h i s −>a c t i o n U p d a t e ( ) ; 18 break ; 19 case ” personne−c r e a t e ” : // C r a t i o n d ’ une n o u v e l l e Adresse dans l a BD 20 $ t h i s −>a c t i o n C r e a t e ( ) ; 21 break ; 22 case ” personne−d e l e t e ” : // S u p r e s s i o n d ’ une Adresse à p a r t i r de son ID 23 $ t h i s −>a c t i o n D e l e t e ( ) ; 24 break ; 25 default : // L ’ a c t i o n i n d é f i n i e ( page par d é f a u t , i c i ac cue i l ) 26 $modele = new Model ( array ( ’ a c t i o n ’ => ” Action non d é f i n i e ( r e s s o u r c e ( s ) introuvables )”) ) ; 27 require ( C o n f i g : :getJsonOutput ( ) [ ” e r r o r H a n d l e d ” ] ) ; 28 break ; 29 } 30 } 31 32 /* * 33 * @ b r i e f Implemente l ’ a c t i o n ” u p d a t e ” ( met à j o u r une i n s t a n c e dans l a BD) 34 */ 35 p r i v a t e f u n c t i o n actionUpdate ( ) { 36 // v a l i d e r ou n e t t o y e r l e s i n p u t s ( par exemple : f i l t e r _var ) 37 require ( dirname (__FILE__) . ”/ v a l i d a t i o n P e r s o n n e . php ” ) ; 38 $modele = ModelPersonne : :getModelPersonneUpdate ( $id , $nom ) ; 39 i f ( $modele−>g e t E r r o r ( ) === f a l s e ) { 40 require ( C o n f i g : :getJsonOutput ( ) [ ” s u c c e s s ” ] ) ; 41 } else { 42 require ( C o n f i g : :getJsonOutput ( ) [ ” e r r o r H a n d l e d ” ] ) ; 43 } 44 } 45 46 /* * 47 * @ b r i e f Implemente l ’ a c t i o n ” c r e a t e ” ( c r é e une i n s t a n c e dans l a BD) 48 */ 49 private function actionCreate () { 50 // v a l i d e r ou n e t t o y e r l e s i n p u t s ( par exemple : f i l t e r _var ) 111 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 require ( dirname (__FILE__) . ”/ v a l i d a t i o n P e r s o n n e . php ” ) ; $modele = ModelPersonne : :ge t Mo d e l P e rs o nn e Cr e a t e ( $id , $nom ) ; i f ( $modele−>g e t E r r o r ( ) === f a l s e ) { require ( C o n f i g : :getJsonOutput ( ) [ ” s u c c e s s ” ] ) ; } else { require ( C o n f i g : :getJsonOutput ( ) [ ” e r r o r H a n d l e d ” ] ) ; } } /* * * @ b r i e f Implemente l ’ a c t i o n ” d e l e t e ” ( supprime une i n s t a n c e à p a r t i r de son ID ) */ private function actionDelete () { // ID de l ’ i n s t a n c e à su ppr imer $ i d = f i l t e r _var ($_REQUEST[ ’ i d ’ ] , FILTER_SANITIZE_STRING) ; $modele = ModelPersonne : : d e l e t e P e r s o n n e ( $ i d ) ; i f ( $modele−>g e t E r r o r ( ) === f a l s e ) { require ( C o n f i g : :getJsonOutput ( ) [ ” s u c c e s s ” ] ) ; } else { require ( C o n f i g : :getJsonOutput ( ) [ ” e r r o r H a n d l e d ” ] ) ; } } } ?> exemples/clientServer/ex09_ControleurAdminAdresse.php 1 <?php 2 /* * 3 * @ b r i e f La c l a s s e C o n t r o l e u r i d e n t i f i e l ’ a c t i o n e t a p p e l l e l a mé t h o d e 4 * pour c o n s t r u i r e l e modèle c o r r e s p o n d a n t à l ’ a c t i o n . 5 * Le c o n t r o l e u r a p p e l l e a u s s i l a vue c o r r e s p o n d a n t e . 6 * I l g è r e a u s s i l e s e x c e p t i o n s e t a p p e l l e l e c a s é ch é ant une vue d ’ e r r e u r . 7 */ 8 c l a s s ControleurAdminAdresse { 9 10 /* * 11 * @ b r i e f C ’ e s t dans l e c o n t r u c t e u r que l e c o n t r ô l e u r f a i t son t r a v a i l . 12 */ 13 f u n c t i o n __construct ( $ a c t i o n ) { 14 // On d i s t i n g u e d e s c a s d ’ u t i l i s a t i o n , s u i v a n t l ’ a c t i o n 15 switch ( $ a c t i o n ) { 16 case ” a d r e s s e −u p d a t e ” : // Met à j o u r une Adresse dans l a BD 17 $ t h i s −>a c t i o n U p d a t e ( ) ; 18 break ; 19 case ” a d r e s s e −c r e a t e ” : // C r a t i o n d ’ une n o u v e l l e Adresse dans l a BD 20 $ t h i s −>a c t i o n C r e a t e ( ) ; 21 break ; 22 case ” a d r e s s e −d e l e t e ” : // S u p r e s s i o n d ’ une Adresse à p a r t i r de son ID 23 $ t h i s −>a c t i o n D e l e t e ( ) ; 24 break ; 25 default : // L ’ a c t i o n i n d é f i n i e ( page par d é f a u t , i c i ac cue i l ) 26 require ( C o n f i g : :getJsonOutput ( ) [ ” e r r o r H a n d l e d ” ] ) ; 27 break ; 112 Chapitre 6 : Requêtes Asynchrones et API Restful 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 } } /* * * @ b r i e f Implemente l ’ a c t i o n ” u p d a t e ” ( met à j o u r une i n s t a n c e dans l a BD) */ p r i v a t e f u n c t i o n actionUpdate ( ) { // v a l i d e r ou n e t t o y e r l e s i n p u t s ( par exemple : f i l t e r _var ) require ( dirname (__FILE__) . ”/ v a l i d a t i o n A d r e s s e . php ” ) ; $modele = ModelAdresse : :getModelAdresseUpdate ( $id , $ i d P e r s o n n e , $numeroRue , $rue , $complementAdresse , $ c o d e P o s t a l , $ v i l l e , $pays ) ; i f ( $modele−>g e t E r r o r ( ) === f a l s e ) { require ( C o n f i g : :getJsonOutput ( ) [ ” s u c c e s s ” ] ) ; } else { require ( C o n f i g : :getJsonOutput ( ) [ ” e r r o r H a n d l e d ” ] ) ; } } /* * * @ b r i e f Implemente l ’ a c t i o n ” c r e a t e ” ( c r é e une i n s t a n c e dans l a BD) */ private function actionCreate () { // v a l i d e r ou n e t t o y e r l e s i n p u t s ( par exemple : f i l t e r _var ) require ( dirname (__FILE__) . ”/ v a l i d a t i o n A d r e s s e . php ” ) ; $modele = ModelAdresse : :g e t M o d e l A d r e s s e C r e a t e ( $id , $ i d P e r s o n n e , $numeroRue , $rue , $complementAdresse , $ c o d e P o s t a l , $ v i l l e , $pays ) ; i f ( $modele−>g e t E r r o r ( ) === f a l s e ) { require ( C o n f i g : :getJsonOutput ( ) [ ” s u c c e s s ” ] ) ; } else { require ( C o n f i g : :getJsonOutput ( ) [ ” e r r o r H a n d l e d ” ] ) ; } } /* * * @ b r i e f Implemente l ’ a c t i o n ” d e l e t e ” ( supprime une i n s t a n c e à p a r t i r de son ID ) */ private function actionDelete () { // ID de l ’ i n s t a n c e à su ppr imer $ i d = f i l t e r _var ($_REQUEST[ ’ i d ’ ] , FILTER_SANITIZE_STRING) ; $modele = ModelAdresse : : d e l e t e A d r e s s e ( $ i d ) ; i f ( $modele−>g e t E r r o r ( ) === f a l s e ) { require ( C o n f i g : :getJsonOutput ( ) [ ” s u c c e s s ” ] ) ; } else { require ( C o n f i g : :getJsonOutput ( ) [ ” e r r o r H a n d l e d ” ] ) ; } } } ?> 113 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 6.5 Persistance avec AJAX 6.5.1 Construction du modèle à partir de la base de données exemples/clientServer/ex10_persistanceRead.js 1 /* * 2 * Dé f i n i t i o n e t e n r e g i s t r e m e n t d e s c a l l b a c k s de chargement du modèle 3 * à p a r t i r d e s donn é e s s u r l e s e r v e u r par une r e q u ê t e AJAX. 4 * Permet l e chargement du modèle à p a r t i r de l a b a s e de donn é e s . 5 */ 6 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s R e b u i l d M o d e l F r o m S e r v e r ” , function ( ) { 7 8 /* * 9 * Informe l ’ u t i l i s a t e u r de l ’ e n s e m b l e d e s e r r e u r s d é t e c t é e s e t re n v oy é e s par le serveur . 10 * @param { O b j e c t } d a t a E r r o r c o u p l e s c l e f / v a l e u r , où l a c l e f e s t une c a t é g o r i e d ’ erreur 11 * ( ou un champs de f o r m u l a i r e ) , e t l a v a l e u r un message d ’ erreur . 12 */ 13 var a l e r t E r r o r M e s s a g e s = function ( d a t a E r r o r ) { 14 var concatErrorMsg=” ” ; 15 i f ( d a t a E r r o r !== null ) { 16 fo r ( var key in d a t a E r r o r ) { 17 i f ( d a t a E r r o r . hasOwnProperty ( key ) ) { 18 concatErrorMsg += key + ” : ” + d a t a E r r o r [ key ] + ” \n” ; 19 } 20 } 21 a l e r t ( concatErrorMsg ) ; 22 } 23 }; 24 25 /* * 26 * Mé t h o d e c a l l b a c k q u i e s t a p p e l é e en c a s de s u c c è s de l a r e q u ê t e AJAX. 27 * C e t t e mé t h o d e r e c o n s t r u i t l e modèle à p a r t i r d e s donn é e s du s e r v e u r . 28 * @param { O b j e c t } r e t r i e v e d D a t a donn é e s r e ¸ ues du s e r v e u r ( a p r è s p a r s i n g du JSON) 29 * @param { O b j e c t | n u l l } r e t r i e v e d D a t a . e r r o r n u l l en l ’ a b s e n c e d ’ e r r e u r d é t e c t é e par l e s e r v e u r , 30 * ou un o b j e t dont l e s p r o p r i é t é s s o n t l e s messages d ’ e r r e u r r en v o y é e s par l e s e r v e u r . 31 * @param { O b j e c t } r e t r i e v e d D a t a . d a t a donn é e s re n v o y é e s par l e s e r v e u r : 32 * c o l l e c t i o n s d ’ o b j e t s p e r m e t t a n t de c o n s t r u i r e d e s per so nn es , avec l e u r s a d r e s s e s . 33 */ 34 var a j a x C a l l b a c k S u c c e s s = function ( r e t r i e v e d D a t a ) { 35 var a d r e s s e s D a t a , a d r e s s e I n s t a n c e ; 36 37 // S i aucune e r r e u r n ’ a é t é d é t e c t é e s u r l e s e r v e u r 38 i f ( r e t r i e v e d D a t a [ ” e r r o r ” ] === null && r e t r i e v e d D a t a [ ’ d a t a ’ ] !== u n d e f i n e d ) { 39 // Parcours d e s o b j e t s dans l e s donn é e s 40 fo r ( var key in r e t r i e v e d D a t a [ ’ d a t a ’ ] ) { 41 i f ( r e t r i e v e d D a t a [ ’ d a t a ’ ] . hasOwnProperty ( key ) ) { 42 // Cré a t i o n d ’ une per son n e s a n s a d r e s s e 43 var newPersonne = myApp . m e t i e r . p e r s o n n e . c r e a t e I n s t a n c e ( { 114 Chapitre 6 : Requêtes Asynchrones et API Restful 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 i d : r e t r i e v e d D a t a [ ’ d a t a ’ ] [ key ] [ ” i d ” ] , nom : r e t r i e v e d D a t a [ ’ d a t a ’ ] [ key ] [ ”nom” ] }) ; // Parcours d e s o b j e t s d é f i n i s s a n t l e s a d r e s s e s a d r e s s e s D a t a = r e t r i e v e d D a t a [ ’ d a t a ’ ] [ key ] [ ” a d r e s s e s ” ] ; fo r ( var k e y A d r e s s e in a d r e s s e s D a t a ) { i f ( a d r e s s e s D a t a . hasOwnProperty ( k e y A d r e s s e ) ) { // Cré a t i o n e t a j o u t d ’ une a d r e s s e a d r e s s e I n s t a n c e = myApp . m e t i e r . a d r e s s e . c r e a t e I n s t a n c e ( { id : adressesData [ keyAdresse ] [ ” id ” ] , numeroRue : a d r e s s e s D a t a [ k e y A d r e s s e ] [ ”numeroRue” ] , r u e : a d r e s s e s D a t a [ k e y A d r e s s e ] [ ” rue ” ] , complementAdresse : a d r e s s e s D a t a [ k e y A d r e s s e ] [ ” complementAdresse ” ] , codePostal : adressesData [ keyAdresse ] [ ” codePostal ” ] , v i l l e : adressesData [ keyAdresse ] [ ” v i l l e ” ] , pays : a d r e s s e s D a t a [ k e y A d r e s s e ] [ ” pays ” ] }) ; newPersonne . addAdresse ( a d r e s s e I n s t a n c e ) ; } } myApp . modele . p e r s o n n e s . push ( newPersonne ) ; // a j o u t dans l e modèle } } } else { alertErrorMessages ( retrievedData [ ” error ” ] ) ; } }; /* * * Mé t h o d e a p p e l é e l o r s q u e l a r e q u ê t e AJAX s e termine , * que ce s o i t a p r è s une e r r e u r ou a p r è s un s u c c è s . * C e t t e mé t h o d e r e c o n s t r u i t l a vue ( a p r è s r e c o n s t r u c t i o n du modèle ) . */ var a j a x C a l l b a c k C o m p l e t e = function ( r e t r i e v e d D a t a ) { // Personne s é l e c t i o n n é e par d é f a u t myApp . modele . s e l e c t e d P e r s o n n e = myApp . modele . p e r s o n n e s [ 0 ] ; // La vue e s t r é i n i t i a l i s é e : on v i d e l e s é l é ments e t é v é nements $ ( ”#l i s t e P e r s o n n e s ” ) . empty ( ) ; $ ( ”#v u e D e t a i l ” ) . empty ( ) ; $ ( ”#ajouterPersonneForm ” ) . empty ( ) ; $ ( ”#modifierPersonneForm ” ) . empty ( ) ; $ ( ”#aj o ut e rA dre sse Form ” ) . empty ( ) ; $ ( ”#modifierAdresseForm ” ) . empty ( ) ; // Provoquer l e pr e mier a f f i c h a g e de l a vue : myApp . g u i . m e d i a t o r . p u b l i s h ( ” per s on n e / changed ” , { p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e }) ; // E n r e g i s t r e m e n t d e s é v é nements u t i l i s a t e u r s g é r é s par jQuery myApp . g u i . i n i t J Q u e r y E v e n t s P e r s o n n e ( ) ; myApp . g u i . i n i t J Q u e r y E v e n t s A d r e s s e ( ) ; 115 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 }; /* * * Mé t h o d e a p p e l é e en c a s d ’ e r r e u r de l a r e q u ê t e AJAX e l l e même . */ var a j a x C a l l b a c k E r r o r = function ( r e t r i e v e d D a t a ) { a l e r t ( ” Erreur : é c h e c de l a r e q u ê t e a j a x ” ) ; } /* * * C a l l b a c k a p p e l é l o r s de l ’ é v é nement ” p ers o n n e / rea d ” du mé d i a t o r . * E f f e c t u e une r e q u ê t e AJAX pour r é cup é r e r t o u t e s l e s p e r s o n n e s * pour r e c o n s t r u i r e l e modèle de donn é e s . */ var r e a d A l l P e r s o n n e = function ( ) { // r e q u ê t e AJAX g e t cod é en JSON var j q x h r = $ . a j a x ( { // Envoyer l e s donn é e s de l a p e rs o n n e a v e c l e fo r m a t JSON dataType : ” j s o n ” , u r l : ” h t t p :// p r o g j s / e x e m p l e s / c l i e n t S e r v e r / a p i /” , // URL du s e r v e u r method : ’ p o s t ’ , // Envoyer l e s donn é e s dans l e t a b l e a u $_POST contentType : ’ a p p l i c a t i o n /x−www−form−u r l e n c o d e d ’ , // donn é e s à t r a n s m e t t r e au s e r v e u r data : { a c t i o n : ” personne−g e t − a l l ” }, // Mé t h o d e c a l l b a c k q u i r e c o n s t r u i t l e modèle en c a s de s u c c è s success : ajaxCallbackSuccess , // Mé t h o d e c a l l b a c k q u i g è r e une é v é e n t u e l l e e r r e u r dans l a r e q u ê t e error : ajaxCallbackError , // Mé t h o d e c a l l b a c k q u i met à j o u r s l a vue l a vue en c a s de s u c c è s ou d ’ erreur complete : ajaxCallbackComplete }) ; }; // E n r e g i s t r e m e n t du c a l l b a c k de l ’ é v é nement de r e c o n s t r u c t i o n du modèle myApp . g u i . m e d i a t o r . s u b s c r i b e ( ” per s on n e / read ” , r e a d A l l P e r s o n n e ) ; }() ] ) ; exemples/clientServer/ex10_index.html 1 2 3 4 5 6 7 8 9 10 11 12 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>A p p l i c a t i o n i n t e r a c t i v e</ t i t l e> <l i nk r e l=” s t y l e s h e e t ” h r e f=” b a s i c S t y l e . c s s ” /> </head> <body> <s c r i p t s r c=” . / m o d u l e s M e t i e r P r o t o t y p e . j s ”></s c r i p t> <s c r i p t s r c=” . / modulesIHM . j s ”></s c r i p t> <s c r i p t s r c=” . / p e r s i s t a n c e R e a d . j s ”></s c r i p t> <s c r i p t s r c=” . / p e r s i s t a n c e C r e a t e P e r s o n n e . j s ”></s c r i p t> 116 Chapitre 6 : Requêtes Asynchrones et API Restful 13 <s c r i p t s r c=” . / p e r s i s t a n c e D e l e t e P e r s o n n e . j s ”></s c r i p t> 14 <s c r i p t s r c=” . / p e r s i s t a n c e U p d a t e P e r s o n n e . j s ”></s c r i p t> 15 <s c r i p t s r c=” . / p e r s i s t a n c e C r e a t e A d r e s s e . j s ”></s c r i p t> 16 <s c r i p t s r c=” . / p e r s i s t a n c e U p d a t e A d r e s s e . j s ”></s c r i p t> 17 <s c r i p t s r c=” . / p e r s i s t a n c e D e l e t e A d r e s s e . j s ”></s c r i p t> 18 <!−− Code HTML de l a vue −− S t r u c t u r e g én é r a l e de l a page HTML −−> 19 20 <button i d=” b o u t o n A j o u t e r P e r s o n n e ”>A j o u t e r une p e r s o n n e</button><br /> 21 <span i d=” l i s t e P e r s o n n e s ” c l a s s=” p a n e l ”></span> 22 <span c l a s s=” p a n e l ”> 23 <span i d=” v u e D e t a i l ”> 24 </span><br /><br /> 25 </span> 26 <span i d=”spanMainForm” c l a s s=” p a n e l ”> 27 <form i d=” ajouterPersonneForm ” method=” p o s t ” ></form> 28 <form i d=” modifierPersonneForm ” method=” p o s t ” ></form> 29 <form i d=” a jo ut e r A dre sseFor m ” method=” p o s t ” ></form> 30 <form i d=” modifierAdresseForm ” method=” p o s t ” ></form> 31 </span> 32 33 <!−− I n c l u s i o n de jQuery l e p l u s t r a d p o s s i b l e −−> 34 <s c r i p t s r c=” . / j q u e r y . j s ”></s c r i p t> 35 <s c r i p t s r c=” . / guiJQueryEventsPersonne . j s ”></s c r i p t> 36 <s c r i p t s r c=” . / g u i J Q u e r y E v e n t s A d r e s s e . j s ”></s c r i p t> 37 38 <!−− Ajout d ’ un main e t ex é c u t i o n −−> 39 <s c r i p t> 40 /* * 41 * Sé r i e d ’ i n s t r u c t i o n s e f f e c t u é e s pour i n i t i a l i s e r l ’ a p p l i c a t i o n / 42 * @method mainFunction 43 * @augments myApp 44 */ 45 myApp . addModule ( ” mainFunction ” , function ( ) { 46 47 myApp . addModule . apply (myApp, [ ” modele ” , { 48 s e l e c t e d P e r s o n n e : null , 49 personnes : [ ] , 50 }]) ; 51 52 53 // Charger l e modèle : 54 myApp . g u i . m e d i a t o r . p u b l i s h ( ” pe rs o n n e / rea d ” , { 55 p e r s o n n e : myApp . modele . s e l e c t e d P e r s o n n e 56 }) ; 57 }) ; 58 59 // /////////////////////////////////////////////////// 60 // Exé c u t i o n du Main a v e c un t e s t d ’ e x c e p t i o n 61 // t r y { 62 // Exé c u t i o n de l a mé t h o d e mainFunction 63 myApp . mainFunction ( ) ; 64 // } c a t c h ( e ) { 65 // a l e r t ( e . message ) ; 66 // } 67 </s c r i p t> 68 </body> 117 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 69 </html> 6.5.2 Création, Mise à jour, et suppression des personnes exemples/clientServer/ex11_persistanceCreatePersonne.js 1 /* * 2 * Dé f i n i t i o n e t e n r e g i s t r e m e n t d e s c a l l b a c k s de c r é a t i o n d ’ une pe r s o n n e 3 * s u r l e s e r v e u r par r e q u ê t e AJAX. 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s C r e a t e P e r s o n n e Q u e r y S e r v e r ” , function () { 6 7 /* * 8 * Mé t h o d e c a l l b a c k q u i e s t a p p e l é e en c a s de s u c c è s de l a r e q u ê t e AJAX. 9 * C e t t e mé t h o d e i n f o r m e simplement l ’ u t i l i s a t e u r d e s é v e n t u e l l e s e r r e u r s . 10 * En e f f e t , l a r e q u ê t e n ’ e s t pas s u p p o s é e r e t o u r n e r d e s donn é e s . 11 */ 12 var a j a x C a l l b a c k S u c c e s s = function ( r e t r i e v e d D a t a ) { 13 var concatErrorMsg=” ” ; 14 i f ( r e t r i e v e d D a t a [ ” e r r o r ” ] !== null ) { 15 fo r ( var key in r e t r i e v e d D a t a [ ’ e r r o r ’ ] ) { 16 i f ( r e t r i e v e d D a t a [ ’ e r r o r ’ ] . hasOwnProperty ( key ) ) { 17 concatErrorMsg += key + ” : ” + r e t r i e v e d D a t a [ ’ e r r o r ’ ] [ key ] + ” \n” ; 18 } 19 } 20 a l e r t ( concatErrorMsg ) ; 21 } 22 }; 23 24 /* * 25 * Mé t h o d e a p p e l é e en c a s d ’ e r r e u r de l a r e q u ê t e AJAX e l l e même . 26 */ 27 var a j a x C a l l b a c k E r r o r = function ( r e t r i e v e d D a t a ) { 28 a l e r t ( ” Erreur : é c h e c de l a r e q u ê t e a j a x ” ) ; 29 }; 30 31 /* * 32 * C a l l b a c k a p p e l é l o r s de l ’ é v é nement ” p ers o n n e / rea d ” du mé d i a t o r . 33 * E f f e c t u e une r e q u ê t e AJAX pour r é cup é r e r t o u t e s l e s p e r s o n n e s 34 * pour r e c o n s t r u i r e l e modèle de donn é e s . 35 */ 36 var c r e a t e P e r s o n n e = function ( c o n t e x t A r g ) { 37 38 // r e q u ê t e AJAX g e t cod é en JSON 39 var j q x h r = $ . a j a x ( { 40 dataType : ” j s o n ” , // On e n v o i e l e s donn é e s l a p e r s o n n e cod é e en JSON 41 u r l : ” h t t p :// p r o g j s / e x e m p l e s / c l i e n t S e r v e r / a p i /” , // URL du s e r v e u r 42 method : ’ p o s t ’ , // Envoyer l e s donn é e s dans l e t a b l e a u $_POST 43 contentType : ’ a p p l i c a t i o n /x−www−form−u r l e n c o d e d ’ , 44 // donn é e s à t r a n s m e t t r e au s e r v e u r 45 data : { 46 a c t i o n : ” personne−c r e a t e ” , 47 p e r s o n n e : { // P r o p r i é t é s de l a p e r s o n n e 48 i d : co n t ex t A rg . p e r s o n n e . g e t P r o p e r t y ( ” i d ” ) , 118 Chapitre 6 : Requêtes Asynchrones et API Restful 49 50 51 52 53 54 55 56 57 58 59 60 61 62 nom : c o n t e x tA r g . p e r s o n n e . g e t P r o p e r t y ( ”nom” ) } }, // Mé t h o d e c a l l b a c k q u i r e c o n s t r u i t l e modèle en c a s de s u c c è s success : ajaxCallbackSuccess , // Mé t h o d e c a l l b a c k q u i g è r e une é v é e n t u e l l e e r r e u r dans l a r e q u ê t e error : ajaxCallbackError }) ; }; // E n r e g i s t r e m e n t du c a l l b a c k de l ’ é v é nement de mise à j o u r de l a pe r s o n n e myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” per s on n e / c r e a t e d ” , c r e a t e P e r s o n n e ) ; }() ] ) ; exemples/clientServer/ex12_persistanceUpdatePersonne.js 1 /* * 2 * Dé f i n i t i o n e t e n r e g i s t r e m e n t d e s c a l l b a c k s de m o d i f i c a t i o n d ’ une pe r s o n n e 3 * s u r l e s e r v e u r par r e q u ê t e AJAX. 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s U p d a t e P e r s o n n e Q u e r y S e r v e r ” , function () { 6 7 /* * 8 * Mé t h o d e c a l l b a c k q u i e s t a p p e l é e en c a s de s u c c è s de l a r e q u ê t e AJAX. 9 * C e t t e mé t h o d e i n f o r m e simplement l ’ u t i l i s a t e u r d e s é v e n t u e l l e s e r r e u r s . 10 * En e f f e t , l a r e q u ê t e n ’ e s t pas s u p p o s é e r e t o u r n e r d e s donn é e s . 11 */ 12 var a j a x C a l l b a c k S u c c e s s = function ( r e t r i e v e d D a t a ) { 13 var concatErrorMsg=” ” ; 14 i f ( r e t r i e v e d D a t a [ ” e r r o r ” ] !== null ) { 15 fo r ( var key in r e t r i e v e d D a t a [ ’ e r r o r ’ ] ) { 16 i f ( r e t r i e v e d D a t a [ ’ e r r o r ’ ] . hasOwnProperty ( key ) ) { 17 concatErrorMsg += key + ” : ” + r e t r i e v e d D a t a [ ’ e r r o r ’ ] [ key ] + ” \n” ; 18 } 19 } 20 a l e r t ( concatErrorMsg ) ; 21 } 22 }; 23 24 /* * 25 * Mé t h o d e a p p e l é e en c a s d ’ e r r e u r de l a r e q u ê t e AJAX e l l e même . 26 */ 27 var a j a x C a l l b a c k E r r o r = function ( r e t r i e v e d D a t a ) { 28 a l e r t ( ” Erreur : é c h e c de l a r e q u ê t e a j a x ” ) ; 29 }; 30 31 /* * 32 * C a l l b a c k a p p e l é l o r s de l ’ é v é nement ” p ers o n n e / rea d ” du mé d i a t o r . 33 * E f f e c t u e une r e q u ê t e AJAX pour r é cup é r e r t o u t e s l e s p e r s o n n e s 34 * pour r e c o n s t r u i r e l e modèle de donn é e s . 35 */ 36 var updatePersonne = function ( c o n t e x t A r g ) { 37 38 // r e q u ê t e AJAX g e t cod é en JSON 119 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 var j q x h r = $ . a j a x ( { dataType : ” j s o n ” , // On e n v o i e l e s donn é e s l a p e r s o n n e cod é e en JSON u r l : ” h t t p :// p r o g j s / e x e m p l e s / c l i e n t S e r v e r / a p i /” , // URL du s e r v e u r method : ’ p o s t ’ , // Envoyer l e s donn é e s dans l e t a b l e a u $_POST contentType : ’ a p p l i c a t i o n /x−www−form−u r l e n c o d e d ’ , // donn é e s à t r a n s m e t t r e au s e r v e u r data : { a c t i o n : ” personne−u p d a t e ” , p e r s o n n e : { // P r o p r i é t é s de l a p e r s o n n e i d : co n t ex t A rg . p e r s o n n e . g e t P r o p e r t y ( ” i d ” ) , nom : c o n t e x tA r g . p e r s o n n e . g e t P r o p e r t y ( ”nom” ) } }, // Mé t h o d e c a l l b a c k q u i r e c o n s t r u i t l e modèle en c a s de s u c c è s success : ajaxCallbackSuccess , // Mé t h o d e c a l l b a c k q u i g è r e une é v é e n t u e l l e e r r e u r dans l a r e q u ê t e error : ajaxCallbackError }) ; }; // E n r e g i s t r e m e n t du c a l l b a c k de l ’ é v é nement de mise à j o u r de l a pe r s o n n e myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” per s on n e / u p d a t e ” , updatePersonne ) ; }() ] ) ; exemples/clientServer/ex13_persistanceDeletePersonne.js 1 /* * 2 * Dé f i n i t i o n e t e n r e g i s t r e m e n t d e s c a l l b a c k s de s u p p r e s s i o n d ’ une p e r s o n n e 3 * s u r l e s e r v e u r par r e q u ê t e AJAX. 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s D e l e t e P e r s o n n e Q u e r y S e r v e r ” , function () { 6 7 /* * 8 * Mé t h o d e a p p e l é e en c a s d ’ e r r e u r de l a r e q u ê t e AJAX e l l e même . 9 */ 10 var a j a x C a l l b a c k E r r o r = function ( r e t r i e v e d D a t a ) { 11 a l e r t ( ” Erreur : é c h e c de l a r e q u ê t e a j a x ” ) ; 12 }; 13 14 /* * 15 * C a l l b a c k a p p e l é l o r s de l ’ é v é nement ” p ers o n n e / rea d ” du mé d i a t o r . 16 * E f f e c t u e une r e q u ê t e AJAX pour r é cup é r e r t o u t e s l e s p e r s o n n e s 17 * pour r e c o n s t r u i r e l e modèle de donn é e s . 18 */ 19 var d e l e t e P e r s o n n e = function ( c o n t e x t A r g ) { 20 21 // r e q u ê t e AJAX g e t cod é en JSON 22 var j q x h r = $ . a j a x ( { 23 dataType : ” j s o n ” , // On e n v o i e l e s donn é e s l a p e r s o n n e cod é e en JSON 24 u r l : ” h t t p :// p r o g j s / e x e m p l e s / c l i e n t S e r v e r / a p i /” , // URL du s e r v e u r 25 method : ’ p o s t ’ , // Envoyer l e s donn é e s dans l e t a b l e a u $_POST 26 contentType : ’ a p p l i c a t i o n /x−www−form−u r l e n c o d e d ’ , 27 // donn é e s à t r a n s m e t t r e au s e r v e u r 28 data : { 120 Chapitre 6 : Requêtes Asynchrones et API Restful 29 30 31 32 33 34 35 36 37 38 39 40 a c t i o n : ” personne−d e l e t e ” , i d : c o n t e x tA r g . p e r s o n n e . g e t P r o p e r t y ( ” i d ” ) , }, // Mé t h o d e c a l l b a c k q u i g è r e une é v é e n t u e l l e e r r e u r dans l a r e q u ê t e error : ajaxCallbackError }) ; }; // E n r e g i s t r e m e n t du c a l l b a c k de l ’ é v é nement de mise à j o u r de l a pe r s o n n e myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” per s on n e / d e l e t e ” , d e l e t e P e r s o n n e ) ; }() ] ) ; 6.5.3 Création, Mise à jour, et suppression des adresses exemples/clientServer/ex14_persistanceCreateAdresse.js 1 /* * 2 * Dé f i n i t i o n e t e n r e g i s t r e m e n t d e s c a l l b a c k s de c r é a t i o n d ’ une pe r s o n n e 3 * s u r l e s e r v e u r par r e q u ê t e AJAX. 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s C r e a t e A d r e s s e Q u e r y S e r v e r ” , function () { 6 7 /* * 8 * Mé t h o d e c a l l b a c k q u i e s t a p p e l é e en c a s de s u c c è s de l a r e q u ê t e AJAX. 9 * C e t t e mé t h o d e i n f o r m e simplement l ’ u t i l i s a t e u r d e s é v e n t u e l l e s e r r e u r s . 10 * En e f f e t , l a r e q u ê t e n ’ e s t pas s u p p o s é e r e t o u r n e r d e s donn é e s . 11 */ 12 var a j a x C a l l b a c k S u c c e s s = function ( r e t r i e v e d D a t a ) { 13 var concatErrorMsg=” ” ; 14 i f ( r e t r i e v e d D a t a [ ” e r r o r ” ] !== null ) { 15 fo r ( var key in r e t r i e v e d D a t a [ ’ e r r o r ’ ] ) { 16 i f ( r e t r i e v e d D a t a [ ’ e r r o r ’ ] . hasOwnProperty ( key ) ) { 17 concatErrorMsg += key + ” : ” + r e t r i e v e d D a t a [ ’ e r r o r ’ ] [ key ] + ” \n” ; 18 } 19 } 20 a l e r t ( concatErrorMsg ) ; 21 } 22 }; 23 24 /* * 25 * Mé t h o d e a p p e l é e en c a s d ’ e r r e u r de l a r e q u ê t e AJAX e l l e même . 26 */ 27 var a j a x C a l l b a c k E r r o r = function ( r e t r i e v e d D a t a ) { 28 a l e r t ( ” Erreur : é c h e c de l a r e q u ê t e a j a x ” ) ; 29 }; 30 31 /* * 32 * C a l l b a c k a p p e l é l o r s de l ’ é v é nement ” p ers o n n e / rea d ” du mé d i a t o r . 33 * E f f e c t u e une r e q u ê t e AJAX pour r é cup é r e r t o u t e s l e s p e r s o n n e s 34 * pour r e c o n s t r u i r e l e modèle de donn é e s . 35 */ 36 var c r e a t e A d r e s s e = function ( c o n te x t A r g ) { 37 121 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 // r e q u ê t e AJAX g e t cod é en JSON var j q x h r = $ . a j a x ( { dataType : ” j s o n ” , // On e n v o i e l e s donn é e s l a p e r s o n n e cod é e en JSON u r l : ” h t t p :// p r o g j s / e x e m p l e s / c l i e n t S e r v e r / a p i /” , // URL du s e r v e u r method : ’ p o s t ’ , // Envoyer l e s donn é e s dans l e t a b l e a u $_POST contentType : ’ a p p l i c a t i o n /x−www−form−u r l e n c o d e d ’ , // donn é e s à t r a n s m e t t r e au s e r v e u r data : { a c t i o n : ” a d r e s s e −c r e a t e ” , a d r e s s e : { // P r o p r i é t é s de l ’ a d r e s s e i d : co n t ex t A rg . a d r e s s e . g e t P r o p e r t y ( ” i d ” ) , i d P e r s o n n e : c on t e x t A rg . p e r s o n n e . g e t P r o p e r t y ( ” i d ” ) , numeroRue : c o nt e xt A r g . a d r e s s e . g e t P r o p e r t y ( ”numeroRue” ) , r u e : c on t ex t A rg . a d r e s s e . g e t P r o p e r t y ( ” rue ” ) , c o d e P o s t a l : c on t ex t A rg . a d r e s s e . g e t P r o p e r t y ( ” c o d e P o s t a l ” ) , v i l l e : c o n te x t Ar g . a d r e s s e . g e t P r o p e r t y ( ” v i l l e ” ) , pays : c on t e xt A rg . a d r e s s e . g e t P r o p e r t y ( ” pays ” ) } }, // Mé t h o d e c a l l b a c k q u i r e c o n s t r u i t l e modèle en c a s de s u c c è s success : ajaxCallbackSuccess , // Mé t h o d e c a l l b a c k q u i g è r e une é v é e n t u e l l e e r r e u r dans l a r e q u ê t e error : ajaxCallbackError }) ; }; // E n r e g i s t r e m e n t du c a l l b a c k de l ’ é v é nement de mise à j o u r de l a pe r s o n n e myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” a d r e s s e / c r e a t e d ” , c r e a t e A d r e s s e ) ; }() ] ) ; exemples/clientServer/ex15_persistanceUpdateAdresse.js 1 /* * 2 * Dé f i n i t i o n e t e n r e g i s t r e m e n t d e s c a l l b a c k s de c r é a t i o n d ’ une pe r s o n n e 3 * s u r l e s e r v e u r par r e q u ê t e AJAX. 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s U p d a t e A d r e s s e Q u e r y S e r v e r ” , function () { 6 7 /* * 8 * Mé t h o d e c a l l b a c k q u i e s t a p p e l é e en c a s de s u c c è s de l a r e q u ê t e AJAX. 9 * C e t t e mé t h o d e i n f o r m e simplement l ’ u t i l i s a t e u r d e s é v e n t u e l l e s e r r e u r s . 10 * En e f f e t , l a r e q u ê t e n ’ e s t pas s u p p o s é e r e t o u r n e r d e s donn é e s . 11 */ 12 var a j a x C a l l b a c k S u c c e s s = function ( r e t r i e v e d D a t a ) { 13 var concatErrorMsg=” ” ; 14 i f ( r e t r i e v e d D a t a [ ” e r r o r ” ] !== null ) { 15 fo r ( var key in r e t r i e v e d D a t a [ ’ e r r o r ’ ] ) { 16 i f ( r e t r i e v e d D a t a [ ’ e r r o r ’ ] . hasOwnProperty ( key ) ) { 17 concatErrorMsg += key + ” : ” + r e t r i e v e d D a t a [ ’ e r r o r ’ ] [ key ] + ” \n” ; 18 } 19 } 20 a l e r t ( concatErrorMsg ) ; 21 } 22 }; 122 Chapitre 6 : Requêtes Asynchrones et API Restful 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 /* * * Mé t h o d e a p p e l é e en c a s d ’ e r r e u r de l a r e q u ê t e AJAX e l l e même . */ var a j a x C a l l b a c k E r r o r = function ( r e t r i e v e d D a t a ) { a l e r t ( ” Erreur : é c h e c de l a r e q u ê t e a j a x ” ) ; }; /* * * C a l l b a c k a p p e l é l o r s de l ’ é v é nement ” p ers o n n e / rea d ” du mé d i a t o r . * E f f e c t u e une r e q u ê t e AJAX pour r é cup é r e r t o u t e s l e s p e r s o n n e s * pour r e c o n s t r u i r e l e modèle de donn é e s . */ var u p d a t e A d r e s s e = function ( c o n te x t A r g ) { // r e q u ê t e AJAX g e t cod é en JSON var j q x h r = $ . a j a x ( { dataType : ” j s o n ” , // On e n v o i e l e s donn é e s l a p e r s o n n e cod é e en JSON u r l : ” h t t p :// p r o g j s / e x e m p l e s / c l i e n t S e r v e r / a p i /” , // URL du s e r v e u r method : ’ p o s t ’ , // Envoyer l e s donn é e s dans l e t a b l e a u $_POST contentType : ’ a p p l i c a t i o n /x−www−form−u r l e n c o d e d ’ , // donn é e s à t r a n s m e t t r e au s e r v e u r data : { a c t i o n : ” a d r e s s e −u p d a t e ” , a d r e s s e : { // P r o p r i é t é s de l ’ a d r e s s e i d : co n t ex t A rg . a d r e s s e . g e t P r o p e r t y ( ” i d ” ) , i d P e r s o n n e : c on t e x t A rg . p e r s o n n e . g e t P r o p e r t y ( ” i d ” ) , numeroRue : c o nt e xt A r g . a d r e s s e . g e t P r o p e r t y ( ”numeroRue” ) , r u e : c on t ex t A rg . a d r e s s e . g e t P r o p e r t y ( ” rue ” ) , c o d e P o s t a l : c on t ex t A rg . a d r e s s e . g e t P r o p e r t y ( ” c o d e P o s t a l ” ) , v i l l e : c o n te x t Ar g . a d r e s s e . g e t P r o p e r t y ( ” v i l l e ” ) , pays : c on t e xt A rg . a d r e s s e . g e t P r o p e r t y ( ” pays ” ) } }, // Mé t h o d e c a l l b a c k q u i r e c o n s t r u i t l e modèle en c a s de s u c c è s success : ajaxCallbackSuccess , // Mé t h o d e c a l l b a c k q u i g è r e une é v é e n t u e l l e e r r e u r dans l a r e q u ê t e error : ajaxCallbackError }) ; }; // E n r e g i s t r e m e n t du c a l l b a c k de l ’ é v é nement de mise à j o u r de l a pe r s o n n e myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” a d r e s s e / changed ” , u p d a t e A d r e s s e ) ; }() ] ) ; exemples/clientServer/ex16_persistanceDeleteAdresse.js 1 /* * 2 * Dé f i n i t i o n e t e n r e g i s t r e m e n t d e s c a l l b a c k s de c r é a t i o n d ’ une pe r s o n n e 3 * s u r l e s e r v e u r par r e q u ê t e AJAX. 4 */ 5 myApp . addModule . apply (myApp . gui , [ ” c a l l b a c k s D e l e t e A d r e s s e Q u e r y S e r v e r ” , function () { 6 7 /* * 123 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 * Mé t h o d e c a l l b a c k q u i e s t a p p e l é e en c a s de s u c c è s de l a r e q u ê t e AJAX. * C e t t e mé t h o d e i n f o r m e simplement l ’ u t i l i s a t e u r d e s é v e n t u e l l e s e r r e u r s . * En e f f e t , l a r e q u ê t e n ’ e s t pas s u p p o s é e r e t o u r n e r d e s donn é e s . */ var a j a x C a l l b a c k S u c c e s s = function ( r e t r i e v e d D a t a ) { var concatErrorMsg=” ” ; i f ( r e t r i e v e d D a t a [ ” e r r o r ” ] !== null ) { fo r ( var key in r e t r i e v e d D a t a [ ’ e r r o r ’ ] ) { i f ( r e t r i e v e d D a t a [ ’ e r r o r ’ ] . hasOwnProperty ( key ) ) { concatErrorMsg += key + ” : ” + r e t r i e v e d D a t a [ ’ e r r o r ’ ] [ key ] + ” \n” ; } } a l e r t ( concatErrorMsg ) ; } }; /* * * Mé t h o d e a p p e l é e en c a s d ’ e r r e u r de l a r e q u ê t e AJAX e l l e même . */ var a j a x C a l l b a c k E r r o r = function ( r e t r i e v e d D a t a ) { a l e r t ( ” Erreur : é c h e c de l a r e q u ê t e a j a x ” ) ; }; /* * * C a l l b a c k a p p e l é l o r s de l ’ é v é nement ” p ers o n n e / rea d ” du mé d i a t o r . * E f f e c t u e une r e q u ê t e AJAX pour r é cup é r e r t o u t e s l e s p e r s o n n e s * pour r e c o n s t r u i r e l e modèle de donn é e s . */ var d e l e t e A d r e s s e = function ( c o n t e x t A r g ) { // r e q u ê t e AJAX g e t cod é en JSON var j q x h r = $ . a j a x ( { dataType : ” j s o n ” , // On e n v o i e l e s donn é e s l a p e r s o n n e cod é e en JSON u r l : ” h t t p :// p r o g j s / e x e m p l e s / c l i e n t S e r v e r / a p i /” , // URL du s e r v e u r method : ’ p o s t ’ , // Envoyer l e s donn é e s dans l e t a b l e a u $_POST contentType : ’ a p p l i c a t i o n /x−www−form−u r l e n c o d e d ’ , // donn é e s à t r a n s m e t t r e au s e r v e u r data : { a c t i o n : ” a d r e s s e −d e l e t e ” , i d : c o n t e x tA r g . a d r e s s e . g e t P r o p e r t y ( ” i d ” ) , }, // Mé t h o d e c a l l b a c k q u i r e c o n s t r u i t l e modèle en c a s de s u c c è s success : ajaxCallbackSuccess , // Mé t h o d e c a l l b a c k q u i g è r e une é v é e n t u e l l e e r r e u r dans l a r e q u ê t e error : ajaxCallbackError }) ; }; // E n r e g i s t r e m e n t du c a l l b a c k de l ’ é v é nement de mise à j o u r de l a pe r s o n n e myApp . g u i . m ed i a t o r . s u b s c r i b e ( ” a d r e s s e / d e l e t e ” , d e l e t e A d r e s s e ) ; }() ] ) ; 124 Annexe A Graphisme avec les Canvas HTML5 A.1 Notion de canvas Les canvas HTML5 fournissent une petite API graphique 2D en javascript qui permet de réaliser des dessins, des graphiques, etc. sans plugin. Les canvas 2D sont dores et déjà disponible sur tous les grands navigateurs. L’extension webGL (qui dépasse le cadre de ce cours) permet de faire des affichage de scènes 3D en accédant aux fonctionnalités d’OpenGL via les shaders en GLSL. L’extension webGL est implémentée dans tous les Grands Navigateurs mais n’est pas implémentée à ce jour dans internet explorer car l’éditeur de ce navigateur préfère privilégier une solution propriétaire. Voici un exemple avec un canvas qui dessine un triangle. exemples/canvas/ex01_triangle.html 1 <!doctype HTML> 2 <html lang=” f r ”> 3 <head> 4 <meta charset=”UTF−8” /> 5 <t i t l e>Mon p r e m i e r canvas HTML5</ t i t l e> 6 </head> 7 <body> 8 <!−− Dé c l a r a t i o n d ’ un canvas v i d e a v e c son i d −−> 9 <canvas id=”monCanvas” width=” 2000 ” height=” 1000 ” style= ” position :a b s o l u t e ; ” ></canvas> 10 <s c r i p t> 11 // On r é c u p è r e l e canvas pour d e s s i n e r 12 var myCanvas = document . getElementById ( ”monCanvas” ) ; 13 // On r é c u p è r e un c o n t e x t e du canvas pour u t i l i s e r l e s mé t h o d e s de dessin 14 var c o n t e x t = myCanvas . g e t C o n t e x t ( ”2d” ) ; 15 // c o u l e u r de r e m p l i s s a g e r o u g e 16 c o n t e x t . f i l l S t y l e = ”#FF0000” ; 17 18 c o n t e x t . beginPath ( ) ; 19 c o n t e x t . moveTo ( 1 0 , 1 0 ) ; 20 context . lineTo (100 , 100) ; 21 context . lineTo (190 , 10) ; 22 context . lineTo (10 , 10) ; 23 24 context . f i l l () ; 25 context . closePath () ; 125 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 26 </s c r i p t> 27 <h1>Page HTML avec un canvas</h1> 28 <p> 29 </p> 30 </body> 31 </html> A.2 Exemple d’animation dans un canvas Voici un exemple qui réalise une animation à l’aide d’un timer qui exécute la fonction animate toutes les 20ms, soit 50 fois par seconde. exemples/canvas/ex02_animation.html 1 <!doctype HTML> 2 <html lang=” f r ”> 3 <head> 4 <meta charset=”UTF−8” /> 5 <t i t l e>Mon p r e m i e r canvas HTML5</ t i t l e> 6 </head> 7 <body> 8 <!−− Dé c l a r a t i o n d ’ un canvas v i d e a v e c son i d −−> 9 <canvas id=”monCanvas” width=” 2000 ” height=” 1000 ” style= ” position :a b s o l u t e ; ” ></canvas> 10 <s c r i p t> 11 var t i m e r = s e t I n t e r v a l ( animate , 2 0 ) ; 12 13 function animate ( ) { 14 15 // On r é c u p è r e l e canvas pour d e s s i n e r 16 var canvas = document . getElementById ( ”monCanvas” ) ; 17 // On r é c u p è r e un c o n t e x t e du canvas pour u t i l i s e r l e s mé t h o d e s de dessin 18 var c o n t e x t = canvas . g e t C o n t e x t ( ”2d” ) ; 19 // c o u l e u r de r e m p l i s s a g e r o u g e 20 c o n t e x t . f i l l S t y l e = ”#FF0000” ; 21 c o n t e x t . beginPath ( ) ; 22 var d = new Date ( ) ; 23 var n = d . getTime ( ) ; 24 // nombre de m i l l i s e c o n d e s d e p u i s l e 01 /01/1970 25 26 var s e c = n / 1 0 0 0 . 0 ; 27 c o n t e x t . c l e a r Rect ( 0 , 0 , canvas . width , canvas . height ) ; 28 29 context . save ( ) ; 30 c o n t e x t . t r a n s l a t e (200+500 * (1+Math . c o s ( 0 . 5 * s e c ) ) , 200+200 *(1.0+ Math . s i n ( s e c ) ) ) ; 31 // l ’ a n g l e de r o t a t i o n d o i t ê t r e e n t r e 0 e t 2*Math . PI ’ 32 c o n t e x t . r o t a t e ( s e c − 2*Math . PI *Math . round ( s e c / ( 2 * Math . PI ) ) ) ; 33 c o n t e x t . moveTo ( 0 , 0 ) ; 34 context . lineTo (100 , 100) ; 35 context . lineTo (200 , 0) ; 36 context . lineTo (0 , 0) ; 37 38 context . f i l l () ; 126 Chapitre A : Graphisme avec les Canvas HTML5 39 context . closePath () ; 40 context . r e s t o r e () ; 41 } 42 </s c r i p t> 43 <h1>Page HTML avec un canvas</h1> 44 <p> 45 46 </p> 47 </body> 48 </html> 127 Annexe B Programmation Événementielle en JavaScript B.1 Rappel sur la Gestion d’Événements en CSS Dans un style CSS, on peut mettre des styles différents sur une balise HTML donnée, suivant le contexte utilisateur, via la notion d’événement. Dans l’exmple suivant, le style d’un lien est modifié suivant que le lien a déja été cliqué, ou si la souris survolle le lien (événement hover). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 /* s t y l e par d é f a u t d e s l i e n s */ a :link { text−decoration : none ; color : #00e ; /* b l e u c l a i r */ } /* s t y l e d e s l i e n s v i s i t é s */ a :visited { text−decoration : none ; color : #c 0 c ; /* mauve */ } /* s t y l e d e s l i e n s v i s i t é s */ a :hover { text−decoration : u n d e r l i n e ; /* s o u l i g n é */ color : #e40 ; /* r o u g e v i f */ } Voici un autre exemple, dans lequel un élément HTML (ici une balise <span> et son contenu) apparaît en popup pour afficher les détails d’une personne lors du survol de nom de la personne. La balise span (au sein d’un paragraphe d’une classe CSS spécifique appelé popupDetails) est par défaut invisible (propriété display à none). Cette même balise span devient visible lorsque le paragraphe est survollé. workspace_progWeb_2a/events/ex01_popup_html_css.html 1 <!doctype html> 2 <html l a n g=” f r ”> 3 <head> 4 <meta c h a r s e t=” u t f −8” /> 5 <l i nk r e l=” s t y l e s h e e t ” h r e f=” . / my S tyl e . c s s ” /> 6 <s ty le> 128 Chapitre B : Programmation Événementielle en JavaScript 7 body { 8 font −family : ”Comic Sans MS” ; 9 font−s i z e : 120% ; 10 } 11 h1{ 12 margin : 0 auto ; 13 text −align : c e n t e r ; 14 } 15 p . popupDetails { 16 background−color : y e l l o w ; 17 position : r e l a t i v e ; /* pour position e r l e span en a b s o l u */ 18 max−width : 200 px ; 19 } 20 p . p o p u p D e t a i l s span { 21 display : none ; 22 } 23 p . p o p u p D e t a i l s :hover span { 24 position : a b s o l u t e ; 25 l e f t : 200 px ; 26 top : −30 ; 27 min−width : 500 px ; 28 background−color : b l a c k ; 29 color : w h i t e ; 30 border−r a d i u s : 20 px ; 31 padding : 10 px ; 32 display : b l o c k ; 33 } 34 </s t yl e> 35 <t i t l e>Popups en HTML e t CSS</ t i t l e> 36 </head> 37 <body> 38 <!−− dé but du c o r p s HTML −−> 39 <h1><i>Popup</ i> en <i>HTML</ i> e t <i>CSS</ i></h1> 40 <p c l a s s=” p o p u p D e t a i l s ”> 41 S c a r l e t t Johansson 42 <span>né e l e 22 novembre 1984 à New York , 43 e s t une a c t r i c e e t c h a n t e u s e amé r i c a i n e .<br /> 44 ( s o u r c e  ; : w i k i p é d i a ) 45 </span> 46 </p> 47 </body> 48 <!−− f i n du c o r p s HTML −−> 49 </html> 50 <!−− f i n du code HTML −−> B.2 Événements en Javascript B.2.1 Le principe des événements en Javascript Les événements en Javascript permettent, en réponse à une événement sur un élément HTML du document, d’appeler une fonction callback en Javascript. Ceci suffit à créer une interface homme machine (IHM ) côté client, basée sur de la programmation événementielle en Javascript. Une liste (non exhaustive ; Voir sur le web pour la liste complète) 129 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 1. Événements souris (a) onclick : sur un simple clic (b) ondblclick : sur un double clic (c) onmousedown : lorsque le bouton de la souris est enfoncé, sans forcément le relâcher (d) onmousemove : lorsque la souris est déplacée (e) onmouseout : lorsque la souris sort de l’élément (f) onmouseover : lorsque la souris est sur l’élément (g) onmouseup : lorsque le bouton de la souris est relâché 2. Événements clavier (a) onkeydown : lorsqu’une touche est enfoncée (b) onkeypress : lorsqu’une touche est pressée et relâchée (c) onkeyup : lorsqu’une touche est relâchée 3. Événements formulaire (a) (b) onblur : à la perte du focus (c) onchange : à la perte du focus si la valeur a changé (d) onfocus : lorsque l’élément prend le focus (ou devient actif) (e) onreset : lors de la remise à zéro du formulaire (via un bouton ”reset” ou une fonction reset()) (f) onselect : quand du texte est sélectionné (g) onsubmit : quand le formulaire est validé (via un bouton de type ”submit” ou une fonction submit()) B.2.2 Exemple de mise à jour d’un élément workspace_progWeb_2a/events/ex02_updateElementOnChange.html 1 <!doctype html> 2 <html l a n g=” f r ”> 3 <head> 4 <meta c h a r s e t=” u t f −8” /> 5 <l i nk r e l=” s t y l e s h e e t ” h r e f=” . / my S tyl e . c s s ” /> 6 <s ty le> 7 body { 8 font −family : ”Comic Sans MS” ; 9 font−s i z e : 120% ; 10 } 11 h1{ 12 margin : 0 auto ; 13 text −align : c e n t e r ; 14 } 15 </s t yl e> 130 Chapitre B : Programmation Événementielle en JavaScript 16 <t i t l e>Mise à Jour Par Év é nement</ t i t l e> 17 </head> 18 <body> 19 <!−− dé but du c o r p s HTML −−> 20 <h1>Mise à Jour Par Év é nement <code>onchange</code></h1> 21 <p c l a s s=” p o p u p D e t a i l s ”> 22 <i nput i d=” myInputId ” type=” t e x t ” s i z e=” 15 ” 23 onchange=” f o n c t i o n M i s e A J o u r ( ’ myInputContent ’ , ’ myInputId ’ ) ” /> 24 <br /> 25 <span i d=” myInputContent ”></span> 26 </p> 27 <s c r i p t> 28 function f o n c t i o n M i s e A J o u r ( el eme nt Id , i n p u t I d ) { 29 document . getElementById ( e l e m e n t I d ) . innerHTML 30 = document . getElementById ( i n p u t I d ) . v a l u e ; 31 } 32 </s c r i p t> 33 </body> 34 <!−− f i n du c o r p s HTML −−> 35 </html> 36 <!−− f i n du code HTML −−> B.2.3 Formulaires Dynamiques an Javascript Nous voyons ici un exemple d’utilisation du javascript pour créer un formulaire dont les attributs dépendent de la valeur d’un premier champ. Lorsqu’on sélectionne “deuxième année”, un nouveau champ apparaît. Pour celà, on utilise l’évennement onchange sur l’input de l’année, qui est géré par la fonction anneeChange. On teste alors la valeur de l’attribut, puis le cas échéant on génére un nouveau champ dans un div d’id attributSupplementaire. workspace_progWeb_2a/events/formulaire_dynamique.html 1 <!doctype html> 2 <html l a n g=” f r ”> 3 <head> 4 <meta c h a r s e t=”UTF−8” /> 5 <t i t l e>F o r m u l a i r e dynamique</ t i t l e> 6 </head> 7 <body> 8 <form method=” p o s t ” a c t i o n=” r e c e p t i o n . php ”> 9 <p> 10 <l a b e l fo r=”nom”>Nom</ l a b e l><i nput name=”nom” i d=”nom” /> 131 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 11 12 13 </p> <p> <s e l e c t name=” annee ” i d=” annee ” p a t t e r n=” ( p r e m i e r e ) | ( deuxieme ) ” onchange =’ anneeChange ( ) ; ’> <option v a l u e=” c h o i s i s s e z ” s e l e c t e d d i s a b l e d>−− c h o i s i s s e z −−</option> <option v a l u e=” p r e m i e r e ”>Prem ière ann é e</option> <option v a l u e=” deuxième ”>Deuxième ann é e</option> </s e l e c t> </p> <div i d=” a t t r i b u t S u p p l e m e n t a i r e ”> 14 15 16 17 18 19 20 21 </div> 22 <p> 23 <i nput type=” s u b m i t ” v a l u e=”−− OK −−” /> 24 </p> 25 </form> 26 <s c r i p t> 27 function anneeChange ( ) { 28 var pa ra gr a p h e = document . getElementById ( ” a t t r i b u t S u p p l e m e n t a i r e ” ) ; 29 p a r a g ra p h e . innerHTML=document . getElementById ( ” annee ” ) . v a l u e+” ann é e .<br />” ; 30 i f ( document . getElementById ( ” annee ” ) . v a l u e == ” deuxième ” ) { 31 par a g r aph e . innerHTML+=”<l a b e l>O r i e n t a t i o n pr é vue pour l ’ ann é e p r o c h a i n e :</ l a b e l>” 32 +’<s e l e c t name=” o r i e n t a t i o n ” i d=” o r i e n t a t i o n ”> ’ 33 +’<option v a l u e=”LP”>LP</option> ’ 34 +’<option v a l u e=” master ”>master</option> ’ 35 +”<option v a l u e =\” i n g e \”>E c o l e d ’ i n g é</option>” 36 +’<option v a l u e=” b o u l o t ”>Boulot</option> ’ 37 +’<option v a l u e=” a u t r e ”>Autre</option> ’ 38 +’</s e l e c t> ’ ; 39 40 } 41 } 42 anneeChange ( ) ; 43 </s c r i p t> 44 </body> 45 </html> workspace_progWeb_2a/events/reception.php 1 <!doctype html> 2 <html l a n g=” f r ”> 3 <head> 132 Chapitre B : Programmation Événementielle en JavaScript 4 <meta c h a r s e t=”UTF−8” /> 5 <t i t l e>F o r m u l a i r e dynamique</ t i t l e> 6 </head> 7 <body> 8 <?php 9 $nom= ( i s s e t ($_POST [ ”nom” ] ) ) ? $_POST [ ”nom” ] : ”nom i n d é te r m i n é ” ; 10 $annee = ( i s s e t ($_POST [ ” annee ” ] ) ) ? $_POST [ ” annee ” ] : ”ann é e i n d é temin é e ” ; 11 echo ”Nom : ” . $nom . ”<br />” ; 12 echo ”Anné e : ” . $annee . ”<br />” ; 13 i f ( $annee==” deuxième ” ) 14 echo ” O r i e n t a t i o n : ” . $_POST [ ” o r i e n t a t i o n ” ] ; 15 16 17 ?> 18 </body> 19 </html> 133 Annexe C Gestion des fenêtres C.1 Charger un nouveau document workspace_progWeb_2a/window/ex01_classes_telephone.js 1 2 3 4 5 6 7 8 9 10 11 12 13 // c o n s t r u c t e u r function Telephone ( t e l 1 ) { // t e s t de t é l é phone f r a n ¸ a i s à 10 c h i f f r e s // 1) s uppr ime r l e s e s p a c e s , 2) t e s t e r l e s c h i f f r e s i f ( t e l 1 . r e p l a c e ( / \ s /g , ’ ’ ) . match ( /^ ((\+33) | 0 ) [0 −9]{9} $/g ) ) t h i s . t e l 1=t e l 1 ; else throw new E r r o r ( ”Numé ro de t é l é phone i n v a l i d e ” ) ; } Telephone . p r o t o t y p e . a f f i c h e = function ( ) { document . w r i t e ( ”Té l é phone 1 : ”+t h i s . t e l 1+”<br />” ) ; } workspace_progWeb_2a/window/ex01_loadNewDoc.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>Charger un document</ t i t l e> <s c r i p t s r c=” . / e x 0 1 _ c l a s s e s _ t e l e p h o n e . j s ”></s c r i p t> </head> <body> <p> <s c r i p t> try { var numero = prompt ( ” Merci d ’ e n t r e r un numé ro de t é l é phone en France mé tropolitaine ”) ; var t e l = new Telephone ( numero ) ; tel . affiche () ; } catch ( e r r ) { l o c a t i o n = ” ex01_error . html ” ; } </s c r i p t> <p> 134 Chapitre C : Gestion des fenêtres 20 </body> 21 </html> workspace_progWeb_2a/window/ex01_error.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>Charger un document</ t i t l e> <s c r i p t s r c=” . / e x 1 0 _ c l a s s e s _ t e l e p h o n e . j s ”></s c r i p t> </head> <body> <p> Bonjour , I l s ’ e s t p r o d u i t une e r r e u r . Merci d ’ e n t r e r un numé r o v a l i d e . S i l e problème p e r s i s t e , m e r c i de c o n t a c t e r l e s t a g i a i r e q u i a f a i t l e s i t e . . . <button o n c l i c k=” l o c a t i o n = ’ ex01_loadNewDoc . html ’ ; ”>Retour à l a s a i s i e</button> <p> </body> </html> C.2 Naviguer dans l’historique la propriété history a deux méthodes back() et forward() qui permettent respectivement de reculer ou d’avancer dans l’historique. workspace_progWeb_2a/window/ex02_history.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>Charger un document</ t i t l e> <s c r i p t s r c=” . / e x 1 0 _ c l a s s e s _ t e l e p h o n e . j s ”></s c r i p t> </head> <body> <p> Bonjour , bla , b l a . . .<br /> <a h r e f = ” e x 0 2 _ h i s t o r y B a c k . html ”>C l i q u e z i c i</a> pour a l l e r à l a page s u i v a n t e . <p> </body> </html> workspace_progWeb_2a/window/ex02_historyBack.html 1 2 3 4 5 6 7 8 9 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>Charger un document</ t i t l e> <s c r i p t s r c=” . / e x 1 0 _ c l a s s e s _ t e l e p h o n e . j s ”></s c r i p t> </head> <body> <p> 135 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 10 Bla , b l a . . .<br /> 11 Vous avez r a t é q u e l q u e c h o s e ? 12 <button o n c l i c k=” h i s t o r y . b a c k ( ) ; ”>Retour à l a page pr é c é d e n t e</button> 13 <p> 14 </body> 15 </html> C.3 Ouvrir une nouvelle fenêtre (popup) workspace_progWeb_2a/window/ex03_windowOpen.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>Ouvrir un f e n ê t r e</ t i t l e> <s c r i p t s r c=” . / e x 1 0 _ c l a s s e s _ t e l e p h o n e . j s ”></s c r i p t> </head> <body> <p> Bonjour , bla , b l a . . . <button o n c l i c k=” window . open ( ’ ex03_windowPopup . html ’ , ’ma popup ’ , ’ width=400 , height =400 , r e s i z e a b l e=yes ’ ) ; ”> Plus d ’ i n f o s </button> <p> </body> </html> workspace_progWeb_2a/window/ex03_windowPopup.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>Charger un document</ t i t l e> <s c r i p t s r c=” . / e x 1 0 _ c l a s s e s _ t e l e p h o n e . j s ”></s c r i p t> </head> <body> <p s t y l e=” font−s i z e : 100 ; text −align : c e n t e r ; ”> Coucou ! <p> <p> <a h r e f=” j a v a s c r i p t :window . c l o s e ( ) ; ”>Fermer l a f e n ê t r e</a> </p> </body> </html> 136 Annexe D Document Object Model (DOM) La programmation côté client permet de modifier certaines parties d’un document HTML dans recharger toute la page. Il y a plusieurs avantages : on évite de surcharger le serveur et le trafic réseau et on améliore la réactivité de l’application web pour le plus grand bonheur de l’utilisateur. Pour faire cela, le langage Javascript côté client fournit une structure de données permettant d’accéder aux éléments du document HTML et de modifier les éléments du document HTML. Cette structure de données s’appelle le Document Object Model, en abrégé DOM. Il existe un DOM legacy qui s’est sédimenté informellement au travers des versions successives du javascript en tenant compte des implémentations des différents navigateurs, qui collaboraient plus ou moins bien pour être mutuellement compatibles. Il existe aussi le DOM tel qu’il a été finalement spécifié par le W3C. Les éléments du document HTML ayant, de par leur imbrication, une structure arborescente, le DOM W3C a une structure d’arbre. On peut accèder et manipuler via un ensemble de propriétés et de méthodes javascript, notamment de l’interface Document et de l’interface Element et ses classes filles, qui permettent de manipulet les éléments (HTML entre autres) du document. D.1 Qu’est-ce que le DOM ? Le Document Object Model (en abrégé DOM ) corrrespond à l’arborescence des imbrications des balises HTML d’un document. Voici un fichier HTML simple et une représentation schématique du DOM correspondant. workspace_progWeb_2a/dom/ex01_documentHTML5.html 1 2 3 4 5 6 7 8 9 10 11 12 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>Un document HTML5</ t i t l e> </head> <body> <h1>Exemple de f i c h i e r <i>HTML5</ i></h1> <p> Un document <i>HTML5</ i> e s t une s t r u c t u r e a r b o r e s c e n t e . <p> </body> 137 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 13 </html> Document <html> <body> <head> <title> "Un document HTML5" <meta> <h1> "Exemple de fichier" <p> <i> "Un document" "HTML5" <i> "est une structure arborescente." "HTML5" Le DOM dont nous parlons ici est le DOM du W3C, qui est aujourd’hui supporté par tous les grands navigateurs. Le langage Javascript côté client propose une hiérarchie de classes pour parcourir et manipuler le DOM d’un document. Il s’agit essentiellement d’une structure de donnée d’arbre, où chaque noeud (correspondant à une balise ou commentaire ou texte, etc. du document) possède une collection de noeuds fils, qui sont les éléments ou structures imbriquées. La bibliothèque jQuery permet un accès plus haut niveau au DOM pour sonder et manipuler le code du document. D.2 Sélection et Manipulation de Base sur le DOM D.2.1 Sélection de tout ou partie des éléments L’exemple suivant cherche tous les éléments du document et affiche leur nom de balise (tagName ou nodeName). On apprend aussi à ajouter du code HTML à l’interieur d’un élément (au début ou à la fin). exemples/dom/ex02_basicAllSelector.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!doctype html> <html l a n g=” f r ”> <head> <meta c h a r s e t=” u t f −8”> <t i t l e>C o l l e c t i o n de t o u s l e s é l é ments</ t i t l e> <s c r i p t s r c=” . / j q u e r y − 1 . 1 0 . 2 . j s ”></s c r i p t> <s t y l e> d i v#j a v a s c r i p t O u t p u t D i v { background−color : #ddd ; padding : 5px 0 ; } </s t y l e> </head> 138 Chapitre D : Document Object Model (DOM) 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <body> <h1>C o l l e c t i o n de t o u s l e s é l é ments</h1> <div> <h2>P a r t i e 1</h2> <p>C e c i e s t l e t e x t e de l a p a r t i e 1 .</p> </div> <div> <h2>P a r t i e 2</h2> <p>Le t e x t e de l a p a r t i e 2 e s t d i f f é r e n t .</p> </div> <div i d=” j a v a s c r i p t O u t p u t D i v ”></div> <s c r i p t> // Ré cup é r a t i o n d ’ un e n s e m b l e d ’ é l é ments jQuery var e l e m e n t s = $ ( ” *” ) ; // O b t e n t i o n d ’ un Array d ’ Elements ( I n t e r f a c e du DOM W3C c l a s s i q u e ) var a r r a y E l e m e n t s = e l e m e n t s . toArray ( ) ; // Parcours du t a b l e a u fo r ( var i =0 ; i<a r r a y E l e m e n t s . l e n g t h ; i ++){ // Ajout du nom du t a g HTML dans l e d i v d ’ ID j a v a s c r i p t O u t p u t D i v $ ( ”#j a v a s c r i p t O u t p u t D i v ” ) . append ( a r r a y E l e m e n t s [ i ] . nodeName+” , ” ) ; } // Ajout d ’ un t i t r e AU DÉBUT du d i v d ’ ID j a v a s c r i p t O u t p u t D i v $ ( ”#j a v a s c r i p t O u t p u t D i v ” ) . prepend ( ”<h2>L i s t e d e s é l é ments t r o u v é s</h2>” ) ; </s c r i p t> </body> L’exemple suivant montre commen sélectionner certains éléments du document, par nom de balise, classe CSS, etc. On apprend aussi à modifier des propriétés CSS des éléments. exemples/dom/ex03_basicMultiSelector.html 1 <!doctype html> 2 <html l a n g=” f r ”> 3 <head> 4 <meta c h a r s e t=” u t f −8”> 5 <t i t l e>M o d i f i e r l e s t y l e de c e r t a i n s é l é ments</ t i t l e> 6 <s c r i p t s r c=” . / j q u e r y − 1 . 1 0 . 2 . j s ”></s c r i p t> 7 <s t y l e> 139 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 p . myClass { background−color padding : 10 px ; } </s t y l e> </head> : #ddd ; <body> <h1>M o d i f i e r l e s t y l e de c e r t a i n s é l é ments</h1> <div> <h2>P a r t i e 1</h2> <p>C e c i e s t l e t e x t e de l a p a r t i e 1 .</p> </div> <div> <h2>P a r t i e 2</h2> <p c l a s s=” myClass ”>Le t e x t e de l a p a r t i e 2 e s t d i f f é r e n t .</p> </div> <s c r i p t> // Ré cup é r a t i o n d ’ é l é ments jQuery pour l e s b a l i s e s <p> e t <h2> var e l e m e n t s = $ ( ”p , h2 ” ) ; e l e m e n t s . c s s ( ”border” , ”2 px s o l i d ” ) ; // M o d i f i c a t i o n du s t y l e du t i t r e <h1> $ ( ” h1 ” ) . c s s ( ” text −align ” , ” c e n t e r ” ) ; // M o d i f i c a t i o n du s t y l e du ( ou d e s ) p a r a g r a p h e ( s ) de l a c l a s s e CSS myClass $ ( ”p . myClass ” ) . c s s ( ”border−r a d i u s ” , ” 20 px ” ) ; </s c r i p t> </body> D.2.2 Filtrage par le texte L’exemple suivant montre comment sélectionner des éléments par mots du texte (sensible à la casse). exemples/dom/ex04_contains.html 1 <!doctype html> 2 <html l a n g=” f r ”> 140 Chapitre D : Document Object Model (DOM) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <head> <meta c h a r s e t=” u t f −8”> <t i t l e>F i l t r a g e du t e x t e</ t i t l e> <s c r i p t s r c=” . / j q u e r y − 1 . 1 0 . 2 . j s ”></s c r i p t> <s t y l e> p { padding : 10 px 0 ; } </s t y l e> </head> <body> <h1>F i l t r a g e du t e x t e</h1> <div> <h2>P a r t i e 1</h2> <p>C e c i e s t l e t e x t e de l a p a r t i e 1 .</p> </div> <div> <h2>P a r t i e 2</h2> <p>Le t e x t e de l a <em>p a r t i e 2</em> e s t d i f f é r e n t .</p> </div> <s c r i p t> $ ( ” p :c o n t a i n s ( ’ d i f f é r e n t ’ ) ” ) . prepend ( ’<s trong>Ce p a r a g r a p h e c o n t i e n t l e mot ” d i f f é r e n t ”</s trong>.<br /> ’ ) . c s s ( ”background−color ” , ”#ddd ” ) ; 25 </s c r i p t> 26 </body> D.2.3 Application de Méthode aux éléments L’exemple suivant montre comment appliquer une fonction à chacun des éléments sélectionnés. Ici, on met le contenu des paragraphes en gras. On ajoute une information au début de chaque paragraphe de la classe myClass. exemples/dom/ex05_each.html 1 <!doctype html> 2 <html l a n g=” f r ”> 141 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <head> <meta c h a r s e t=” u t f −8”> <t i t l e>A p p l i q u e r une mé thode à chaque é l é ment</ t i t l e> <s c r i p t s r c=” . / j q u e r y − 1 . 1 0 . 2 . j s ”></s c r i p t> <s t y l e> p . myClass { background−color : #ddd ; padding : 10 px ; } </s t y l e> </head> <body> <h1>A p p l i q u e r une mé thode à chaque é l é ment</h1> <div> <h2>P a r t i e 1</h2> <p>C e c i e s t l e t e x t e de l a p a r t i e 1 .</p> </div> <div> <h2>P a r t i e 2</h2> <p c l a s s=” myClass ”>Le t e x t e de l a p a r t i e 2 e s t d i f f é r e n t .</p> </div> <s c r i p t> $ ( ” p” ) . each ( function ( ) { $ ( t h i s ) . c s s ( ” font−weight” , ” b o l d e r ” ) ; i f ( $ ( t h i s ) . h a s C l a s s ( ” myClass ” ) ) { $ ( t h i s ) . prepend ( ”<em>p a r a g r a p h e de c l a s s e myClass</em><br />” ) ; } }) ; </s c r i p t> </body> 142 Chapitre D : Document Object Model (DOM) D.2.4 Événements et Callbacks EL’exemple suivant montre comment, en réaction au click sur un bouton, transformer es paragraphes en div. exemples/dom/ex06_clickEvent.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <!doctype html> <html l a n g=” f r ”> <head> <meta c h a r s e t=” u t f −8”> <t i t l e>Év é nement de c l i c k</ t i t l e> <s c r i p t s r c=” . / j q u e r y − 1 . 1 0 . 2 . j s ”></s c r i p t> <s t y l e> p { background−color : #ddd ; padding : 10 px ; } d i v . myClass { font−weight : b o l d e r ; padding : 10 px ; border −style : dashed ; } em { font −variant : s m a l l −ca p s ; font−s i z e : 120% ; } button { margin : 10 px 0 ; } </s t y l e> </head> <body> <h1>Év é nement de c l i c k</h1> <div> 143 Rémy Malgouyres, http://malgouyres.org Programmation Web Côté Client, JS et jQuery 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <h2>P a r t i e 1</h2> <p>C e c i e s t l e t e x t e de l a p a r t i e 1 .</p> </div> <div> <h2>P a r t i e 2</h2> <p>Le t e x t e de l a <em>p a r t i e 2</em> e s t d i f f é r e n t .</p> </div> <button>M o d i f i e r l e s p a r a g r a p h e s</button> <s c r i p t> // Év é nement de c l i c k $ ( ” b u t t o n ” ) . c l i c k ( function ( ) { // A p p l i c a t i o n d ’ une mé t h o d e à chaque p a r a g r a p h e $ ( ”p” ) . each ( function ( ) { // Remplacer l e <p> par un <div> en l a i s s a n t l e HTML i n c h a n g é $ ( t h i s ) . r e p l a c e W i t h ( ’<div c l a s s=” myClass ”> ’ + $ ( t h i s ) . html ( ) + ”</div>” ) ; }) ; }) ; </s c r i p t> </body> D.2.5 Fitrage d’un Tableau L’exemple suivant montre comment, en utilisant les utilitaires de jQuery permettant de traiter des Array Javascript génériques : 1. Filtrer le contenu d’un tableau avec une méthode de choix booléenne pour les éléments (ici, valeur multiple de 3) ; 2. Générer le HTML en appliquant une méthode à chaque élément du tableau. 144 Chapitre D : Document Object Model (DOM) exemples/dom/ex07_filterGrep.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <!doctype HTML> <html l a n g=” f r ”> <head> <meta c h a r s e t=”UTF−8” /> <t i t l e>F i l t r a g e Grep s u r Tableau</ t i t l e> </head> <body> <h1>F i l t r a g e Grep s u r Tableau</h1> <s c r i p t s r c=” . / j q u e r y − 1 . 1 0 . 2 . j s ”></s c r i p t> <p i d=” o u t p u t ”><p> <s c r i p t> // Cré a t i o n d ’ un t a b l e a u a v e c l e s e n t i e r s de 0 à 19 var tab = new Array ( ) ; fo r ( var i =0 ; i<20 ; i ++){ tab . push ( i ) ; } // Sé l e c t i o n d e s é l é ments du t a b l e a u par l a f o n c t i o n ” m u l t i p l e de 3” var t a b M u l t i p l e D e 3 = $ . g r e p ( tab , function ( key , v a l u e ) { i f ( key%3 == 0 ) return true ; else return f a l s e ; }) ; // A f f i c h a g e du t a b l e a u d e s m u l t i p l e s de 3 var outHTML = ” ” ; // A p p l i c a t i o n d ’ une f o n c t i o n ( g éné r a t i o n d ’HTML) // à chaque é l é ment du t a b l e a u $ . each ( tabMultipleDe3 , function ( key , v a l u e ) { outHTML += ” t a b [ ”+key +” ] = ”+v a l u e+”<br />” ; }) ; $ ( ”#o u t p u t ” ) . append ( outHTML ) ; </s c r i p t> </body> </html> 145
Documents pareils
Reverse Proxy, Apache et sécurisation d`application
Bien évidemment, il faudra, à chaque nouveau VHost, et donc nouveau "client" accédant à une application différente,
créer dans le DNS un autre nom mappé sur cette adresse IP. Ceci pour bien cloison...