Building Twitter Search using the ASP.NET Ajax Library Beta – Part II

In the first installment of this two-part series we explored how you can consume JSONP data sources using hardly any code thanks to the ASP.NET Ajax Library and the powerful networking stack that lies within.  This time around we’ll take a look at doing something useful with the data and render it using client templates, again, using the ASP.NET Ajax Library.

In the next few paragraphs, this is what we will build:

image

At this stage we have received data from the Twitter endpoint and now we want to do something useful with the JSON.  As a reminder, here is the code required to get to this point:

  1. <script src="http://ajax.microsoft.com/ajax/beta/0911/start.debug.js" type="text/javascript"></script>
  2.      
  3.     <script type="text/javascript">
  4.  
  5.         Sys.require([Sys.scripts.WebServices], function callback() {
  6.             Sys.Net.WebServiceProxy.invoke(
  7.                 "http://search.twitter.com/search.json?q=jsenior",
  8.                 null,
  9.                 true,
  10.                 null,
  11.                 function (result) { alert(result.results[0].text) });
  12.         });
  13.  
  14.         // Workaround for a bug in ASP.NET Ajax Beta, you don't need this in the final version
  15.         function createElement(tag) { return document.createElement(tag); }
  16.   
  17.     </script>

Let’s first construct how our template will look.  I want to display some basic information from a twitter search, like twitter handle, the contents of the tweet, when it was posted, and links to Twitter.com so the user can see the original tweet and the profile of the user.  Here’s what that looks like based on the documentation provided by Twitter on how their API returns data.

  1. <div id="TweetList">
  2.     <ul id="resultsView" class="sys-template">
  3.         <li>                
  4.             <a sys:href="{{ 'http://twitter.com/' + from_user }}">
  5.                 <img sys:src="{{ profile_image_url }}"  />                 
  6.                 <b>@{{ from_user }}</b></a>                     
  7.             says:                                 
  8.             <span class="tweet_created_at">
  9.                 (<a sys:href="{{ 'http://twitter.com/' + from_user + '/statuses/' + id }}">
  10.                 {{ new Date(created_at).format('dd MMMM yyyy HH:mm:ss') }}</a>)
  11.             </span>                
  12.             <br />
  13.                 {{ text }}
  14.             <br />   
  15.         </li>
  16.     </ul>
  17. </div>

As you can see, the template is fairly straightforward and the HTML is quite clean.  If you run the code in it’s current form you get the template output to the browser including all the curly braces.  This is to be expected because we haven’t attached the DataView control to the tempate – that’s the next step.

First we use the Script Loader to bring in the things like DataView, WebServices, Globalization (for the date formatting), and Watermark that we need for this app.  We’ll use the Watermark later on when we add an input box for specifying a search query.  Remember that the Script Loader takes care of loading all these components so you don’t need to know their dependencies – which is very handy with complex scripts.

  1. Sys.require([Sys.components.dataView, Sys.scripts.WebServices, Sys.scripts.Globalization, Sys.components.watermark], function () {
  2.  
  3.             var myDV = Sys.create.dataView("#resultsView");

Now that we have told the Script Loader to bring in the DataView control, we can assign it to our template, thus:

  1. var myDV = Sys.create.dataView("#resultsView");

Notice how we use the Sys namespace to do this (Sys is the ASP.NET Ajax Library namespace) and select the div using its ID, “resultsView”.  We are effectively telling the library that we want whatever is in the div to be data aware so when we bind the DataView it will repeat its contents for each row of JSON data – in this example we are repeating the LI element.

At this point let’s stop and add an input box and button to our page so we can specify the query to make to Twitter’s search API.  Use this code just before your template DIV.

  1. <div id="queryarea">
  2.         <input id="query" />   
  3.         <button id="btnQuery">Submit</button>
  4.     </div>
  5. <div id="TweetList">
  6.     <ul id="resultsView" class="sys-template">
  7.         <li>

Nice and easy, huh?  Throughout the rest of the sample we are going to refer to the input, “query”, exactly twice, so for performance reasons I am going to select it once and then store that reference in a var so I don’t need to select it any more than is absolutely necessary (this takes extra cycles for libraries like ASP.NET Ajax Library and jQuery).  Here’s the code you need to add under your existing JavaScript.

  1. var queryBox = Sys.get("#query");

Remember, when we brought in the Watermark control (part of the 35 free client-side controls in ASP.NET Ajax Library)?  We’re ready to use it!  We are going to apply the Watermark control to the input box using the var we have created above.  Here we go:

Sys.create.watermark(queryBox, "Search Twitter...", "watermark");

The first two parameters for the above method are fairly straightforward and the third one applies a style to the input box and its watermark so you can make it look how you want.

So now we have that sorted, it’s time to wire up our button to do the search when the user clicks the button.  We use the addHandler method for this purpose:

  1. Sys.addHandler("#btnQuery", "click", function () {
  2.  
  3. Sys.Net.WebServiceProxy.invoke("http://search.twitter.com/search.json?q=" + (encodeURIComponent(queryBox.value)), null, true, null, function (result) {
  4.     myDV.set_data(result.results);
  5. });
  6.  
  7. });

This method’s first parameter takes the selection of an element, in this case “btnQuery” and then applies an HTML event, “click” to it.  It then has a callback function that will fire when the user clicks the button; here we are just using an anonymous function which should be familiar to you.  Yes, it’s the WebServiceProxy.invoke method, which calls the Twitter Search API and passes it the search string.  We pass the results of the query into the function and then bind the dataset to the DataView using myDV.set_data.

Twitter nests its search results in a JSON array so that’s why we must use result.results to get to the data.

We are pretty much set. If we run the code then we’ll have the ability to search twitter and have the results displayed in the template we’ve setup.  However, those among you who have been paying attention will notice that we still have our ugly templates displayed with all the curly braces etc prior to searching.  Let’s apply a style to hide that and and the same time make our UI a bit more appealing – right now it looks like a dog’s dinner (Englishism for looking a “mess”).

Adding our styles to the page with all the JavaScript code we get the following.  The key part for hiding the template is .sys-template. Below is the full HTML page and you can just copy and paste this and you will be good to go.

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml" >
  3. <head>
  4.     <title>Untitled Page</title>
  5.     <style type="text/css">
  6.     
  7.         .sys-template {
  8.             display: none;
  9.         }
  10.         
  11.         #queryarea
  12.         {
  13.             width: 500px;
  14.             border: 3px solid #1958b7;   
  15.             padding: 10px;
  16.         }
  17.         
  18.         input {
  19.             height: 30px;
  20.             width: 280px;
  21.             font-size: 14pt;
  22.             border: 2px dotted #2586d7;
  23.             padding: 5px;           
  24.         }
  25.         
  26.         button
  27.         {
  28.             height: 45px;
  29.             width: 110px;
  30.             border: 2px solid #1958b7;
  31.             background-color: #FFFFFF;
  32.             font-size: 14pt;
  33.             color: #1958b7;
  34.         }        
  35.         
  36.         .watermark
  37.         {
  38.             font-style: italic;
  39.             color: Gray;
  40.         }
  41.         
  42.         
  43.         .tweet_created_at {
  44.             font-size: 8pt;
  45.             text-align: right;
  46.         }
  47.         
  48.         img {
  49.             height: 50px;
  50.             width: 50px;
  51.             float: left;
  52.             padding: 5px;
  53.             margin: 5px;
  54.             vertical-align: top;
  55.             margin-bottom: 15px;
  56.             border: 2px solid white;
  57.         }        
  58.         
  59.         #TweetList {
  60.             width: 490px;
  61.             padding: 0 0 1em 0;
  62.             margin-bottom: 1em;
  63.             font-family: 'Trebuchet MS', 'Lucida Grande',
  64.               Verdana, Lucida, Geneva, Helvetica,
  65.               Arial, sans-serif;
  66.             font-size: 10pt;            
  67.             color: #333;
  68.         }
  69.         
  70.         #TweetList ul {
  71.             list-style: none;
  72.             margin: 0;
  73.             padding: 0;
  74.             border: none;
  75.         }
  76.         
  77.         #TweetList li {
  78.             border-bottom: 1px solid #90bade;
  79.             margin: 0;
  80.             display: block;
  81.             padding: 5px 0px 5px 0.5em;
  82.             border-left: 10px solid #1958b7;
  83.             border-right: 10px solid #508fc4;
  84.             background-color: #2175bc;
  85.             color: #fff;
  86.             text-decoration: none;
  87.             width: 100%;
  88.             
  89.             height: 85px;
  90.         }
  91.  
  92.         html>body #TweetList li a {
  93.             width: auto;
  94.             color: White;
  95.         }
  96.  
  97.         #TweetList li a:hover {
  98.             color: Blue;
  99.         }
  100.  
  101.  
  102.     </style>
  103.  
  104.     <!-- Get the script loader from the CDN -->
  105.     <script src="http://ajax.microsoft.com/ajax/beta/0911/start.debug.js" type="text/javascript"></script>
  106.     <script src="http://ajax.microsoft.com/ajax/beta/0911/Extended/ExtendedControls.debug.js" type="text/javascript"></script>
  107.     
  108.     <script type="text/javascript">
  109.     
  110.         // We need dataView for templating, Web Services for JSONP call, Globalization for formatting the date, watermark for the input box
  111.         Sys.require([Sys.components.dataView, Sys.scripts.WebServices, Sys.scripts.Globalization, Sys.components.watermark], function () {
  112.  
  113.             var myDV = Sys.create.dataView("#resultsView");
  114.             var queryBox = Sys.get("#query");
  115.  
  116.  
  117.             Sys.create.watermark(queryBox, "Search Twitter...", "watermark");
  118.             
  119.  
  120.             Sys.addHandler("#btnQuery", "click", function () {
  121.  
  122.                 Sys.Net.WebServiceProxy.invoke("http://search.twitter.com/search.json?q=" + (encodeURIComponent(queryBox.value)), null, true, null, function (result) {
  123.                     myDV.set_data(result.results);
  124.                 });
  125.  
  126.             });
  127.  
  128.         });
  129.                
  130.     </script>
  131.     
  132.     <script type="text/javascript">
  133.  
  134.         // Workaround for a bug in ASP.NET Ajax Beta, you don't need this in the final version
  135.         function createElement(tag) { return document.createElement(tag); }
  136.         
  137.     </script>
  138.  
  139. </head>
  140. <body>
  141.     <div id="queryarea">
  142.         <input id="query" />   
  143.         <button id="btnQuery">Submit</button>
  144.     </div>
  145. <div id="TweetList">
  146.     <ul id="resultsView" class="sys-template">
  147.         <li>                
  148.             <a sys:href="{{ 'http://twitter.com/' + from_user }}">
  149.                 <img sys:src="{{ profile_image_url }}"  />                 
  150.                 <b>@{{ from_user }}</b></a>                     
  151.             says:                                 
  152.             <span class="tweet_created_at">
  153.                 (<a sys:href="{{ 'http://twitter.com/' + from_user + '/statuses/' + id }}">
  154.                 {{ new Date(created_at).format('dd MMMM yyyy HH:mm:ss') }}</a>)
  155.             </span>                
  156.             <br />
  157.                 {{ text }}
  158.             <br />   
  159.         </li>
  160.     </ul>
  161. </div>
  162. </body>
  163. </html>

This brings to close this 2-part series showing how to create a Twitter search in pure client-side code thanks to the ASP.NET Ajax Library.  We’ve covered the following important topics:

  • Getting the library from the Microsoft Ajax CDN
  • Using the Script Loader to load and execute all the required scripts and components
  • Applying the DataView Control to the Page
  • Creating a watermark
  • Adding an onClick handler to the button
  • Calling the Twitter Search API using JSONP
  • Setting up our client template

Tags: