Le Post de MCNEXT

Les articles des consultants de MCNEXT

Notions fondamentales de JSON

Les formats de données

Lorsque l’on développe en Javascript, le format de données roi est le JSON (JavaScript Object Notation). Ce format supporte deux types d’entité : les tableaux (ou array) représentés par la notation littérale "[]", et les objets, aussi appelés dictionnaires ou hash représentés par la notation littérale "{}".

Les tableaux ont une propriété length qui indique le nombre d’éléments qu’ils contiennent. Il est possible d’accéder à ces éléments par le biais d’un indexeur, et on utilise généralement une boucle for pour les parcourir.

var array = [ "Foobar", 42, true ];
for (var i = 0; i < array.length; i++) {
    var item = array[i];
}

Les hash quand à eux, contiennent des propriétés nommées auxquelles il est possible d’accéder directement, soit par une notation pointée, soit par le biais d’un indexeur nommé.

var object = {
    "Lastname":  "Guillot",
    "Firstname": "François"
};
var lastname = object.Lastname;      // Notation pointée
var firstname = object["Firstname"]; // Indexeur nommé

L’accès par indexeur nommé offre l’avantage de pouvoir être utilisé avec une variable contenant le nom de la propriété.

// Même chose que précédemment, mais le nom de la propriété "Firstname" est contenu dans une variable
var propertyName = "Firstname";
var firstname = object[propertyName];

Pour parcourir les propriétés d’un hash, on utilise une boucle for in. Chaque tour de boucle permet d’accéder au nom d’une propriété du hash, permettant ensuite d’accéder à la valeur de celle-ci par le biais de l’indexeur nommé.

var city = {
    "Name": "Marseille",
    "ZipCode": "13000"
};
for (var key in city) {
    // Au premier tour de boucle, key contiendra "Name", et value contiendra "Marseille"
    var value = city[key];
}

Il n’est pas possible de parcourir un tableau avec la boucle for in, car bien qu’à chaque tour de boucle la clef contiendrait une des valeurs contenue par le tableau, elle contiendrait également la valeur "length", car toutes les propriétés d’un objet Javascript sont inspectées.

La boucle for in inspecte également les propriétés d’un objet héritées par prototypage. Si vous ne souhaitez inspecter que les propriétés que porte l’objet lui-même, vous devez tout d’abord contrôler leur origine avec la méthode hasOwnProperty().

for (var key in city) {
    if (city.hasOwnProperty(key)) {
        // La clef contient bien le nom d'une propriété que l'objet city n'a pas reçu par héritage
        var value = city[key];
    }
}

Il est bien entendu possible de mixer les tableaux et les hash, pour concevoir des graphes de données complexes.

// Tableau contenant un hash, un booléen, et un autre tableau
var complexGraph = [
    { "Language": "C#", "Version": "4.5", "Year": 2012, "Creators": [ "Anders Hejlsbjerg" ] },
    true,
    [ 'Test', 12, { "Id": "4BB84287-B919-45DD-98BA-FFBC7D6F7BF2" } ]
];

Écrire du code JSON valide

JSON est un format de données natif du langage Javascript, ce qui signifie que dans ce contexte, il est possible de l’utiliser au-delà de ses possibilités standards. Par exemple, en Javascript on s’autorise généralement à écrire les noms des propriétés des hash sans les encadrer par des double quotes ("), et on peut placer dans ces mêmes hash des données non littérales, comme des fonctions.

var hashJavascript = {
    Property: function (param) { return param; }
};

Mais la notation JSON a aujourd’hui largement dépassé le cadre du seul langage Javascript, au point d’être utilisée couramment dans de nombreux autres langages. Ceux-ci disposent généralement d’un parseur, qui est très pointilleux quand au respect de la syntaxe. En effet, le JSON a été normalisé, et les moteurs de parsing lèvent une exception si la chaîne de caractères qui leur est soumise ne respecte pas cette norme.

Cette norme est toutefois assez simple à respecter. Les valeurs contenues par les tableaux ou par les propriétés des hash, ne peuvent être que des chaînes de caractères, des nombres entiers ou décimaux, des booléens, la valeur spéciale null, ou bien entendu des sous-tableaux ou des sous-objets. Sont prohibés les fonctions et les dates (ce qui pose souvent problème pour faire circuler des informations de type DateTime).

Autre point important : les noms de propriétés dans les hash doivent être encadrés par des double quotes ("), pas des simples quotes (‘), et ils doivent encore moins être écrits sans formatage. Cette règle s’applique également aux chaînes de caractères : double quotes (") obligatoires. Notez qu’à titre personnel, j’ai tendance à trouver la notation des propriétés avec des double quotes nettement plus lisible.

// Cet objet JSON est mal formé
var wrongObject = {
    prop: 'je m\'appelle "toto"',
    today: new Date(2012, 6, 21, 12, 44, 55, 0)
};

// Cet objet JSON respecte la norme
var rightObject = {
    "prop": "je m'appelle \"toto\"",
    // La date est une chaîne de caractères au format ISO-8601, qui est en train de devenir la nouvelle norme
    "today": "2012-06-21T12:44:55"
};

Moralité : si vous devez faire circuler des données au format JSON entre votre serveur et le client, prenez soin de bien respecter le norme, car un parseur comme la méthode jQuery.parseJSON() rejettera votre chaîne de caractères si celle-ci contient des erreurs de formatage.

Optimiser la récupération des données

Il arrive couramment que l’on doive retrouver un objet JSON parmi un tableau de ceux-ci, à partir de la valeur d’une de ses propriétés; typiquement un Id.

var animals = [
    { "Id": "D8777CD6-5E94-4464-8B3D-474DF4EC99FB", "Name": "Warthog" },
    { "Id": "BCDFEEC7-63F5-44C1-850D-246FB40B470A", "Name": "Hedgehog" },
    { "Id": "A71ED56F-DB42-4105-97E8-5C0BD7C3D3F1", "Name": "Fawn" },
    { "Id": "2EBAC3FC-C03E-4263-95D2-1B26E0260C0F", "Name": "Gibbon" }
];

Admettons que nous souhaitons récupérer parmi cette liste d’animaux celui dont l’Id est A71ED56F-DB42-4105-97E8-5C0BD7C3D3F1 (c’est le faon). Une solution consiste à utiliser la méthode utilitaire jQuery.grep().

var idFawn = "A71ED56F-DB42-4105-97E8-5C0BD7C3D3F1";

// Remarquez que jQuery.grep() renvoie un tableau d'éléments respectant le prédicat passé en paramètre
// Comme nous savons qu'il n'y a qu'un seul élément respectant le prédicat, nous prenons directement le premier élément du tableau résultant
var fawn = jQuery.grep(animals, function (item) {
    return item.Id === idFawn;
})[0];

Une autre solution consiste à le faire en Javascript pur. On s’épargne ainsi de boucler sur toutes les valeurs du tableau, puisqu’on peut arrêter la recherche dès lors que le résultat a été trouvé.

var idFawn = "A71ED56F-DB42-4105-97E8-5C0BD7C3D3F1",
    fawn = null;
for (var i = 0; i < animals.length; i++) {
    var animal = animals[i];
    if (animal.Id === idFawn) {
        fawn = animal;
        break;
    }
}

Le problème de ces deux méthodes, c’est qu’elles ne sont en aucun cas optimisées pour les moteurs Javascript. En effet, la solution idéale consiste à utiliser un indexeur nommé sur un hash. Mais pour cela, il faut qu’au départ nos données soient dans un format quelque peu différent.

var animals = {
    "D8777CD6-5E94-4464-8B3D-474DF4EC99FB": { "Name": "Warthog" },
    "BCDFEEC7-63F5-44C1-850D-246FB40B470A": { "Name": "Hedgehog" },
    "A71ED56F-DB42-4105-97E8-5C0BD7C3D3F1": { "Name": "Fawn" },
    "2EBAC3FC-C03E-4263-95D2-1B26E0260C0F": { "Name": "Gibbon" }
},
    idFawn = "A71ED56F-DB42-4105-97E8-5C0BD7C3D3F1",
    fawn = animals[idFawn];

Cette méthode est optimale car elle permet d’aller récupérer le faon directement dans la liste des animaux par son id, sans avoir à effectuer la moindre boucle. Dans des cas de manipulations de très grandes quantités de données, les performances peuvent s’en trouver nettement améliorées.

C’est pourquoi il est important que les données soient présentées dans un format adéquat. Ce format peut être élaboré côté serveur, avec d’injecter les données dans la page ou de les envoyer par le biais d’une requête Ajax. Pour effectuer ce type d’opération, j’ai généralement recours à la très bonne librairie JSON.NET, qui est maintenant fournie en standard dans les projets ASP.NET Web API (qui l’utilise lui-même pour toutes les opérations de manipulation de JSON).

// Exemple de création d'un objet JSON en C# avec JSON.NET
// N'oubliez pas d'ajouter "using Newtonsoft.Json.Linq;"
Animal[] animals = new Animal[]
{
    new Animal { Id = Guid.Parse("D8777CD6-5E94-4464-8B3D-474DF4EC99FB"), Name = "Warthog" },
    new Animal { Id = Guid.Parse("BCDFEEC7-63F5-44C1-850D-246FB40B470A"), Name = "Hedgehog" },
    new Animal { Id = Guid.Parse("A71ED56F-DB42-4105-97E8-5C0BD7C3D3F1"), Name = "Fawn" },
    new Animal { Id = Guid.Parse("2EBAC3FC-C03E-4263-95D2-1B26E0260C0F"), Name = "Gibbon" }
};

JObject objectJson = new JObject();

foreach (Animal animal in animals)
    objectJson.Add(animal.Id.ToString(), JObject.FromObject(animal));

// Résultat sous forme de chaîne de caractères, non-indenté
string json = objectJson.ToString(Formatting.None);

On peut aussi transformer le format des données côté client, opération coûteuse mais qui peut être ensuite contrebalancée par le gain de performances obtenu par l’utilisation des indexeurs nommés.

// Exemple de transformation du tableau d'animaux vers un hash d'animaux
var animals = [
    { "Id": "D8777CD6-5E94-4464-8B3D-474DF4EC99FB", "Name": "Warthog" },
    { "Id": "BCDFEEC7-63F5-44C1-850D-246FB40B470A", "Name": "Hedgehog" },
    { "Id": "A71ED56F-DB42-4105-97E8-5C0BD7C3D3F1", "Name": "Fawn" },
    { "Id": "2EBAC3FC-C03E-4263-95D2-1B26E0260C0F", "Name": "Gibbon" }
], newAnimals = {};
for (var i = 0; i < animals.length; i++) {
    var animal = animals[i];
    newAnimals[animal.Id] = animal;
}

Comment éteindre Windows 8 / How to shutdown Windows 8

Windows 8 sera disponible pour le public le 26 octobre prochain. Mais les quelques privilégiés qui ont eu l’opportunité de se pencher dessus ont souvent rencontré la même difficulté : avec la disparition du menu "Démarrer", il est difficile de trouver comment éteindre son ordinateur.
Windows 8 will be released on October 26th. But the happy fews who had the opportunity to try it have often encountered the same difficulty: with the vanishing of the "Start" menu, it’s hard to find out how to turn off the computer.

Laissez-moi vous montrer comment retrouver la commande.
Let me show you how to get to the command.

Tout d’abord, il vous faut placer le curseur de la souris dans un des coins supérieur droit ou inférieur droit afin de faire apparaître la "Charm bar" (les rectangles rouges sur l’image). Vous pouvez aussi appuyez sur les touches "Windows + C" de votre clavier. Si vous êtes sur une tablette, vous devez effectuer un geste du doigt depuis le côté droit de l’écran vers l’intérieur.
First and foremost, you have to place the mouse cursor in the upper right of lower right corner so the "Charm bar" show up (the red rectangles on the picture). You can also press the touches "Windows + C" on your keyboard. If you’re on a tablet, you must execute a gesture by pulling your finger from the right border of the screen toward the center.

Ensuite, il vous faut vous rendre dans la section "Settings" ("Paramètres") représentée par un engrenage, en bas du menu. Vous pouvez aussi accéder directement à ce menu en pressant les touches "Windows + I" du clavier.
Then, you have to go to the "Settings" section represented by a gear, at the bottom of the menu. You can also access this menu directly with the touches "Windows + I" of your keyboard.

Un nouveau menu "Settings" apparaît alors. Cliquez sur le bouton "Power" ("Marche/Arrêt").
A new menu shows up. Click on the "Power" button.

Finalement, cliquez sur "Shutdown" ("Arrêter").
Finally, click on "Shutdown".

Animer vos pages web avec CSS3

Il n’est un secret pour personne que la nouvelle norme HTML5, et la norme CSS3 qui l’accompagne, ont pour objectif de rendre à terme obsolète l’utilisation de plugins tiers comme Flash ou Silverlight. A ce titre, une nouvelle API permettant d’animer les éléments d’une page web est actuellement en cours de normalisation par le W3C, l’organisme en charge de mettre au point les nouveaux standards de demain.

Cette nouvelle API, bien qu’actuellement à l’état de brouillon et disséminée dans plusieurs modules de la norme CSS3, a déjà été largement implémentée dans les dernières versions des navigateurs que nous connaissons tous (à l’exception d’Internet Explorer 10 qui se fait attendre) et est donc d’ores et déjà exploitable.

Nous allons étudier chacune des nouveautés qui nous permet d’animer nos sites. Notez néanmoins que bien que les différents acteurs du monde du web se soient déjà mis d’accord sur une syntaxe commune, l’utilisation de préfixes CSS est encore pour le moment d’usage. Le but de ceux-ci est de permettre aux développeurs d’être en mesure de différencier les différents navigateurs, afin de pouvoir effectuer le cas échéant de menus réglages.

Par soucis de brièveté, dans les exemples qui suivront ces différents préfixes seront omis pour ne conserver que la syntaxe qui devrait devenir définitive à terme.

Les transformations

Tout d’abord, de nouvelles propriétés CSS3 vont nous permettre de transformer nos éléments HTML de manière inédite. Pour cela, on utilise la propriété CSS transform associée à une fonction de transformation. Ces fonctions de transformation vont nous permettre par exemple de modifier les dimensions de l’élément (translate / translate3d), de le faire pivoter sur lui-même (rotate / rotate3d) sur un ou plusieurs de ses axes, de l’étirer (skew) ou d’appliquer une transformation matricielle (matrix / matrix3d). Comme vous le voyez, la plupart de ces transformation existent en version 2D ou 3D. Par ailleurs, il est généralement possible de les appliquer sur un seul axe à la fois, avec par exemple rotateX, rotateY et rotateZ.

.mon-element {
    /* L'élément pivotera de 30 degrés dans le sens horaire */
    transform: rotate(30deg);
}

Notez – et c’est important – que toutes ces transformations se font à la fois sur l’élément et son contenu, que le flux d’affichage de la page n’est pas modifié et que cette opération se fait à un niveau graphique. Par exemple lors d’un étirement, le texte inclus dans l’élément sera lui aussi transformé.

La propriété CSS transform s’accompagne de plusieurs autres propriétés permettant de préciser certains éléments d’affichage. Par exemple, la propriété transform-origin permet d’indiquer à partir de quel point de l’élément la transformation doit s’appliquer. Si l’on reprend notre précédent exemple de la rotation, on peut ainsi préciser que celle-ci doit s’appliquer non pas sur le centre de l’élément – comme c’est le cas par défaut – mais par exemple à dix pixels de haut et dix pixels de large de son coin supérieur gauche.

.mon-element {
    transform: rotate(30deg);
    /* La transformation s'appliquera avec un point d'origine différent */
    transform-origin: 10px 10px;
}

Lorsque la transformation s’applique en trois dimensions, il est possible avec la propriété backface-visibility de préciser si l’on souhaite ou non que le dos de l’élément soit visible. La propriété perspective permet quand à elle de modifier la perspective selon laquelle un objet est perçu dans l’espace.

La transformation en elle-même se fait sans effet d’animation. Il est néanmoins possible de rendre cette transformation dynamique en utilisant les pseudo-éléments CSS comme :hover ou :focus.

a.mon-lien:hover {
    /* Multiplie par deux la largeur et la hauteur de l'élément
       lorsque le curseur de la souris le survole */
    transform: scale(2,2);
}

Les transitions

Il est courant dans une page web de vouloir modifier dynamiquement l’apparence d’un élément, que ce soit ses dimensions, la taille du texte qu’il contient, ou ses couleurs. Toutefois, ces opérations se faisaient jusqu’à maintenant en Javascript, et elles étaient généralement assez abruptes (sauf à utiliser des moteurs d’animation comme celui de jQuery). Le changement de couleur de fond d’un élément se faisait par exemple instantanément.

Grâce à la nouvelle propriété CSS3 transition, il est maintenant possible de préciser que l’on souhaite que certaines caractéristiques d’un élément changent progressivement lorsque celles-ci sont altérées. Concrètement, lorsque par exemple on modifie la largeur d’un élément de 100 pixels à 200 pixels, il est possible d’indiquer au moteur de rendu du navigateur que la transition entre ces deux états doit se faire en passant par tous les états intermédiaires (les valeurs entre 100 et 200), et non pas instantanément. L’utilisation de Javascript devient alors caduque, car tout se fait en CSS.

La déclaration de ces effets de transition s’effectue en indiquant à quelle propriété elle s’applique, et combien de temps doit durer la transition. Il est possible de cumuler plusieurs propriétés dans la même déclaration, comme on le voit dans l’exemple suivant :

.mon-element {
    transition: width 2s, height 2s, transform 2s;
}

Il est possible d’appliquer ces effets sur n’importe quelle propriété CSS. Lorsque la valeur de l’une de celles-ci sera altérée, de quelque manière que ce soit (Javascript, pseudo-element CSS, …), si un effet de transition a été appliqué à l’élément, alors la transition se fera automatiquement selon les paramètres spécifiés.

On peut également préciser directement dans la propriété transition ou par le biais de la propriété transition-timing-function un effet "d’assouplissement" (en anglais : easing), permettant d’altérer la manière dont la transition va s’appliquer dans le temps. Par exemple, on peut indiquer que l’on souhaite que la transition commence très rapidement, puis ralentisse sur la fin. Pour cela il existe déjà plusieurs mots-clefs standardisés (linear, ease, ease-in, ease-out, ease-in-out), mais on peut également utiliser la fonction cubic-bezier pour avoir un contrôle plus fin sur ce timing. Vous pouvez d’ailleurs vous amuser à tester différents effets sur ce site.

.mon-element {
    transition: width 3s cubic-bezier(.99, .01, -.01, .01);
}

Dernière possibilité offerte par la propriété transition-delay : différer le lancement de la transition. Comme la durée de la transition elle-même, la valeur de cette propriété peut être indiquée en secondes (s) ou en millisecondes (ms).

Dans l’exemple suivant nous appliquons un effet de transition en utilisant séparément chacune des propriétés CSS3 distinctes, plutôt qu’en les rassemblant par le biais de la propriété transition :

.mon-element {
    transition-property: font-size;
    transition-duration: 5s;
    transition-timing-function: ease-in-out;
    transition-delay: 1000ms; /* Équivalent à 1 seconde */
}

Cette effet de transition s’appliquera donc sur la propriété font-size (la taille des caractères autrement dit), il durera cinq secondes mais ne commencera qu’une seconde après que la propriété ait été altérée. Par ailleurs l’effet s’effectuera selon le timing ease-in-out (légèrement plus rapide au début et à la fin).

Voici un exemple de code Javascript modifiant la valeur du style CSS font-size d’un élément. Ce code permettrait de provoquer l’exécution de l’effet de transition :

document.getElementsByClassName('mon-element')[0].style.fontSize = '48px';

Les animations

Les animations avec CSS3 sont cousines des transitions, à l’exception qu’elles peuvent être répétées en boucle, et qu’elles offrent un contrôle plus fin sur la manière dont va évoluer l’apparence des éléments du DOM.

A la base d’une animation, il y a la notion de keyframe. Une keyframe n’est pas une propriété CSS mais une règle. Elle permet d’indiquer la suite d’états par lesquels l’élément du DOM va transiter au cours de l’animation.

@keyframes mon-animation {
    0% {
        background-color: red;
    }

    25% {
        background-color: green;
    }

    75% {
        background-color: blue;
    }

    100% {
        background-color: yellow;
    }
}

Dans cet exemple, nous indiquons que l’élément auquel sera appliquée l’animation devra avoir un fond de couleur rouge au début de celle-ci, puis vert au premier quart, puis bleu au troisième quart, puis jaune à la fin. Les mots-clefs from et to peuvent remplacer respectivement 0% et 100%, mais n’importe quelle valeur entre 0 et 100 est aussi acceptée.

Ensuite pour appliquer cette animation à un élément, on utilise la propriété animation proprement dite :

.mon-element {
    animation: mon-animation 5s;
}

Le minimum est d’indiquer la durée souhaitée de l’animation. Mais il est également possible, comme pour les transitions, d’indiquer un effet d’assouplissement, et un délai d’attente avant le lancement de l’animation. Par ailleurs, les animations disposent également des propriétés animation-iteration-count et animation-direction. La première permet d’indiquer combien de fois l’animation doit s’effectuer (on peut utiliser le mot-clef infinite pour que celle-ci tourne en boucle), et la seconde, si l’animation doit s’effectuer toujours dans le même ordre (mot-clef normal), ou si celui-ci doit s’inverser entre deux itérations (mot-clef reverse), ce qui s’avère très pratique lorsque l’on veut par exemple faire tourner un élément dans un sens puis dans l’autre.

.mon-element {
    animation: mon-animation 5s;
    animation-timing-function: linear;
    animation-delay: 2s;
    animation-iteration-count: infinite;
    animation-direction: alternate;
}

Les animations disposent enfin d’une dernière propriété : animation-play-state. Celle-ci permet de contrôler l’exécution de l’animation, avec la possibilité de mettre celle-ci en pause pour la continuer plus tard. Cela permet donc d’imaginer des scénarios où l’utilisateur peut piloter l’exécution de l’animation grâce au Javascript ou aux pseudo-éléments CSS.

.mon-element {
    animation: mon-animation 5s;
    animation-play-state: running;
}

.mon-element:hover {
    animation-play-state: paused;
}

Pour aller plus loin

Si vous souhaitez consulter la documentation complète des transformations, transitions et animations en CSS3, je vous recommande chaudement l’excellent site W3Schools. Vous pourrez y consulter des démonstrations, mais aussi créer les vôtres grâce à un système de bac-à-sable interactif très bien conçu qui vous permettra de vous entraîner à maîtriser toutes ces nouvelles propriétés.

Avec tout cela il devient beaucoup plus simple d’animer nos pages web. Toutefois, la création d’animations complexes reste fastidieuse à réaliser sans outils dédiés. On peut noter l’arrivée prochaine du nouveau logiciel Adobe Edge, qui est d’ores et déjà disponible en version béta, et dont l’objectif est précisément de permettre la création de contenu web animé en utilisant les nouveaux standards HTML5, CSS3 et le langage Javascript.

Les petites astuces du langage Javascript

Javascript est un langage qui avait été initialement conçu pour permettre de rendre les pages web un peu plus dynamiques, mais à l’époque de sa conception personne n’aurait imaginé qu’il pourrait prendre un jour une place aussi prépondérante dans la conception des applications web.

Alors que la nouvelle norme ECMAScript 5 commence à être doucement implémentée par les différents navigateurs du marché, certains développeurs ont su faire preuve de beaucoup d’astuce pour permettre à ce langage de donner le meilleur de lui-même.

Déterminer si une année donnée est bissextile

Aucune méthode n’est prévue nativement en Javascript pour permettre de déterminer si une année donnée est bissextile. Mais en s’appuyant sur le comportement par défaut des moteurs Javascript inclus dans les différents navigateurs, il est possible d’obtenir cette information.

function isLeapYear(year) {
    return new Date(year, 1, 29).getMonth() === 1;
}

L’astuce consiste tout simplement à créer une nouvelle date, correspondant au 29 février de l’année que l’on souhaite tester. Notez qu’en Javascript, le numéro de mois est en base 0; le mois de février correspond donc au chiffre 1.

Ce qu’il se passe, c’est que si l’année passée au constructeur de l’objet Date n’est pas bissextile, la date retournée ne sera pas le 29 février… mais le 1er mars. Il ne reste donc qu’à tester si le mois de cette date est bien toujours février, ou si celui-ci s’est miraculeusement transformé en mars.

Obtenir la plus petite ou la plus grande valeur contenue dans un tableau

Prenons le tableau suivant :

var numbers = [ 4, 8, 15, 16, 23, 42 ];

Par défaut, le langage Javascript ne contient aucune méthode particulière permettant de déterminer la plus petite valeur contenue par ce tableau. Par contre, il existe dans l’objet Math une méthode min() permettant de déterminer qu’elle est la plus petite valeur parmi toutes celles passées en paramètre. Il serait donc tentant d’essayer de brancher les deux ensembles, et c’est possible, grâce à la méthode spéciale apply().

Array.min = function(array) {
    return Math.min.apply(Math, array);
};

La méthode apply() permet d’exécuter une méthode en lui passant seulement deux paramètres. Le premier correspond au contexte d’exécution (le mot clef this dans le corps de la méthode), mais ici nous ne souhaitons pas le modifier et nous contentons donc de passer l’objet Math lui-même. Le second paramètre est celui qui nous intéresse. Il permet de passer les paramètres de la méthode non pas les uns après les autres mais regroupés dans un tableau. Or c’est justement d’un tableau de nombres dont nous disposons et que nous souhaitons tester.

Grâce à ce mécanisme, il est alors facile d’ajouter une nouvelle méthode min() à l’objet Array, et de brancher celle-ci à la méthode Math.min(). L’opération devient alors un jeu d’enfant :

var smallestNumber = Array.min(numbers); // 4

Vous pourriez arguer qu’il n’est pas beaucoup plus difficile d’écrire soi-même un algorithme pour boucler sur ces nombres et trouver lequel est le plus petit. Mais en utilisant une méthode incluse nativement dans le moteur Javascript, nous bénéficions d’une amélioration des performances, ce qui sur de très grands tableaux peut s’avérer décisif.

Je vous laisse deviner vous-même comment permettre le calcul de la plus grande valeur contenue dans un tableau.

Créer son propre opérateur de coalescence sur null

Javascript est un langage extrêmement souple, et un nouvel exemple de cette souplesse tient dans le fait que l’opérateur de comparaison OR (noté "||") est capable d’avoir une influence sur l’assignation d’une variable. Voyons plutôt un exemple :

var nullVariable = null;
var anotherVariable = nullVariable || 12;

La variable nullVariable contient la valeur null. Nous tentons d’assigner cette valeur à une autre variable. Mais notez l’utilisation de l’opérateur OR. Grâce à lui, si la variable source a une valeur booléenne correspondant à false – ce qui est le cas pour le mot-clef null – c’est la valeur de l’opérateur de droite qui sera assignée. Dans notre exemple, la variable anotherVariable se verra donc assigner la valeur 12.

Ce mécanisme, très pratique au demeurant, correspond peu ou prou à l’opérateur ?? en C#. Attention néanmoins, car le test des valeurs booléennes en Javascript obéit parfois à des règles quelque peu abstraites. Par exemple le test du chiffre 0 renvoie false, alors que tout autre nombre – même négatif – renverra true.

Les barrières mentales du développeur junior

Quand on débute dans la programmation, on est souvent victime de son manque d’expérience. Par exemple, on est rarement conscient au départ de la pléthore d’outils qui s’offre à nous. J’ai ainsi souvenir d’avoir codé à mes débuts une fonction permettant de calculer la date de la veille, avant de découvrir quelques semaines plus tard que le Framework .NET propose une fonction toute faite pour cela (DateTime.AddDays(), à laquelle il suffit de passer un nombre négatif pour retrancher des jours).

Mais il y a aussi un certain nombre d’idées préconçues et de mythes auxquels il est bon de tordre le cou si l’on veut progresser rapidement. Nous allons ici en examiner quelques-uns.

Ce n’est pas parce qu’un code est long qu’il est lent

Le développeur junior a tendance à croire que plus les lignes de code s’empilent, et plus son application va prendre de temps pour s’exécuter. Cela paraît logique de prime d’abord, mais il faut toutefois nettement relativiser cette affirmation.

Tout d’abord, il faut parvenir à se mettre en tête que plusieurs milliers de lignes de code peuvent tout à fait s’exécuter en quelques millisecondes. Certains se sont amusés à mesurer que sur un ordinateur actuel, il est possible d’initialiser en l’espace d’une seule seconde plusieurs millions d’objets.

En réalité, les performances d’un programme sont liées d’une part à la manière dont celui-ci a été conçu, et d’autre part à quels types d’opérations celui-ci est dévolu. Il est évident qu’un programme qui en l’espace de quelques lignes de code effectue plusieurs opérations d’input/output (comme lire un fichier, ou requêter une base de données) sera plus lent qu’un autre qui se contente de manipuler des types primitifs.

Croire que la quantité de lignes de code tapée est directement corrélée à la vitesse d’exécution d’un programme est donc faux, et le programmeur débutant doit apprendre à se concentrer sur la qualité du code, et non sur la quantité, car c’est cette qualité et la manière dont le programme est conçu qui vont réellement influencer ses performances.

Rajouter des niveaux d’indirection se fait généralement au bénéfice du code

Dans le même esprit, le fait qu’un programme soit conçu avec de nombreux niveaux d’indirection (des méthodes, qui appellent d’autres méthodes, qui appellent d’autres méthodes, etc), et fasse appel à de nombreuses abstractions, ne contribue pas forcément à rendre celui-ci plus compliqué ou plus inintelligible. Bien sûr, certains paradigmes de programmation, comme les design patterns, nécessitent une solide expérience pour être compris et manipulés. Mais ces outils architecturaux sont avant tout là pour rendre le code mieux articulé, plus efficace et assurer sa pérennité; en un mot : à le rendre plus propre.

Que ce soit ces design patterns, les principes de programmation SOLID, ou tout simplement le vieux principe voulant qu’on ne doit pas copier/coller du code mais le factoriser pour mieux le réutiliser, tout tend à encourager le développeur à multiplier les niveaux d’indirection.

Constatant que j’avais tendance à écrire des fonctions faisant plusieurs centaines de lignes, un collègue de MCNext m’a un jour obligé à me limiter à un maximum de 50 lignes par fonction. En me pliant à cet exercice, j’ai appris à mieux découper mon code, à abstraire ce qui pouvait l’être, et plus généralement à voir les niveaux d’indirection comme autant de points d’articulation dans mes programmes.

Le code est à tout le monde…

Un autre point important qu’il est bon de garder en tête, est que le code d’une application appartient à tous ceux qui travaillent dessus.

Concevoir une application professionnelle est rarement le travail d’un seul homme, et chaque membre de l’équipe devrait donc pouvoir s’approprier la base de code en entier.

Cette affirmation a plusieurs conséquences. Tout d’abord, il convient qu’une équipe de programmeurs se mette d’accord sur un vocabulaire commun, qui sera réutilisé tout au long du développement. Par exemple, il y a une différence sémantique entre parler d’image et d’illustration. J’ai eu l’occasion d’utiliser cette différence dans un projet afin d’enrichir les interactions avec les autres membres de l’équipe. Cette notion de vocabulaire peut même être élargie au commanditaire de l’application, afin d’améliorer là encore la qualité des échanges.

Toujours autour de la notion de compréhension, se pose généralement la question de : on code en français ou en anglais ? En effet, la plupart des langages de programmation utilisent des mots-clefs en anglais, et le .NET ne fait pas exception. Toutefois, il est tout à fait concevable d’utiliser des noms d’entités en français. Beaucoup de développeurs préfèrent coder entièrement en anglais, toutefois cette question devrait être systématiquement débattue en équipe. Personnellement, j’ai tendance à préférer coder en anglais, mais avec les commentaires rédigés en français, afin de s’assurer qu’aucun détail technique (que l’on transmet généralement dans ces commentaires) ne soit perdu. Par contre il est préférable d’éviter d’imposer l’anglais à une équipe où le niveau dans cette langue serait plutôt faible; les fautes d’orthographe et de grammaire ne sont généralement pas d’une grande aide lorsqu’il s’agit d’améliorer la compréhension d’un programme.

Mais le point le plus important de cette notion de le code est à tout le monde, c’est que chaque membre de l’équipe devrait pouvoir intervenir comme bon lui semble sur n’importe quelle partie du programme. Bien sûr telle ou telle fonctionnalité qui aura été codée par tel ou tel développeur sera mieux comprise de celui-ci. Toutefois, aucun développeur ne devrait considérer un code qu’il a écrit comme son code, et chacun devrait pouvoir contribuer à améliorer continuellement la base de code. Il ne faut donc pas hésiter à réécrire le code d’un autre si l’on pense que celui-ci peut être amélioré, que ce soit en terme de compréhension ou de performances.

Toutefois, il s’agit de savoir se montrer diplomate. Il est facile de vexer les autres lorsqu’on remet en cause la qualité de leur travail. Par ailleurs, j’ai souvent vu des développeurs modifier le code d’un autre non pas pour l’améliorer, mais par pure maniaquerie (en l’indentant d’une manière particulière par exemple). Même si nous sommes tous un peu maniaques, ce genre de pratiques est à proscrire.

En outre, renommer des entités pour garder une base de code homogène est un bien, mais ne doit pas aboutir à des aller/retour continuel pour décider qui de Michel ou Jean-Pierre a choisi le meilleur terme. Les règles de nommage, qu’on établi au début d’un projet, sont aussi là pour éviter ce type de situation.

Un développeur junior ne doit en tout cas pas avoir le moindre scrupule à "bousculer" le code des autres s’il juge que ses changements sont opportuns.

…et tout le monde est susceptible de travailler dessus

Un développeur ne devrait jamais oublier que d’autres personnes sont susceptibles d’intervenir sur le code qu’il écrit, et donc de s’assurer que celui-ci respecte les canons de lisibilité et de maintenabilité du métier. Un code se doit d’être conçu de manière à expliciter de la manière la plus claire possible son fonctionnement et sa finalité.

Pourtant, parvenir à atteindre un tel niveau de qualité n’est pas chose aisée, surtout pour un débutant, et même en y mettant la meilleure volonté du monde, il n’est pas rare lorsqu’on a plusieurs mois de recul d’éprouver soi-même des difficultés à se replonger dans un code qu’on avait pourtant programmé soi-même.

Comparez les deux bases de code suivantes. Laquelle vous paraît la plus compréhensible ?

public int GetResult(string number)
{
    var order = (from o in Session.Orders
                 where o.Id == number
                 select o).First();

    /*********************************************
     * Ici beaucoup trop de code permettant de calculer
     * le statut d'une commande.
     ********************************************/

     int result = resultatDesCalculsPrecedents;
     return result;
}
public enum OrderStatus
{
    Canceled,
    Pending,
    Paid,
    Delivered
}

public OrderStatus GetOrderStatus(string orderId)
{
    Order order = OrderRepository.GetOrderFromId(orderId);
    OrderStatus orderStatus = new OrderStatusSpecification(order).Execute();
    return orderStatus;
}

La différence de clarté entre ces deux codes est flagrante. Le code de qualité utilise des termes très précis pour expliciter son fonctionnement et sa finalité, là où l’autre reste très vague.

Même le type de retour est différent. Le mauvais code renvoie un simple nombre entier qui n’a de sens que celui que l’équipe aura bien voulu lui donner par convention, ce sens risquant d’évoluer pendant le cycle de développement de l’application, avec les conséquences qu’on imagine. Utiliser une énumération permet à peu de frais d’expliciter le sens du résultat de l’opération.

Par ailleurs, dans le code de qualité, les calculs potentiellement complexes sont isolés par un niveau d’indirection, alors que dans l’autre, tout est compressé dans un seul bloc.

S’attacher à rendre un programme plus clair ne permet pas seulement à l’équipe de travailler dans de meilleures conditions, cela permet aussi de rendre le code plus pérenne.

Parvenir à remplir au mieux cet objectif s’apprend beaucoup avec l’expérience. Toutefois je ne saurai trop vous conseiller cet excellent livre (en anglais) qui donne de nombreuses clefs pour apprendre à manipuler du code et à le rendre plus souple.

En tout état de cause, on devrait toujours penser aux autres développeurs lorsque l’on conçoit un programme; ceux qui sont susceptibles de travailler dessus aussi bien demain que dans un an.

Comment ne pas simplifier la vie aux pirates sur votre site ASP.NET MVC

Voici une capture d’écran de l’en-tête de la réponse HTTP envoyée par une application ASP.NET MVC hébergée par un serveur web IIS :

Capture de réponse HTTP-

Comme on le voit, avec chaque requête à laquelle l’application répond sont envoyées un certain nombre d’informations concernant :

  • Server : le type de serveur hébergeant l’application, ainsi que sa version.
  • X-AspNet-Version : la version du moteur ASP.NET utilisé par l’application.
  • X-AspNetMvc-Version : la version spécifique d’ASP.NET MVC utilisée par l’application.
  • X-Powered-By : Le langage de programmation utilisé côté serveur.

Tout cela peut s’avérer extrêmement néfaste pour votre application! En effet, ces informations ne regardent personne, et surtout pas les pirates qui ne demandent rien de mieux que d’essayer de s’infiltrer par toutes les failles de sécurité concernées par ces technologies, et auxquelles votre serveur est susceptible d’être sensible s’il n’a pas été correctement mis à jour.

Pour vous donner une idée, cela revient à donner à un cambrioleur votre adresse ainsi que la marque et le modèle de votre serrure. Tout ce qui lui manque, c’est la forme de la clef.

Malheureusement, communiquer ces informations est le comportement par défaut de toute application ASP.NET MVC. Nous allons voir néanmoins comment modifier ce comportement pour faire disparaître ces données de nos en-têtes de réponse HTTP.

Commençons par le plus simple : pour retirer l’en-tête X-AspNetMvc-Version, il suffit de rajouter la ligne suivante dans la méthode Application_Start qui se trouve dans la classe MvcApplication de votre fichier Global.asax.cs.

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    // Ajoutez cette ligne :
    MvcHandler.DisableMvcResponseHeader = true;

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
}

Pour ce qui est des en-têtes X-AspNet-Version et X-Powered-By, il va vous falloir effectuer quelques modifications dans le fichier web.config à la racine de votre application web.

D’abord, s’il n’est pas encore présent, ajoutez un nœud "configuration/system.web/httpRuntime". Pour désactiver l’en-tête X-AspNet-Version, ajoutez-y un attribut "enableVersionHeader" et donnez-lui la valeur "false".

<system.web>
    <httpRuntime enableVersionHeader="false" />
    <!-- Ici le reste de la section system.web -->
</system.web>

Ensuite, pour supprimer l’en-tête X-Powered-By, rendez-vous dans la section "configuration/system.webServer". S’ils ne sont pas encore présents, ajoutez-y la série de nœuds "httpProtocol/customHeaders", et adjoignez-y un nœud "remove" dont l’attribut "name" devra avoir la valeur "X-Powered-By". Notez que comme pour tout ce qui a trait à la section "configuration/system.webServer", vous pouvez directement effectuer cette manipulation dans la console d’IIS 7.x.

<system.webServer>
    <httpProtocol>
      <customHeaders>
        <remove name="X-Powered-By" />
      </customHeaders>
    </httpProtocol>
    <!-- Ici le reste de la section system.webServer -->
</system.webServer>

L’en-tête Server est le cas le plus délicat. En effet, il est impossible de supprimer cet en-tête, que ce soit dans la configuration de notre application web, ou dans celle d’IIS. Pour arriver à nos fins, nous allons donc devoir intercepter la réponse HTTP avant que celle-ci soit envoyée au client, et la modifier.

Pour effectuer cette opération, nous allons altérer le pipeline de traitement de notre application web, en utilisant un HttpModule. Pour cela, il suffit de créer une classe héritant de IHttpModule. J’ai choisi d’appeler ma classe "CustomHeadersModule". Nous devons d’ores et déjà implémenter les membres de l’interface "IHttpModule"; il s’agit des méthodes "Init" et "Dispose". Vous pouvez laisser "Dispose" vide. Par contre dans "Init", nous allons nous abonner à l’évènement "PreSendRequestHeaders".

public class CustomHeadersModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.PreSendRequestHeaders += OnPreSendRequestHeaders;
    }

    public void Dispose()
    {
    }

    private void OnPreSendRequestHeaders(object sender, EventArgs e)
    {
        HttpContext.Current.Response.Headers.Remove("Server");
        // Ou bien
        HttpContext.Current.Response.Headers.Set("Server", "Apache");
    }
}

Dans l’EventHandler qui va nous servir à nous abonner, nous allons pouvoir librement et au choix, soit supprimer complètement l’en-tête Server, soit plus sournoisement la modifier pour donner l’impression que nous utilisons en réalité une autre technologie de serveur web (ici en l’occurrence Apache, très populaire et principal concurrent d’IIS).

Il reste toutefois une action à effectuer : enregistrer ce HttpModule dans le pipeline de traitement de notre application ASP.NET MVC, ce qui se fait soit directement dans le console d’IIS, soit en modifiant à nouveau la section "configuration/system.webServer" de notre fichier web.config, cette fois-ci dans la sous-section "modules".

<system.webServer>
    <modules>
        <add name="CustomHeadersModule" type="VotreNamespace.CustomHeadersModule" />
    </modules>
    <!-- Ici le reste de la section system.webServer -->
</system.webServer>

Toutes ces customisations vont contribuer à rendre votre application web plus sûre. Mais bien sûr, avoir un serveur web correctement mis à jour est encore le meilleur moyen de se prémunir des attaques de pirates.

Bonnes pratiques pour stocker des informations dans les fichiers de configuration

Lors du développement d’une application, il est courant de stocker des informations dans les fichiers de configuration, que ce soit "app.config" pour les clients lourds, ou "web.config" pour les applications web.

La méthode la plus simple pour stocker ces informations est d’utiliser la section "AppSettings".

<AppSettings>
  <add key="ContactMail" value="contact@mcnext.com" />
</AppSettings>

 

string mail = ConfigurationManager.AppSettings["ContactMail"];

C’est une manière pratique de conserver des données qui pourront être facilement modifiées en production, car ces fichiers ne sont pas compilés.

Malheureusement, on constate souvent que ces données ont tendance à s’empiler au fur et à mesure que le développement avance, au point de se retrouver parfois avec d’interminables sections "AppSettings".

Première alternative : créer ses propres SectionHandler

Une alternative à ce problème est de créer ses propres sections de configuration. Il faut pour cela créer une classe qui hérite de ConfigurationSection.

namespace ApplicationNamespace
{
    public class ContactSectionHandler : ConfigurationSection
    {
        [ConfigurationProperty("Mail", IsRequired = true)]
        public string Mail
        {
            get
            {
                return (string)this["Mail"];
            }
        }
    }
}

 

<configuration>
  <configSections>
    <section name="contact" type="ApplicationNamespace.ContactSectionHandler, NomDeLAssembly" />
  </configSections>
  <contact Mail="contact@mcnext.com" />
</configuration>

L’accès aux données se faisant ensuite tout aussi simplement qu’avec "AppSettings" :

string mail = ((ContactSectionHandler)ConfigurationManager.GetSection("contact")).Mail;

Ces sections de configuration personnalisées peuvent aussi contenir des collections de pairs clef/valeur. Cela se fait en créant une classe qui hérite de ConfigurationElementCollection pour représenter la collection, et une autre classe qui hérite de ConfigurationElement pour représenter un élément de cette collection. On peut alors obtenir ce type de représentations :

<contacts>
  <contact nom="Hémery" prenom="Pierre-Yves" fonction="Directeur du Pôle .NET" mail="pole-dotnet@mcnext.com" />
  <contact nom="Baduel" prenom="Roch" fonction="Directeur du Pôle Biztalk" mail="pole-biztalk@mcnext.com" />
</contacts>

Expliquer dans le détail comment créer ces sections de configuration personnalisées serait un peu long. Je vous invite à vous reporter à la documentation sur MSDN pour obtenir des informations supplémentaires. Car ce qui nous intéresse ici, c’est la seconde alternative : celle qui permet de ne pas avoir à créer de section personnalisées, tout en évitant de se retrouver avec des fichiers de configuration où tout est mélangé.

Seconde alternative : utiliser les SectionHandler "clefs en main"

Une alternative peu connue et beaucoup plus simple que la création de sections personnalisées est l’utilisation des SectionHandler "clefs en main" fournis en standard dans le Framework .NET. Ceux-ci sont au nombre de trois :

  • SingleTagSectionHandler, qui permet de créer une section simple contenant des attributs.
  • NameValueSectionHandler, qui permet de créer une collection de paires clef/valeur, où chaque clef peut être répétée plusieurs fois.
  • DictionarySectionhandler, qui permet de créer une collection de paires clef/valeur, où chaque clef doit être unique.

Il faut tout d’abord déclarer ces sections dans le nœud "configuration/configSections" :

<configuration>
  <configSections>
    <!-- Les noms donnés à chaque section sont purement arbitraires -->
    <section name="singleSection" type="System.Configuration.SingleTagSectionHandler" />
    <section name="nameValueSection" type="System.Configuration.NameValueSectionHandler" />
    <section name="dictionarySection" type="System.Configuration.DictionarySectionHandler" />
  </configSections>
  <!-- ... -->
</configuration>

Il ne reste ensuite plus qu’à utiliser ces sections :

<singleSection nom="Hémery" prenom"Pierre-Yves" fonction="Directeur du Pôle .NET" mail="pole-dotnet@mcnext.com" />
<nameValueSection>
  <add key="ValeurEnDouble" value="toto" />
  <add key="ValeurEnDouble" value="titi" />
  <add key="AutreValeur" value="foobar" />
</nameValueSection>
<dictionarySection>
  <add key="DRH" value="drh@mcnext.com" />
  <add key="CONTACT" value="contact@mcnext.com" />
</dictionarySection>

La récupération des données dans le code C# se fait là encore très facilement. SingleTagSectionHandler et DictionarySectionHandler vous renverront une Hashtable, tandis que NameValueSectionHandler vous renverra une NameValueCollection.

string mail = ((Hashtable)ConfigurationManager.GetSection("singleSection"))["mail"];
// valeurEnDouble aura la valeur "toto,titi" (nos deux valeurs séparées par une virgule)
string valeurEnDouble = ((NameValueCollection)ConfigurationManager.GetSection("nameValueSection"))["ValeurEnDouble"];
string drh = ((Hashtable)ConfigurationManager.GetSection("dictionarySection"))["DRH"];

Comme on le voit, ces SectionHandler "clefs en main" permettent d’organiser très simplement les données stockées dans les fichiers de configuration, sans avoir à en passer par l’exercice souvent fastidieux de la création de sections personnalisées.

Une autre bonne pratique qu’il est bon de noter est d’encapsuler l’accès à ces données dans des classes statiques, et d’y gérer l’éventuelle levée d’exceptions. Par exemple en contrôlant qu’une certaine clef existe bel et bien.

public static class ConfigurationData
{
    private static readonly Hashtable _sectionDatabaseAccess = (Hashtable)ConfigurationManager.GetSection("databaseAccess");

    public static int Timeout
    {
        get
        {
            if (_sectionDatabaseAccess.ContainsKey("timeout"))
            {
                int val;
                if (!int.TryParse(_sectionDatabaseAccess["timeout"].ToString(), out val))
                    throw new Exception(@"La clef ""timeout"" de la section ""databaseAccess"" doit stocker un nombre entier.");

                return val;
            }

            throw new Exception(@"La section de configuration ""databaseAccess"" devrait contenir une valeur pour la clef ""timeout"".");
        }
    }
}

L’accès aux données s’en trouve nettement facilité :

int timeout = ConfigurationData.Timeout;

Localiser la saisie utilisateur côté client dans ASP.NET MVC 3

Depuis la première version d’ASP.NET MVC, un des composants qui a subi le plus de modifications est sans doute la validation côté client. Au départ Microsoft avait développé sa propre librairie Javascript, avant de se tourner peu à peu vers jQuery, une librairie très populaire qu’ils ont choisi d’intégrer directement à leur solution.

Le contrecoup de ce revirement est que selon qu’une application est basée sur ASP.NET MVC 1, 2 ou 3, la validation côté client peut fonctionner selon des mécanismes assez différents. La localisation de cette validation en particulier peut s’avérer assez délicate à mettre en place.

La localisation de la validation dans ASP.NET MVC 3

Quand on parle de localisation de la validation, on ne fait pas seulement allusion au fait que les messages qui s’affichent soient dans la langue attendue, il s’agit aussi que les informations saisies par l’utilisateur soient validées selon les règles adéquates. Par exemple, que penser d’une application entièrement en français qui refuserait le nombre décimal "5,5" parce que celui-ci contient une virgule comme séparateur décimal? Ce n’est probablement pas ce à quoi s’attendraient la plupart des utilisateurs.

Avec ASP.NET MVC 3, parvenir à mettre en place cette localisation est un vrai parcours du combatant. En effet, tout repose maintenant sur le plugin jQuery Validation, qui utilise un système de "règles" pour valider la saisie utilisateur. Les règles fournies par défaut sont adaptées à la culture anglaise, et il faut les surcharger, c’est à dire les remplacer et les adapter, si l’on souhaite que notre application accepte de valider une culture différente.

Ce que fournit jQuery Validation

En standard, jQuery Validation fourni trois autres groupes de règles, pour les cultures allemande, néerlandaise et brésilienne. Mais non seulement celles-ci sont très incomplètes (la néerlandaise et la brésilienne ne surchargent que la règle de validation des dates) mais aucune ne colle à la culture française. Et voici ce que propose le fichier de règle pour la culture allemande :

/*
 * Localized default methods for the jQuery validation plugin.
 * Locale: DE
 */
jQuery.extend(jQuery.validator.methods, {
	date: function(value, element) {
		return this.optional(element) || /^\d\d?\.\d\d?\.\d\d\d?\d?$/.test(value);
	},
	number: function(value, element) {
		return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:\.\d{3})+)(?:,\d+)?$/.test(value);
	}
});

Les allemands utilisent bien la virgule comme séparateur décimal; en revanche ils utilisent le point comme séparateur de milliers (alors que nous utilisons un espace blanc). Bien sûr il serait facile de recopier le contenu de ce fichier avant de l’adapter, mais ce scénario n’est réaliste que si notre application n’est conçue que pour la langue française. Que faire si cette dernière est multilingue?

La librairie jQuery Globalization

L’intérêt récent de Microsoft pour jQuery les a amené à proposer leurs propres plugins : jQuery DataLink pour la liaison de données, jQuery Templates pour le templating et jQuery Globalization pour la localisation. Jusqu’à il y a peu, ces trois plugins, encore à l’état de béta version, étaient considérés par l’équipe jQuery comme des plugins "officiels". Ce n’est plus le cas, toutefois les deux premiers plugins ont vu leur existence prolongée par leur créateur, sous la forme de JsRender et JsViews. Ils devraient être à terme inclus dans la librairie jQuery UI. Quand à jQuery Globalization, il était déjà très complet et parfaitement utilisable en production, et c’est heureux puisque nous allons nous en servir pour rendre notre application multilingue.

jQuery Globalization est composé de très nombreux fichiers correspondant à autant de cultures. Ces fichiers exposent les caractéristiques de chacune de ces cultures, ainsi que des fonctions permettant de traduire aussi bien les dates que les nombres décimaux, et bien plus encore.

Pour implémenter ce plugin dans une application web ASP.NET MVC 3, rien de plus simple. Après l’avoir téléchargé et placé dans notre solution, nous devons tout d’abord le référencer :

<script type="text/javascript" src="@Url.Content("~/Scripts/jquery/plugins/glob/jquery.global.js")"></script>

Ensuite nous devons charger dynamiquement le fichier de localisation correspondant à la culture de l’utilisateur, et configurer le plugin pour utiliser celui-ci :

<script type="text/javascript" src="@Url.Content("~/Scripts/jquery/plugins/glob/globinfo/jquery.glob." + Request.UserLanguages[0] + ".js")"></script>
<script type="text/javascript">
    jQuery.global.preferCulture("@Request.UserLanguages.ToCorrectCase()");
</script>

Request.UserLanguages est un tableau permettant d’obtenir côté serveur la liste des cultures configurées (par ordre de préférence) dans le navigateur de l’utilisateur. Vous vous demandez sans doute à quoi sert la fonction ToCorrectCase(). Malheureusement certain navigateurs comme Firefox renvoient "fr-fr" pour la langue française quand ils devraient renvoyer "fr-FR" (notez la différence de casse), et jQuery Globalization est sensible à ce détail; il faut donc s’y adapter et c’est à ça que sert cette fonction.

N’oubliez pas que les fichiers source de jQuery sont disponibles sur le CDN de Microsoft, tout comme de nombreux plugins.

Tout le nécessaire est maintenant en place pour permettre la localisation multilingue de notre application.

Comment relier jQuery Validation et jQuery Globalization

La dernière étape va consister à relier jQueryValidation, qui s’occupe de la mécanique de validation côté client de l’application, à jQuery Globalization, qui s’occupe exclusivement de la localisation.

Pour cela, nous devons faire référencer un nouveau fichier Javascript par notre application, et y ajouter le code suivant :

jQuery.extend(jQuery.validator.methods, {
    date: function (value, element) {
        return this.optional(element) || jQuery.global.parseDate(value) != null;
    },
    number: function (value, element) {
        return this.optional(element) || !isNaN(jQuery.global.parseFloat(value));
    },
    range: function (value, element, param) {
        value = jQuery.global.parseFloat(value);
        return this.optional(element) || (value &gt;= param[0] &amp;&amp; value &lt;= param[1]);
    }
});

Par le biais de ce code nous surchargeons trois règles de validation de jQuery Validation : la validation de la date, des nombres décimaux, et des intervalles. D’autres règles pourraient potentiellement être également surchargées.

Grâce à ce "pont" établi entre les deux plugins, jQuery Validation va maintenant se servir des fonctions de jQuery Globalization pour valider la saisie de l’utilisateur. Supposons que celui-ci doive indiquer quel sera le pourcentage de TVA sur un produit quelconque. La propriété correspondante dans le modèle de la vue ressemblerait à ceci :

/// <summary>
/// TVA applicable sur le produit
/// </summary>
[Range(0.0, 100.0)]
public double TVA { get; set; }

Notez que la règle de validation de nombre décimal est implicite dès lors qu’il s’agit d’un type "double" (nombre décimal à double précision).

Maintenant si par exemple l’utilisateur a son navigateur configuré dans la culture française, et qu’il saisit "5,5", nous serons en mesure de valider que ce nombre est dans un format décimal correct, et qu’il est compris dans une intervalle allant de 0 à 100. Notre application web est donc bien multilingue.

Suivre

Recevez les nouvelles publications par mail.