Introduction ● AJAX: Asynchronous Javascript and XML ● Similarités entre les WebServices et les requêtes XmlHttpRequest. (REST Services) ● Une requête est passée, elle contient une fonction et des paramètres. Une réponse nous est retournée en XML, Text ou Javascript (JSON) ● ● Les requêtes ont un but spécifique et donc remplissent une fonction spécifique. La démo de cette présentation se trouve ici : http://www.newcommerce.ca/demo Crédit photo: http://ptitecocci.deviantart.com AJAX – Exemple d'utilisation ● ● ● ● Je suis sur Google Suggest et je cherche à faire une recherche. Je commence à taper quelques lettres dans le but de former un mot. À chaque frappe, la page web (html + javascript) côté client attrape l'événement et fait une requête auprès du service de suggestion de google pour obtenir des possibilités de recherches basées sur les lettres entrez. ( GET http://www.google.com/complete/search?hl=en&js=true&qu=g ) Google suggest fait une recherche du côté serveur et retourne en format javascript les recherches les plus populaires débutant par les lettres entrées par l'utilisateur. ( sendRPCDone(frameElement, "gjr", new Array("gjr", "gjr.paknet.com.pk", "gjr garch", "gjradio", "gjrealsource", "gjr krishnan", "gjrealsource.com", "gjrentals", "gjrentals.com", "gjrealtors.org"), new Array("68,900 results", "1 result", "861 results", "101 results", "162 results", "591 results", "1 result", "29 results", "1 result", "&nbsp;"), new Array(""));) La page web (html+javascript) reçoit ces informations et les démontre à l'utilisateur à l'aide de dHTML et Javascript Introduction à l'objet XmlHttpRequest ● L'object xmlHttpRequest est utilisé en Javascript pour communiquer avec le serveur HTTP, soit le site web. <code:javascript> function getXMLHTTP(){ var xhr = null; if(window.XMLHttpRequest) // Firefox et autres xhr = new XMLHttpRequest(); else if(window.ActiveXObject){ // Internet Explorer try { xhr = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } } else { // XMLHttpRequest non supporté par le navigateur alert("Votre navigateur ne supporte pas les objets XMLHTTPRequest..."); xhr = false; } return xhr; } </code:javascript> Objectif ● ● ● Utiliser AJAX pour la validation de données dans un formulaire Développeer l'algorithme de validation : Méchanisme de validation en temps réel, c'est-à-dire au fur et à mesure que les données sont insérées dans le formulaire. ● Il y aura trois états pour le champ: jaune, vert et rouge ● Affichage du message d'erreur à côté du champ à valider. État du champ ● ● ● Initialement, le champ n'a pas d'état. Son background est blanc et il n'y a aucun message à côté Une fois le focus activé sur le champ, il entre à l'état Jaune. C'est-à-dire que le background du champ change et un message guidant l'entrée de donnée peut apparaître à sa droite. Si le champ a une longueur de contenu fixe, c'est-à-dire que son contenu doit être composé d'un contenir un certain nombre de caractères et que ce nombre est atteint alors que les données saisies ne répondent pas au critère de validation, le champ passe au Rouge. Dans les mêmes conditions si les données saisies correspondent au critère de validation, le champ passe au Vert. État du champ (suite) ● ● Si l'utilisateur quitte le champ alors que les données qui y sont saisies ne correspondent pas au critère de validation, le champ passe au Rouge. Si l'utilisateur quitte le champ alors que les données répondent au critère de validation, le champ passe au Vert. État du champ (suite) État initial le champ n'a pas reçu le focus encore Jaune le champ reçoit le focus et le visiteur tape Rouge les données saisies ne répondent pas au critère de validation Vert les données saisies répondent au critère de validation Types de données à traiter ● ● ● ● ● ● Données qui sont validables avec une simple fonction javascript ou une regular expression. Des données à format prédéfini. Par exemple un numéro de téléphone ou un code postal. Aide à l'utilisateur pour formater le champ en temps réel. Données qui nécessitent une vérification du côté serveur pour la validation Pour empêcher la duplication de certaines informations (nom d'utilisateur) Pour empêcher un utilisateur d'avoir deux comptes (adresse email) Intro. manipulation html javascript ● Chaque balise HTML peut contenir un identificateur unique: son 'id' <input type='text' id='zipcode' length='6' /> ● Pour obtenir un élément on utilise la commande 'getElementById' ou sa version simplifiée (crossbrowser friendly) comme suit : Intro. manipulation html javascript <code:javascript> function getElement(layerID) { if (document.getElementById) return document.getElementById(layerID); else if (document.all) return document.all.layerID; else // on a un problème return null; } // exemple var zipCodeEl = getElement('zipcode'); </code:javascript> Intro. manipulation html javascript ● De là on peut éditer toutes ses propriétés incluant ses éléments de style CSS <code:javascript> function setColors(el, bg, fg){ if (el.style){ if(bgl) el.style.backgroundColor = bg; if(fg) el.style.color = fg; } } // obtenir l'élément var zipCodeEl = getElement(“zipcode'); // changer sa couleur de background à jaune, et de foreground (texte) à noir. setColors(zipCodeEl, “#cad116”, “#000000”); </code:javascript> Intro. manipulation html javascript ● Exemple de d'autres propriétés qu'on peut modifier: ● .style.width, .style.height ● .style.margin ● .style.border, borderColor, borderStyle ... ● .style.clear, .style.cursor, .style.display ● .style.font, fontFamily, fontSize, fintStretch, fontWeight ● .style.lineHeight ● .style.lef, top (position) ● .style.overflow, .style.opacity, .style.padding ● InnerHTML ***, etc.. Intro. manipulation html javascript ● Pour obtenir toute la liste, vous pouvez utiliser le DOM Inspector. Sélectionnez une node dans la fenêtre de gauche et dans la fenêtre de droite choisissez Object – Javascript Object. Utilisation de la propriété innerHTML ● Étant donnée la structure du HTML, c'est-à-dire que chaque tag peut en contenir d'autres, nous avons en notre possession une propriété TRÈS utile: .innerHTML. Prenez le tableau suivant: <table id='tableau' cellspacing='1' cellpadding='4'> <tr id='range'> <td id='col01'>Hello</td> <td id='col02'>Bonjour</td> </tr> </table> Utilisation de la propriété innerHTML ● Maintenant on peut manipuler ce tableau et son contenu directement en javascript <code:javascript> var tableau = getElement('tableau'); var range = getElement('range'); var col1 = getElement('col01'); var col2 = getElement('col02'); col1.innerHTML = “Konishuwa”; col2.innerHTML = “Buenas Dias”; range.innerHTML = “<td>Konishuwa</td><td>Buenas Dias</td> <td>Danke</td>”; tableau = “<tr><td>Konishuwa</td><td>Buenas Dias</td><td>Danke</td></tr> <tr><td>Bonjour</td><td>Hello</td><td> .... </td></tr>”; </code:javascript> Utilisation de la propriété innerHTML ● Les possibilités sont illimitées... Champ, couleurs et message d'erreur ● dans notre exemple précédent nous avons à gauche un libellé, suivit de notre champ et à droite le message d'erreur. En HTML, en utilisant les tableaux, cela se traduit en... <table border=”0” cellpadding=”2” cellspacing=”0”> <tr bgcolor="#0A3F7B"> <td width="33%" align="right">Code Postal</td> <td width="25%" bgcolor="#0A3F7B"><input name="codepostal" type="text" id="codepostal" size="7" maxlength="7"></td> <td width="42%" nowrap id='codepostal_status'>&nbsp;</td> </tr> </table> on a donc 2 éléments HTML qui contiennent un id. Le champ et la cellule de tableau à sa droite. Changer le statut du champ ● La cellule qui contient le message d'erreur a un id de “id du champ”+'_status', ce qui facilite la tache. <code:javascript> function changeHTML(element, text) { elementObject = getElement(element); elementObject.innerHTML = text; } setFieldState(“zipCode”, “yellow”, “H0H 0H0”); </code:javascript> Changer le statut du champ (suite) ● Définir la fonction maintenant <code:javascript> function setFieldState(field, state, message) { var messageId = field+”_status”; stateBg = {yellow: “#cad116”, red: “#bb6969”, green: “#b6b6b6”}; var bgColor = stateBg[state]; var fgColor = “#000000”; changeHTML(messageId, message); setColors(getElement(field), bgColor, fgColor); setColors(getElement(messageId), null, bgColor); } </code:javascript> Les événements ● Il existe plusieurs événements en Javascript qui sont lancés pour les éléments '<input>' tels que le champ de saisi texte. Ils sont : ● onfocus ● onblur (perte de focus) ● onkeyup ● onkeydown ● onchange ● onmouseover, onmouseout, onmousedow, onmouseup ● Pour une liste complète consultez: http://www.devguru.com/technologies/javascript/ Les événements (suite) ● ● Nous sommes intéressés à plusieurs événements notamment 'onkeyup', 'onfocus', 'onblur'. L'événement onChange ne fonctionne pas de façon cohérente à notre utilisation, alors on n'en prendra pas compte. Le champ zipCode devient donc: <input type='text' id='zipcode' name='zipcode' onblur=”validate('zipcode', false)” onkeyup=”validate('zipcode', true)” onfocus=”validate('zipcode', true)'> La fonction de validation ● Notre fonction de validation doit prendre en charge plusieurs types de validation: ● La validation par regular expression ● La validation par fonction javascript ● La validation par appel AJAX ● La façon propre de le faire serait par Objet. Toutefois étant donné les circonstances j'utiliserai des array. La fonction de validation (suite) ● Le régistre d'informations de validation <code:javascript> var fields = new Object(); fields.zipcode = { minLength: 7, maxLength: 7, regExp: "/^[a-zA-Z]{1}[0-9]{1}[a-zA-Z]{1}[ .-][0-9]{1}[a-zA-Z]{1}[0-9]{1}$/i", valFunction: null, hint: "V0A 1H0", ajax: false, fixed: true; </code> La fonction de validation (suite) function validate(fieldId, focus){ var valInfo = fields[fieldId]; var fieldEl = getElement(fieldId); fields[fieldId].valid = false; var regExpGood = true; var functionGood = true; var lengthGood = (fieldEl.length >= valInfo.minLength && fieldEl.length <= valInfo.maxLength); if(valInfo.regExp != null) { eval('reg = '+valInfo.regExp); regExpGood = reg.test(getFieldValue(field)) } if(valInfo.function != null) eval(“functionGood = “+valInfo.function+”(“+fieldEl.value+”);”); if(regExpGood && functionGood && lengthGood){ setFieldState(fieldId, “green”, “ok”); fields[fieldId].valid = true;} else if(focus == true) setFieldState(fieldId, “yellow”, valInfo.hint); else setFieldState(fieldId, “red”, valInfo.hint); } L'objet XmlHttpRequest revisité ● Une description plus détaillée de l'objet XmlHttpRequest Propriété Description open(“methode”, “url”, flag) Ouvre la connexion avec le serveur. méthode -> "GET" ou "POST" url -> l'url à laquelle on va envoyer notre requête. Si la méthode est GET, on met les paramètres dans l'url. flag -> true si l'on veut un dialogue asynchrone, sinon, false setRequestHeader("nom","valeur") Assigne une valeur à un header HTTP qui sera envoyé lors de la requête. Par exemple, pour un POST : nom -> "Content-Type" valeur -> "application/x-www-form-urlencoded" send("params") Envoie la requête au serveur. Si la méthode est GET, on met null en paramètre. Si la méthode est POST, on met les paramètres a envoyer, sous la forme : "nomparam1=valeurparam1&nomparam2=valeurparam2". abort() Abandonne la requête. onreadystatechange Ici, on va lui affecter une fonction a nous qui sera exécutée à chaque "changement d'état" de notre objet. L'objet XmlHttpRequest revisité (suite) ● Une description plus détaillée de l'objet XmlHttpRequest Propriété Description readyState C'est cette propriété qu'on va tester dans le onreadystatechange. Elle représente l'état de l'objet et peut prendre plusieurs valeurs : 0 -> Non initialisé. 1 -> Ouverture (open() vient de s'exécuter). 2 -> Envoyé (send() vient de s'exécuter). 3 -> En cours (des données sont en train d'arriver). 4 -> Prêt (toutes les données sont chargées). status Le code de la réponse du serveur. 200 -> OK. 404 -> Page non trouvée. statusText Le message associé à status. responseText La réponse retournée par le serveur, au format texte. responseXML La réponse retournée par le serveur, au format dom XML. La fonction d'appel AJAX ● ● ● la fonctionalité dont on a besoin est très simple: appel du script php sur le serveur et gestion de la réponse. L'URL appelé sera: validate.php?field=[champ]&value=[valeur] Il retournera le nombre d'enregistrement dans la table User qui ont ces propriétés. La fonction d'appel AJAX (suite) <code:javascript> var _xmlHttp = null; //l'objet xmlHttpRequest utilisé pour contacter le serveur var _baseUrl = "validate.php" //l'adresse à interroger pour valider l'information var _lastCall = ""; function callValidate(field, value){ if(_xmlHttp&&_xmlHttp.readyState!=0) _xmlHttp.abort(); _xmlHttp=getXMLHTTP(); if(_xmlHttp){ //appel à l'url distante _lastCall = field; var urlCall = _baseUrl+"?field="+field+"&value="+value; addStatus("AJAX Call: "+urlCall); _xmlHttp.open("GET",_baseUrl+"?field="+field+"&value="+encodeURI(value), true); La fonction d'appel AJAX (suite) _xmlHttp.onreadystatechange=function() { if(_xmlHttp.readyState==4 &&_xmlHttp.responseText) { var valInfo = fields[_lastCall]; addStatus("AJAX Return:"+_xmlHttp.responseText); if(_xmlHttp.responseText == "1"){ setFieldState(_lastCall, "red", valInfo.ajaxHint); } else { setFieldState(_lastCall, "green", "ok"); fields[_lastCall].valid = true; } } }; _xmlHttp.send(null) } } </code:javascript> // envoi de la requête Le code PHP derrière ● le code php servira à valider qu'une adresse email est unique et qu'un username est unique <code:php> $field = $_GET['field']; $value = $_GET['value']; if(!isset($_GET['field']) || !isset($_GET['value'])) { ?>This file takes two parameters in get : field and value. The URL should look like <?= $_SERVER['REQUEST_URI'] ?>?field=username&value=simbad<br><br> It returns either 1 if we find a record that has that value, or 0 if it doesn't. <?php exit; } $dbConn = mysql_connect("localhost", "******", "*******"); mysql_select_db("ajaxpresentation"); if($dbConn == null) die("we have a problem connecting"); Le code PHP derrière (suite) $res = mysql_query("select id from members where $field = '$value'"); if(!$res) { $message = 'Invalid query: ' . mysql_error() . "\n"; $message .= 'Whole query: ' . $query; die($message); } echo mysql_num_rows($res); </code:php> Fonction de validation Javascript (2) <code:javascript> function validate(fieldId, focus) { if(focus == false) addStatus("validate called on:<b>"+fieldId+";</b> focus:"+focus); var valInfo = fields[fieldId]; var fieldEl = getElement(fieldId); var fieldObj = getFieldObject(fieldId); fields[fieldId].valid = false; var regExpGood = true; var functionGood = true; var lengthGood = (fieldEl.value.length >= valInfo.minLength && fieldEl.value.length <= valInfo.maxLength); if(valInfo.regExp != null) { eval('reg = '+valInfo.regExp); regExpGood = reg.test(fieldObj.value) } Fonction de validation Javascript (2) if(valInfo.valFunction != null) eval("functionGood = "+valInfo.valFunction+"("+fieldEl.value+");"); if(valInfo.ajax && focus == false && regExpGood && functionGood && lengthGood) { callValidate(fieldId, fieldObj.value); setFieldState(fieldId, "yellow", "validating..."); } else if(regExpGood && functionGood && lengthGood){ if(valInfo.ajax || focus == true && !valInfo.fixed) setFieldState(fieldId, "yellow", "form valid"); else { setFieldState(fieldId, "green", "ok"); fields[fieldId].valid = true; } } else if(focus == true) setFieldState(fieldId, "yellow", valInfo.hint); else setFieldState(fieldId, "red", valInfo.hint); Fonction de validation Javascript (2) if(!focus) addStatus("regExpGood:"+regExpGood+", functionGood:"+functionGood+", lengthGood:"+lengthGood); </code:javascript> Le champ username ● On désire contrôler le format du username (alphanumérique, 6 à 12 caractères) et en deuxième lieu on fait appel aux script de validation côté serveur. <code:javascript> fields.username = { minLengthObject(), minLength: 6, maxLength: 12, regExp: "/^[a-zA-Z0-9]{6,12}$/i", hint: "6 to 12 alphanumeric", ajax: true, ajaxHint: "username already used", fixed: true}; </code:javascript> Le champ username (suite) ● Le code HTML pour le champ // le champ <tr bgcolor="#0A3F7B"> <td align="right" nowrap="nowrap" bgcolor="#0A3F7B">Code d'utilisateur: </td> <td bgcolor="#0A3F7B"><input type="text" onfocus="validate('username', true);" onkeyup="validate('username', true);" onblur="validate('username', false);" name="username" id="username" size="12" maxlength="18" /></td> <td nowrap="nowrap" bgcolor="#0A3F7B" id='username_status'>&nbsp;</td> </tr> Le bouton submit ● ● On doit rajouter du code javascript pour valider que tous les champs soient effectivement corrects avant de soumettre. De plus, étant donné qu'une personne peut passer d'un champ directement au bouton submit, il faut donner un délai pour permettre aux événements et au AJAX de prendre place: <code:javascript> function trySubmit() { setTimeout("submitForm()", 500); } Le bouton submit function submitForm(){ if(_xmlHttp&&_xmlHttp.readyState!=0) { setTimeout(“submitForm()”, 500); exit; } var valid = true; for(var i in fields){ if(!fields[i].valid) valid = false; if(fields[i].valid == undefined) setFieldState(i, "red", fields[i].hint); } if(valid) document.forms[0].submit(); else alert("Please fix errors (red fields) before submitting"); } </code:javascript> // le bouton : <a href='#' onclick='trySubmit();' >[Submit]&nbsp;</a> Rajouter des champs à valider ● ● on peut aisément rajouter des champs dans le système de validation en populant un objet à l'intérieur de l'objet 'fields' qui contient toutes les instructions nécessaires et rajouter le champ, l'espace pour les messages et les appels sur événements appropriés <code:javascript> fields.email = { minLength: 6, maxLength: 64, regExp: "/^[^ ]+@[a-zA-Z0-9-.]+[.][a-zA-Z0-9]{2,3}$/i", hint: "[email protected]", ajax: true, ajaxHint: "courriel deja dans le syst&egrave;me", fixed: true}; </code:javascript> Rajouter les champs à valider (suite) // le champ <tr bgcolor="#0A3F7B"> <td align="right" nowrap="nowrap">Courriel: </td> <td bgcolor="#0A3F7B"><input type="text" onfocus="validate('email', true);" onkeyup="validate('email', true);" onblur="validate('email', false);" name="email" id="email" size="32" maxlength="64" /></td> <td nowrap="nowrap" id='email_status'>&nbsp;</td> </tr> L'espace de statut ● il suffit d'un <DIV> auquel on applique le bon style <div id='status' style="height:172px; overflow:auto"></div> ● et de quelques fonctions javascript simples: <code:javascript> var statusLine = 1; function addStatus(text){ prependHTML("status", statusLine+". "+text+"<br>"); statusLine++; } function clearStatus(){ changeHTML('status', ""); statusLine = 1; } L'espace de statut (suite) function prependHTML(element, text){ elementObject = getElement(element); text = text + elementObject.innerHTML; elementObject.innerHTML = text; } </code:javascript> ● Et ensuite d'en profiter <code:javascript> addStatus(“some information”); </code> ● Et si vous voulez, vous offrir un bouton pour vider le statut <a href="#" onclick='clearStatus()'>[Clear]&nbsp;</a> Appendix A – Behaviour ● ● ● Behaviour est une librairie Javascript qui permet de séparer le code de la présentation. On associe des événements aux objets HTML de la même façon qu'on associe des styles CSS, soit par id et class Au lieu de produire du code spaghetti comme ceci (du site web Backpack <span onmouseover="notesBlock.hoverBegin(128699)" onmouseout="notesBlock.hoverEnd(128699, true)"> <a class="trashcan" href="#" onclick="if (confirm('Are you sure?')) { new Ajax.Updater('notes', '/page/2326/notes/destroy/128699', { Appendix A – Behaviour (suite) ● Ou comme ceci (du site web Flickr) <div id="image_16209134_normal"> <script language="Javascript"> photo_hash['16209134'] = new Object(); photo_hash['16209134'].title = '2am on Saturday'; </script> <h4 id="title_div16209134" style="margin-bottom: 0px; margin-top: 0px;"> 2am on Saturday </h4> <script type="text/javascript">initPhotosUserPageTitle_div('16209134');</script> ● Behaviour vous permet de générer du code propre et facile à comprendre. Appendix A – Behaviour (suite) <ul id="example"> <li> <a href="/someurl">Click me to delete me</a> </li> </ul> <code:javascript> var myrules = { '#example li' : function(el){ el.onclick = function(){ this.parentNode.removeChild(this); } } }; Behaviour.register(myrules); </code:javascript> ● Préférable, ne croyez-vous pas? Appendix B – Outils de débogage ● ● FireBug (extension Firefox) – Firebug permet de déboguer le Javascript, le dHTML et le Ajax. C'est une combinaison de la Javascript Console, du DOM Inspector et d'un interpréteur de commande Javascript. – Il contient un 'XMLHttpRequest Spy', qui capte les appels XMLHttpRequest ainsi que leurs réponses respectives. – Un indicateur d'erreur s'active sur la barre d'état qui vous informe tout de suite des erreurs dans la page courante seulement*. Tamper Data (extension Firefox) – Permets de modifier les requêtes générées par Firefox avant qu'elles soient envoyées. – Utile pour les WebServices & Ajax Liens Utiles ● Web 2.0, allez plus loin avec AJAX et XMLHttpRequest http://siddh.developpez.com/articles/ajax/ ● AJAX – Une autocomplétion pas à pas http://dcabasson.developpez.com/articles/javascript/ajax/ajax-autocompletion-pas-a-pas/ ● Behaviour http://bennolan.com/behaviour/ ● Prototype – A Javascript Framework http://prototype.conio.net/ ● Open Rico – Another Javascript Framework http://openrico.org/rico/demos.page?demo=rico_weather ● Scriptaculous – Another Javascript Framework http://wiki.script.aculo.us/scriptaculous/show/Demos Quelques mots sur moi ● Martin Legris (514) 448-0362 – [email protected] ● ● Développeur, Designer Graphique & Formateur Pigiste – Spécialiste de PHP & Orienté Objet – Méthodologies : UML 2.0, eXtreme Programming, Agile Developpement – Spécialiste Web 2.0 (AJAX) – Spécialiste ActionScript 2.0, Flash 8 http://www.newcommerce.ca