L’utilisation de plus en plus grande du JavaScript coté client a fait émerger des « moteurs » de templating. Le principe de ces librairies est de faciliter la génération de code HTML en JavaScript en utilisant des modèles. Cela s’avère très pratique notamment dans le cas de mise à jour d’une page avec le résultat d’un appel AJAX. Le site template-chooser permet de choisir parmi une liste de librairies suivant certains critères. Pour cet article j’utiliserai la librairie JsRender, librairie développée par Boris Moore, un des développeurs de JQuery UI.
Pour notre exemple nous allons récupérer les tweets d’un utilisateur de twitter (user_timeline) que l’on récupérera via une requête AJAX. L’utilisateur est renseigné via une textbox (documentation sur l’API de twitter). Le résultat nous est retourné sous la forme d’un tableau au format JSON.
Installation
Il suffit d’inclure JsRender via la ligne suivante
<script src="include/jsrender.js"></script>
Contexte
Voici ci dessous le code HTML de la page de démo que nous allons utiliser :
<!-- Le formulaire de recherche --> <form action="#" id="searchForm"> <label for="twittername">Utilisateur twitter :</label><input type="text" placeholder="username" id="twittername" /> afficher les RT : <input type="checkbox" checked="checked" id="includeRT" /> <input type="submit" id="search" value="Rechercher" /> </form> <!-- Le conteneur --> <div id="tweets"></div>
Déclaration d’un template
Sans l’utilisation de template, nous sommes obliger de créer nos code HTML soit en créer des élements HTML (createElement) soit en faisant de la concaténation de string.
// sans template var i = 1; $(mytweets).each(function () { var tweet = this; $("#tweets").append("<div class=tweet>" tweet.user.screen_name + "</br>" + tweet.text + "</div>"); });
Par terrible, non ?
Voyons comment utiliser les templates via JsRender.
Il existe plusieurs façon de déclarer un template (modèle) :
- en JavaScript
- en pseudo HTML en le spécifiant comme un script de type text/x-jsrender
Voici la déclaration en JavaScript pur :
//Déclaration de notre template $.templates({ tweetsTemplate : "<div class="tweet">{{>user.screen_name}} </br> {{>text}}</div>" });
En Pseudo HTML :
<script id="tweetsTemplate" type="text/x-jsrender"> <div class="tweet"> {{:user.screen_name}} </br> {{:text}}</div> </script>
On peut voir que la description et la maintenance des templates est plus aisée en pseudo HTML, surtout dans le cas de template complexe. Je vous conseille d’utiliser cette méthode de déclaration que nous utiliserons dans la suite de l’article.
Comme vu précédemment, la déclaration d’un template JsRender se fait en HTML avec l’utilisation de tags JSRender se trouvant entre {{ et }}.
Voici les principaux tags :
- {{:name }} : Rendu sans encodage d’une propriété (name dans notre exemple)
- {{>name }} : Rendu avec encodage HTML d’une propriété
- {{for entities.hashtags}} : Parcours d’un tableau (hastags dans notre exemple)
- {{if test}} … {{else}} {{/if}} : Conditions
Une fois le template défini, celui-ci peut être utilisé pour effectuer le rendu de vos données.
Génération du HTML via le template
Le rendu de données via un template se fait par l’appel à la méthode render.
Nous avons besoin de 3 choses :
- un template
- des données
- un conteneur dans lequel les données vont être rendues
$("#tweets").html($("#tweetsTemplate").render(data));
On peut noter que nous utilisons ici JQuery pour mettre à jour le contenu de notre conteneur (#tweets) mais JsRender est indépendant de JQuery.
Le code complet :
//Abonnement au click du bouton rechercher $('#search').click(function() { //Définition de l'URL du service var url='http://api.twitter.com/1/statuses/user_timeline.json?callback=?&include_entities=1&count=10&screen_name=' + $('#twittername').val(); //Inclusion des retweets if ($('#includeRT').is(':checked') ) { url += '&include_rts=1'; } //Appel du service REST et affichage des données $.getJSON(url, function(data) { $("#tweets").html( $("#tweetsTemplate").render( data ) ); }); });
Aller plus loin avec JsRender
view path
Un objet est rarement plat. Il peut contenir des objets enfants. JsRender permet de parcourir ses objets. On a pu voir dans les exemples précédent que l’accès à une propriété se fait par l’utilisation du point ({{:user.screen_name}}). JsRender fournit également des mots clés spéciaux facilitant l’accès à ces données.
#view fournit un accès à la vue courante, #data un accès au context courant de la vue, #parent permet d’accéder à l’objet parent et #index renvoi la position dans la liste.
Dans l’exemple suivant, nous affichons le numéro du tweet via le mot clé index. On peut également constater que les opérations basiques sont fonctionnelles (addition, soustraction, multiplication, division, comparaison, égalité, …)
<script id="tweetsTemplate" type="text/x-jsrender"> <div class="tweet"> <p class="tweetInfo"> <img src="{{:user.profile_image_url}}" title="{{:user.description}}" /> {{:#index+1}} : <span class="twitterName">{{:user.screen_name}}</span> ({{:user.name}})</b> </p> <p class="tweetMessage">{{:text}}</p> </div> </script>
boucles
Il est possible d’itérer sur les collections de nos objets. Dans notre exemple, nous récupérer les hashtags de nos tweets. Nous allons les parcourir afin de les afficher. Nous utilisons la « balise » for :
{{for entities.hashtags }}  #{{>text}} {{/for}}
sous templates
Il est également possible de définir des templates qui seront appelés par des templates parents. Ainsi nous allons en déclarer un pour les hashtags et l’appeler dans la balise for. Le code en devient donc beaucoup plus simple, lisible et maintenable.
la définition du template
<script id="hashtagsTemplate" type="text/x-jsrender">  #{{>text}} </script>
L’appel du template se fait via l’attribut tmpl …
hashtags : {{for entities.hashtags tmpl="#hashtagsTemplate" /}}
Conditions (if)
JsRender supporte les conditions if/else. Voici un exemple qui teste si le tweet est un retweet et ajoute la classe retweet à notre élément p.
<p class="tweetMessage {{if retweeted_status}} retweet{{/if}}">{{:text}}</br>
externalisation
Dans le cadre d’un site complexe, vos templates pourront être utilisés dans plusieurs pages. Il devient alors nécessaire d’externaliser vos scripts afin de ne déclarer qu’une seule fois ceux ci et de les charger dans les pages correspondantes.
Je vais reprendre ici une fonction issus d’un article de John Papa qui permet de charger des templates, présenté dans un article (cf Liens). Ceux-ci doivent se trouver dans le fichier /templates. de votre site, commencer par un underscore et avoir l’extension .tmpl.html.
my.utils = (function () { var formatTemplatePath = function (name) { return "/templates/_" + name + ".tmpl.html"; }, renderTemplate = function (tmplName, targetSelector, data) { var file = formatTemplatePath(tmplName); $.get(file, null, function (template) { var tmpl = $.templates(template); var htmlString = tmpl.render(data); if (targetSelector) { $(targetSelector).html(htmlString); } return htmlString; }); }; return { formatTemplatePath: formatTemplatePath, renderExternalTemplate: renderTemplate }; })() //Utilisation my.utils.renderExternalTemplate("tweetTemplate", "#tweets", my.vm);
Voici le page de démo avec un style inspiré de l’interface Métro de Windows 8.
Liens
- Le site github du projet
- Le site des démos officielles : pas très pratique et fait office de documentation. On est obliger d’aller voir les sources des pages pour voir les exemples … 🙁
- Je vous conseille plutôt 2 articles de John Papa très bien fait mais en anglais :
Conclusion
JsRender est un moteur de template très puissant et extensible. Il permet également la compilation de template, un mode permettant l’autorisation de code JavaScript dans les templates, helpers, converters. De plus il permet une séparation entre la présentation de vos données et la gestion de celles ci.
Tweet