Resumé

JsRender as a Templating Solution

Cristian Merighi () 0.00

Purely client-side implementation for a twitter feed-reader using JsRender along with "some" other javascript frameworks.
This article is obsolete. Some functionalities might not work anymore. Comments are disabled.

I want to conform myself to the confusing maelstrom ruling the world of javascript components and try to offer my personal two-cents solution about putting together (I mean PUTTING TOGETHER) a purely client-side Twitter parser.

Let me introduce the guys who'll help me to get my job done. They're MANY, some of them are issue-prones, some of them are wonderful at some aspects but miss (or gave up!) some others... (Fairly pissed off by the actual js world snapshot, does it show?)

  • Microsoft Ajax 4 (String.format, Date.parseInvariant, Array.insert...)
  • jQuery (Async calls, DOM manipulation, Utilities...)
  • JsRender (Templating, Data formatting...)
  • Other stuff (jQuery-relevant) of restricted utility...

Goal: using the Twitter REST Api to retrieve and display tweets in a fully comprehensive fashion (w/ avatars, hashtags, mentions and links), using just DHTML technologies (sorry for having resusciated this 20th century term).

Dissecting the code, let's start with the declarative JsRender template:

JsRender Template
  1. <ul id="tweets" class="Tweets">
  2. <li><div class="Loader"></div></li>
  3. </ul>
  4. <script id="twitTmpl" type="text/x-jsrender">
  5.     <li><img src="{{:user.profile_image_url_https}}" />{{:~renderTweet(#view)}}<span class="Date">{{:~timeago(created_at)}}</span></li>
  6. </script>

I've highlighted the bindings to the tweet entity properties: user along with created_at are respectively a complex and a simple/plain "property" for the json tweet representation, on the other side #view wraps the whole entity object (looks like a DataItem coming from the ASP.Net WebForms environment).
JsRender syntax: {{:[_property_].[_subproperty_]}} (please notice there's no spaces between braces and colons, nor between colon and text!).
{{:~foofunc([_propertypath_or_view_])}} to adjust the output before the very rendering (foofunc is a "view helper method").

Where do these helper methods go (in the following code snippet they're named renderTweet e timeago)?

View Helper Methods
  1. // "sort-of" converters
  2. $.views.helpers({
  3.     // accepts the whole (wrapped) dataItem (aka "#view")
  4.     renderTweet: function (view) {
  5.         var tweet = view.data;
  6.         var txt = tweet.text;
  7.         var rettxt = '';
  8.         var entities = tweet.entities;
  9.         var trunks = [];
  10.         if (entities) {
  11.             // sorting the various entity trunks
  12.             $.each(entities.hashtags, function (i, hash) {
  13.                 pushTrunk(trunks, { start: hash.indices[0], end: hash.indices[1], text: hash.text, type: 'hash' });
  14.             });
  15.             $.each(entities.urls, function (i, url) {
  16.                 pushTrunk(trunks, { start: url.indices[0], end: url.indices[1], text: url.url, type: 'url' });
  17.             });
  18.             $.each(entities.user_mentions, function (i, usr) {
  19.                 pushTrunk(trunks, { start: usr.indices[0], end: usr.indices[1], text: usr.screen_name, type: 'user' });
  20.             });
  21.             // assembling the final html string (rettxt)
  22.             var lastIndex = 0;
  23.             $.each(trunks, function (i, trunk) {
  24.                 switch (trunk.type) {
  25.                     case "hash":
  26.                         rettxt += txt.substring(lastIndex, trunk.start) + "<a target=\"_blank\" href=\"https://twitter.com/search/%23" + trunk.text + "\">#" + trunk.text + "</a>";
  27.                         break;
  28.                     case "url":
  29.                         rettxt += txt.substring(lastIndex, trunk.start) + "<a target=\"_blank\" href=\"" + trunk.text + "\">" + trunk.text + "</a>";
  30.                         break;
  31.                     case "user":
  32.                         rettxt += txt.substring(lastIndex, trunk.start) + "<a target=\"_blank\" href=\"https://twitter.com/" + trunk.text + "\">@" + trunk.text + "</a>";
  33.                         break;
  34.                 }
  35.                 lastIndex = trunk.end;
  36.             });
  37.             rettxt += txt.substring(lastIndex);
  38.         } else rettxt = txt;
  39.         return rettxt ;
  40.     },
  41.     timeago: function (createdAt) {
  42.         // using timeago plugin (issues with UTC date and L10N)
  43.         // Date.parseInvariant is exposed by Ajax 4
  44.         return $.timeago(Date.parseInvariant(createdAt, 'ddd MMM dd HH:mm:ss +0000 yyyy'));
  45.     }
  46. });

Please notice the specific framework lures in the code comments.

As a glue for bridging the declarative-template and the helping methods is the following javascript code-block (please pay attention to the highlighted lines of code and to the respective - sometimes subtle - meaning reported in each preceding line of comment):

Glue Code
  1. function loadTweets() {
  2.     // obey to cross-site-scripting issues
  3.     $.support.cors = true;
  4.     // format url using Ajax 4 String.format
  5.     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);
  6.     // jQuery ajax (fluent version)
  7.     $.ajax({
  8.         // be sure to specify dataType jsonp
  9.         dataType: "jsonp",
  10.         url: rest_url
  11.     }).success(function (tweets) {
  12.         // build up
  13.         // FIRST jsrender template...
  14.         $.templates({ twit: '#twitTmpl' });
  15.         // ...THEN the DOM
  16.         $('#tweets').empty().append($.render.twit(tweets));
  17.     }).error(function (e) {
  18.         alert(e.statusText);
  19.     }).always(function(){ /* runs anyway */ });
  20. }

A fully-functioning DEMO is provided in order to allow to lurk the code I've omitted here ;)

Take care. Bye.

feedback
 

Syndicate

Author

Cristian Merighi facebook twitter google+ youtube

Latest articles

Top rated

Archive

Where am I?

Author

Cristian Merighi facebook twitter google+ youtube

I'm now reading

Feeds