Il y a 15 ans -
Temps de lecture 9 minutes
Introduction à PrototypeJs
Nous avons déjà abordé le sujet en surface lors du précédent article « introduction à Json » ; voici une présentation du framework JavaScript Prototype.
Depuis l’avènement d’Ajax, de multiples framework JavaScript sont nés dans le but de faciliter les développements JavaScript, et d’assurer une meilleure maintenabilité des scripts. Il faut bien reconnaître que le côté langage de script joue traditionnellement en la défaveur de JavaScript.
Mais aujourd’hui, il est possible d’envisager les choses autrement. Nous verrons dans cet article comment :
- Utiliser les classes et l’héritage.
- Simplifier les manipulations DOM.
- Gérer mieux les événements.
- Faire des requêtes Ajax.
Attention, cet article se veut plus un rappel de l’état de l’art de la programmation JavaScript, qu’une formation complète. De plus, on ne se lancera pas dans une comparaison des différents Frameworks disponibles sur le marché. En bref, il s’agit de présenter succinctement Prototype, tout en introduisant un certain nombre de bonnes pratiques pour le développement JavaScript en général.
Classe et Héritage
Finissons-en avec la programmation procédurale, arrêtons d’empiler les appels de fonctions. Avec Prototype, faites des classes, profitez de l’héritage et gérez vos instances. La méthode Class.create()
permet de créer un objet classe qu’on utilise ensuite pour instancier nos objets.
var Person = Class.create ({ initialize: function (name){ this.name = name; }, say: function (msg){ return (this.name+" says : "+ msg); } });
Notez que initialize()
est le constructeur, et que le mot-clé this
est bien sûr disponible dans toutes les méthodes de notre classe. Pour étendre notre classe, on l’insère comme premier argument du create()
.
var Pirate = Class.create(Person, { say: function ($super, msg){ return $super(msg) + " yaarh !"; } });
Notre classe Pirate
hérite donc de Person
et surcharge même la méthode say
. Comme vous le voyez, pour surcharger une méthode sans l’écraser, il suffit de placer le paramètre spécial $super
en tête de la liste des paramètres. Prototype fournira un pointeur vers la méthode de la classe mère dans ce cas.
Attention à bien déclarer les propriétés dans le constructeur :
// A proscrire var Pirate = Class.create(Person, {..., name:"George"}); // Préferrez var Pirate = Class.create(Person, { initialize:function(name) { this.name = name; } ... });
Préférez la deuxième syntaxe utilisée, sinon la valeur de name
sera partagée par toutes les instances de Pirate
.
Pour aller plus loin, pensez aussi aux méthodes statiques qui peuvent être utiles pour faire une factory par exemple. Il suffit d’ajouter notre méthode à la classe déjà créée.
var Validator = Class.create (...); // Méthode statique Validator.required = function (elem){ return new RequiredValidator(elem); }
On peut aussi ajouter des méthodes d’instance à une classe avec Class.addMethods()
. Notez bien que toutes les instances de la classe en hériteront immédiatement (et pas seulement les nouvelles instances). Une utilisation intéressante peut-être, par exemple, un système de « plugins » ou de modules chargés en Ajax.
Utilisez des espaces de noms pour isoler vos objets de vos dépendances. On peut même envisager de faire des packages comme en Java. Par exemple, placez toutes vos classes dans un module parent du nom de votre projet :
var MonProjet = {}; MonProjet.Person = Class.create (....);
Vous pourrez ensuite simuler un import de toutes vos classes avec un blocwith.
with (MonProjet) { var george = new Person("george"); }
Comme vous le voyez, le bloc with
permet d’accéder directement aux propriétés d’un objet.
Notes de bonnes pratiques :
- Utilisez toujours une classe de log qui ajoutera les traces dans une div ou une autre page et que vous pourrez facilement désactiver pour la production.
- Levez des exceptions pour gérer les erreurs de vos scripts, et surtout encadrez votre code de blocs
try-catch
pour faciliter la correction des anomalies.
- Pour gérer les paramètres optionnels, définissez un module avec les valeurs par défaut que vous étendrez avec les options passées en paramètre.
var BetweenIntValidator = Class.create({ initialize: function(min, max, options) { this.min = min; this.max = max; this.options = Object.extend({ includeMin: true, includeMax: true }, options); }, /.../ });
Manipulation DOM
Comme vous le savez sans doute déjà, il est possible d’étendre les types natifs JavaScript en y ajoutant des méthodes qui vous sont propres. Ainsi, on peut envisager que tous les objets de type String
vous fournissent une méthode isDigit()
et intValue()
par exemple. On peut en faire autant sur les objets de l’arbre DOM HTML. La fonction $(id | elem)
vous permet de retrouver par son id et/ou, d’étendre un élément. Si les extensions fournies nativement par Prototype ne vous suffisent pas, vous pourrez ajouter vos propres méthodes avec Element.addMethods()
.
var Module = { highlight: function(element){ $(element).setStyle({backgroundColor:"yellow"}); } }; Element.addMethods(Module); $('ma_div').highlight();
La plupart des fonctions fournies par Prototype retourne l’élément étendu, ce qui permet de chainer les appels en ligne.
$('ma_div').update("Nouveau texte").setStyle({backgroundColor:"yellow"}).show();
Sur cet exemple, nous avons mis à jour le contenu texte d’une div, changé sa couleur de fond pour du jaune et pour finir, fait apparaître.
La liste des méthodes fournies par Prototype est très longue. On ne s’attardera donc pas à étudier chacune d’elle. En voici une liste succincte, organisée par grande famille.
- Outils graphiques : Positionnez vos éléments, obtenez leurs dimensions, changez les styles css, …
- Manipulation de l’arbre DOM : Créez des éléments, insérez-les, changez leur contenu, réordonnez-les, supprimez-les, et recherchez-les.
var a = new Element('a', {'class': 'foo', href: '/foo.html'}).update("Next page");
- Gestion de formulaire : Récupérez les valeurs des champs, activez ou désactivez-les, sérialisez vos formulaires pour un POST ou un GET, …
- À vous de découvrir la suite.
Notez aussi que des extensions sont fournies pour les types Array
, Hash
, Math
, String
, entre autres. Préférez toujours passer par les méthodes du framework plutôt que de ré-écrire une fonction plus spécifique qui risquera d’être incompatible avec certains navigateurs.
Événements JS
La bonne place du code JavaScript est dans un bloc script ou dans un fichier séparé avec l’extension ‘.js‘. Insérer des attributs HTML type onClick
, onXXX
, est aujourd’hui à proscrire. Il faut isoler la couche de présentation HTML des contrôles JS, ce qui accroît nettement la lisibilité du code tant pour le HTML que pour le JavaScript. Avec Prototype, il suffit d’utiliser la méthode Event.observe()
sur un élément pour définir un handler. D’une manière générale, il est préférable d’associer l’événement à une méthode d’objet plutôt qu’à une fonction.
var MyObserver = Class.create ({ initialize: function (elem) { this.elem = $(elem); this.clickHandler = this.onClick.bindAsEventListener(this); this.elem.observe("click", this. clickHandler); }, onClick: function (event){ // Do something for click } });
Dans cet exemple, la méthode onClick
sera appelée sur l’élément elem
ou l’élément d’id elem
selon le cas. L’appel à this.onClick.bindAsEventListener(this);
permet de créer un handler qui associe l’instance de notre objet à l’événement. On relie ensuite le handler à l’événement « click
» de l’élément HTML.
On pourra utiliser plus tard le handler pour stopper l’écoute de l’événement à l’aide de la méthode Event.stopObserving()
.
Notez bien :
- La propagation de l’événement ne sera interrompue que par un appel à
event.stop()
. - Cette méthode est cross-browser, mais sans doute pas votre code.
- La méthode
event.element()
vous retournera l’élément HTML à l’origine de l’événement. - La méthode
event.firstElement(nodeType)
vous retournera le premier élément HTML de typenodeType
contenant l’élément à l’origine de l’événement. - Pour assurer l’exécution et l’initialisation d’un script, une bonne technique est d’écouter l’événement « OnLoad » sur l’objet
window
. Ceci nous permet d’obtenir un équivalent à la fonction Java « main ».
Event.observe(window, "load", funcion (){ // Placez ici vos traitements });
Ajax
Pour les requêtes asynchrones, vous avez sans doute déjà expérimenté la difficulté d’utiliser les objets natifs prévus à cet effet (XMLHttpRequest et consorts). Il n’est plus utile de se soucier de ce problème aujourd’hui, tous les frameworks JavaScript proposent leurs simplifications pour les requêtes Ajax. Prototype ne déroge pas à la règle, il nous permet d’envoyer et de traiter des GET/POST HTTP, avec l’objet Ajax.Request
.
new Ajax.Request('/some_url',{ method: 'get', onSuccess: function(response){ var response = response.responseText || "no response text"; alert("Success! n" + response); }, onFailure: function(response){ alert('Something went wrong...') } });
Cet exemple envoie une requête GET vers l’url « /some_url » et affiche le texte retourné par le serveur dans une boîte d’alerte.
Prototype fourni nativement l’Ajax.Updater
et l’Ajax.PeriodicalUpdater
pour respectivement mettre à jour le contenu d’un élément une fois et périodiquement.
Imaginons par exemple une boîte devant récupérer des nouvelles pour les afficher dans la page. Le serveur fournit un web-service pour récupérer les nouvelles.
Les dernière news
(récupération des news ...)/*...*/ new Ajax.Updater('mes_news', '/news',{ method: 'get' });
Ici nous avons utilisé un objet Ajax.Updater
qui chargera une seule fois les nouvelles du serveur. Libre à vous de prévoir un bouton pour forcer le rechargement, ou d’utiliser un Ajax.PeriodicalUpdater
pour régler une fréquence de mise à jour.
Pour aller plus loin, on peut surveiller l’ensemble des requêtes avec Ajax.Responders
. La méthode register
nous permet d’enregistrer un certain nombre de callbacks. De cette façon, il est possible d’écouter chacun des états disponibles des Ajax.Request
depuis la création jusqu’à la réception réussie d’une réponse. Avec cette fonctionnalité, il devient possible de réaliser une zone de notification, qui indiquera à l’utilisateur si une requête est en cours par exemple.
Ajax.Responders.register({ onCreate: function(){ alert("Une requête est invoquée."); }, onComplete: function(response){ alert("Une requête s'est terminée."); } });
Conclusion
Comme on l’a vu, Prototype s’oriente vers l’amélioration du langage JavaScript avec les classes notamment (sur lesquelles je me suis beaucoup attardé). On pourrait dire que Prototype est à JavaScript ce que java.util
et java.lang
sont à Java, tant ses apports sont essentiels. Mais Prototype s’arrête là où d’autres vont plus loin en proposant des effets et des librairies d’éléments prêts à l’emploi. Dans la plupart des cas, vous pourrez sans trop de difficulté utiliser Prototype avec un autre framework plus orienté graphique. Le prochain article, qui présentera Scriptaculous, sera l’occasion de mettre en pratique plusieurs des points vus ici.
Pour aller plus loin, je vous invite à visiter le site de Prototype qui héberge une documentation très complète et quelques tutoriels bien pratiques.
Commentaire