JsRender per il Templating in javascript
Cristian Merighi ()

Sviluppo di un client per Twitter totalmente ...client! Utilizzando JsRender assieme a "qualche" altro componente javascript.
Questo articolo è da considerarsi obsoleto. Alcune funzionalità potrebbero non essere più disponibili e non è possibile aggiungere commenti.
Mi adeguo al marasma presente sulla scena dei componenti javascript e provo a presentare una soluzione riveduta e rivedibile di come assemblare un
parser per Twitter purely-client.
Introduco preventivamente i componenti coinvolti nel seguente blocco di codice e nella demo allegata:
- Microsoft Ajax 4 (String.format, Date.parseInvariant, Array.insert...)
- jQuery (Async calls, DOM manipulation, Utilities...)
- JsRender (Templating, Data formatting...)
- Altri plugin (jQuery) di utilità ristretta...
Obiettivo: utilizzare i servizi REST di Twitter per recuperare contenuti e mostrarli in una modalità quanto più completa possibile
(mostrando avatars, hashtags, mentions e collegamenti). Il tutto esclusivamente via DHTML (scusate il termine riesumato dal 20° secolo).
Sezioniamo il codice, innanzitutto la dichiarazione del template per JsRender:
JsRender Template
- <ul id="tweets" class="Tweets">
- <li><div class="Loader"></div></li>
- </ul>
- <script id="twitTmpl" type="text/x-jsrender">
- <li><img src="{{:user.profile_image_url_https}}" />{{:~renderTweet(#view)}}<span class="Date">{{:~timeago(created_at)}}</span></li>
- </script>
Ho evidenziato i punti in cui sono riportati richiami all'entità che va a popolare ogni singola istanza del template: user e created_at sono "proprietà" dell'oggetto json che rappresenta
un singolo tweet, #view rappresenta un wrapper per l'intero oggetto (una sorta di DataItem per chi è abituato alla logica del mondo WebForms di ASP.Net).
Sintassi JsRender: {{:[_property_].[_subproperty_]}} (mi raccomando, niente spazi tra parentesi graffe e due-punti!) per il rendering "diretto" del valore della property.
{{:~foofunc([_propertypath_or_view_])}} per modellare l'output con un po' di logica custom (presente nel metodo helper "foofunc") prima del rendering.
Dove posizionare questi metodi helper (nell'esempio seguente renderTweet e timeago):
View Helper Methods
- // "sort-of" converters
- $.views.helpers({
- // accepts the whole (wrapped) dataItem (aka "#view")
- renderTweet: function (view) {
- var tweet = view.data;
- var txt = tweet.text;
- var rettxt = '';
- var entities = tweet.entities;
- var trunks = [];
- if (entities) {
- // sorting the various entity trunks
- $.each(entities.hashtags, function (i, hash) {
- pushTrunk(trunks, { start: hash.indices[0], end: hash.indices[1], text: hash.text, type: 'hash' });
- });
- $.each(entities.urls, function (i, url) {
- pushTrunk(trunks, { start: url.indices[0], end: url.indices[1], text: url.url, type: 'url' });
- });
- $.each(entities.user_mentions, function (i, usr) {
- pushTrunk(trunks, { start: usr.indices[0], end: usr.indices[1], text: usr.screen_name, type: 'user' });
- });
- // assembling the final html string (rettxt)
- var lastIndex = 0;
- $.each(trunks, function (i, trunk) {
- switch (trunk.type) {
- case "hash":
- rettxt += txt.substring(lastIndex, trunk.start) + "<a target=\"_blank\" href=\"https://twitter.com/search/%23" + trunk.text + "\">#" + trunk.text + "</a>";
- break;
- case "url":
- rettxt += txt.substring(lastIndex, trunk.start) + "<a target=\"_blank\" href=\"" + trunk.text + "\">" + trunk.text + "</a>";
- break;
- case "user":
- rettxt += txt.substring(lastIndex, trunk.start) + "<a target=\"_blank\" href=\"https://twitter.com/" + trunk.text + "\">@" + trunk.text + "</a>";
- break;
- }
- lastIndex = trunk.end;
- });
- rettxt += txt.substring(lastIndex);
- } else rettxt = txt;
- return rettxt ;
- },
- timeago: function (createdAt) {
- // using timeago plugin (issues with UTC date and L10N)
- // Date.parseInvariant is exposed by Ajax 4
- return $.timeago(Date.parseInvariant(createdAt, 'ddd MMM dd HH:mm:ss +0000 yyyy'));
- }
- });
Nei commenti al codice, prego di notare i richiami ai vari frameworks javascript utilizzati.
il collante tra declarative-template e il codice sopra è rappresentato dalla seguente function javascript (fate attenzione alle istruzioni evidenziate ed al rispettivo significato riportato nei commenti che le precedono):
Glue Code
- function loadTweets() {
- // obey to cross-site-scripting issues
- $.support.cors = true;
- // format url using Ajax 4 String.format
- var rest_url = String.format("https://api.twitter.com/1/statuses/user_timeline.json?include_entities=true&include_rts=true&screen_name={0}&count={1}", 'cmerighi', 3);
- // jQuery ajax (fluent version)
- $.ajax({
- // be sure to specify dataType jsonp
- dataType: "jsonp",
- url: rest_url
- }).success(function (tweets) {
- // build up
- // FIRST jsrender template...
- $.templates({ twit: '#twitTmpl' });
- // ...THEN the DOM
- $('#tweets').empty().append($.render.twit(tweets));
- }).error(function (e) {
- alert(e.statusText);
- }).always(function(){ /* runs anyway */ });
- }
Ho approntato una DEMO funzionante per consentire di visualizzare anche la porzione di codice omesso e sperando di far cosa gradita... ;)
Take care. Bye.