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&nbsp ; :</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 ’ ) + ”&nbsp ; :</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 ’ )
+ ”&nbsp ; :</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 ’ ) + ”&nbsp ; :</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 ’ ) + ”&nbsp ; :</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&nbsp ; : 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