Posts for the tag: jQuery

Jun
20
2012

Using Node.js and Socket.IO to Stream Tweets From the Twitter API

I've discussed in a previous blog post about the Twitter API. Specifically in that post I talked about making a specific call, and then getting a response back in a traditional request/response manner. However, Twitter also has a streaming service that you can connect to that will push to messages real time in a persistent, open HTTP connection.

In general, this type of service can be very powerful, because it makes it very easy to stream information to clients with out the client having request the updates. In the past, streaming data to a browser was not been easy. In actuality, websites were really just making the browser appear to be streaming data but they were really just polling for data with multiple requests. However, today's browsers now support the WebSockets protocol which makes this type of streaming rather easy to implement. It essentially gives the browser the ability to open a connection to a host service and retrieve data through the open connection. Here is what is said about this functionality on Wikipedia.

"The WebSocket protocol makes possible more interaction between a browser and a web site, facilitating live content and the creation of real-time games. This is made possible by providing a standardized way for the server to send content to the browser without being solicited by the client, and allowing for messages to be passed back and forth while keeping the connection open. In this way a two-way (bi-direction) ongoing conversation can "take place between a browser and the server."

Enter Node.js

Since this type of service is asynchronous, its the type of functionality that really fits into the Node.js sweet spot, because JavaScript is by its very nature asynchronous.

To set this Node.js application up, I am going to need a few packages. Like Ruby on Rails' Gems package manager and Microsoft.Net's Nuget package manager; Node.js has a package manager called NPM. I'm not looking to reinvent the wheel here. I just want an easy way to get access to the Twitter stream. So to accomplish, this I am using the package nTwitter. There are tons of Twitter packages for Node.js, but I found this one the easiest to get up and running.

I am also going to set up the Express NPM package which gives Node.js the ability to have a more MVC approach to building a website.

So the first thing to do is to reference all the needed packages and also reference my custom nTwitter module.

var express = require('express')
  , routes = require('./routes')
  , socketIo = require('socket.io')
  , twitter = require('ntwitter')
  , util = require('util')
  , twitterModule = require('./modules/twitterModule');

 

My twitter module reference looks like this. Here I am initializing my nTwitter reference and passing all the needed Twitter credentials to make the streaming call. As aside, you need to go the Twitter Development Site and set up an application to get all your needed credentials.

var twitter = require('ntwitter'),
	util = require('util');
 
// Twitter Setup
module.exports.twit = new twitter({
  consumer_key: 'consumer key',
  consumer_secret: 'consumer secret',
  access_token_key: 'access token',
  access_token_secret: 'access token secret'
});

 

Enter Socket.IO

In my example, I am going to stream tweets that are in my general location. To do this, the "statuses/filter" streaming API needs to be referenced. This is the streaming API provided by Twitter.

To stream the API from my server to the client I am going to use Socket.IO. Socket.IO was built mainly for Node.js, although it can be used by other languages, to be the broker that sits on both the server and the client and handles all the heavy lifting of passing the data back in forth. The nice thing is, Socket.IO has the ability to check the browser for compatibility, so if the current client browser does not support WebSockets, then Socket.IO will revert to something like polling instead.

Thus the only server code that is needed to stream the tweets to the client is this:

io.sockets.on('connection'function(socket) {
  var twit = twitterModule.twit;
  twit.stream('statuses/filter', {'locations':'-80.10,26.10,-80.05,26.15'},
    function(stream) {
      stream.on('data',function(data){
        socket.emit('twitter',data);
      });
    });
});

Socket.IO has an "on" function, where a callback function is passed in. Inside this callback function the Twitter API stream is called, and as the stream "emits" new tweets, those entries get sent to the client. The locations parameters specifies the general longitude and latitude I want the tweets to originate from.

Socket.IO and JSRender on the Client

On the client there is the corresponding Socket.IO code that is configured to listen to the URL that was set up on the server for emitting the tweets. When each tweet is emitted to the client, an event is fired to render a new item to the list.

To render the tweets on the browser, I am using jsRender. If you have ever used jQuery Templates, jsRender is very similar and is probably going to end up replacing jQuery Templates going forward; although, at the time of this writing jsRender is still in Beta.

(function($) {
	$(document).ready(function() {
		var $container = $('ul.tweets'),
			socket = io.connect('http://localhost:3000'),
			template = $('#tweetTemplate');
			
 
	    socket.on('twitter'function(data) {
	        $container.append(template.render(data));
	    });
	});
})(jQuery);

For my Node.js views, I am using Jade. Very similar to HAML in Ruby on Rails, it is very terse and clean way creating markup. No angle brackets to deal with which is nice. Jade works by determining the indentation of the each line to base how things are nested. By the way, you can get this syntactical goodness on ASP.Net MVC as well by using The Spark View Engine.

This view also contains the jsRender template that is appended for each tweet.

h1= title
p #{title}
#local-tweet-container
	ul.tweets
 
script(id="tweetTemplate", type="text/x-jsrender")
	{{:#index+1}}: <li><img src='{{:user.profile_image_url}}' /> {{:text}}</li>

Running the App

When start the application by running node app.js and then browse to http://localhost:3000, the Socket.IO code makes the following request.

 

GET http://localhost:3000/socket.io/1/websocket/21369566461136251045 HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: localhost:3000
Origin: http://localhost:3000
Sec-WebSocket-Key: Y65qAJoayH795Vbhn3Bj4w==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame
 
The response that is sent back is:
 
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: vCWC+o5mWP5mnD2enVI3/PJOhMk=
 
The 101 status means that the requestor has asked the host to change its protocal and in this case the change was to WebSockets.
 
If I look at my browser I see the tweets getting added realtime.
Socket.Io Tweets
It's kind of hard to demonstrate what is happenning in screenshot but the tweets are being added to the page and the actual page is not doing anything. Looking on Fiddler, no post or get requests are being made from my localhost domain.
 
Pretty cool stuff!
 

References

 

 

May
8
2012

Building a Blog Redux - Goodreads feed using Backbone.js (Part 5)

This is the fifth post in a series of posts about how I went about building my blogging application.

  1. Building a Blog Redux - Why Torture Myself (Part 1)
  2. Building a Blog Redux - The tools for the trade (Part 2)
  3. Building a Blog Redux - Entity Framework Code First (Part 3)
  4. Building a Blog Redux - Web fonts with @font-face and CSS3 (Part 4)

So I put a right rail column on my blog site thinking I probably would put some content over there to engage you users. I had my tag cloud, and eventually I am going to put a search box there as a future feature, but I wasn't really excited about what else to put there. I thought about a dated archive list but I have a dedicated page for that and besides, who really ever clicks on those links.

I could put some ads, but admittedly, I don't have enough traffic to consider that, and even if I did, I still probably would not do that right away. Although, I did put placeholder on the right side in case I changed my mind.

I could put my twitter feed, but I don't know; I don't think that is very interesting to user reading my blog, plus it's sort of been done by everybody already.

Goodreads

Enter Goodreads. If you read books and are social, Goodreads is the perfect site for you. You can keep track of all the books you read, categorizing them by  "Currently Reading", "Read", and "To Read". You can also categorize books by genres or whatever classification you want. The great thing about categorizing the books is Goodreads will give you recommendations based on how you have your books categorized.

Goodreads is social site, so just like Twitter, Facebook, etc. You make friends and then you can see what books they are reading and they have rated and reviewed.

And just like most other social sites, Goodreads has a developer section with an API and other tools for you to use. You just go to their website, request an application key, agree to their terms of use and then you are pretty much good to go.

The API

The API call I am using is the one called "user.show". It pretty much contains the basic information about, me, the user, such as what my bookshelves are, and (what I am most interested in) my book status updates.

The particular node I want is the action_text. Every time I update my status with a book I read or started reading, I get a new one of these nodes. The node value is HTML encoded so that task is already done by Goodreads.

<action_text>
  <![CDATA[
  gave 4 stars to: <a href="http://www.goodreads.com/book/show/12371896-the-node-beginner-book">
  The Node Beginner Book (ebook)</a> by <a href="http://www.goodreads.com/author/show/5132009.Manuel_Kiessling">
  Manuel Kiessling
  </a>
]]>
</action_text>

To get the XML for this particular message, I just need to make a GET reques to their server. Unlike Twitter, I didn't have to add a whole bunch of entries in the request header (I am going to write a post about how to do that later), I only needed the Goodreads API URL which looks like this.

http://www.goodreads.com/user/show/{user_id_number}.xml?key={your_key}

The call is a basic HttpWebRequest and HttpWebResponse.

public class HttpRequestHelper : IHttpRequestHelper
    {
        public string GetResponse(HttpWebRequest request)
        {
            ServicePointManager.Expect100Continue = false;
 
            if (request != null)
                using (var response = request.GetResponse() as HttpWebResponse)
                {
 
                    try
                    {
                        if (response != null && response.StatusCode != HttpStatusCode.OK)
                        {
                            throw new ApplicationException(
                                string.Format("The request did not compplete successfully and returned status code:{0}",
                                              response.StatusCode));
                        }
                        if (response != null)
                            using (var reader = new StreamReader(response.GetResponseStream()))
                            {
                                return reader.ReadToEnd();
                            }
                    }
                    catch (WebException exception)
                    {
                        return exception.Message;
                    }
                }
            return "The request is null";
        }
 
 
        public HttpWebRequest GetRequest(string fullUrl, string authorizationHeaderParams, string method)
        {
            var hwr = (HttpWebRequest)WebRequest.Create(fullUrl);
            if (! string.IsNullOrEmpty(authorizationHeaderParams)) hwr.Headers.Add("Authorization", authorizationHeaderParams);
            hwr.Method = method;
            hwr.Timeout = 3 * 60 * 1000;
            return hwr;
        }
    }

I have a function to build the HttpWebRequest and then I pass that request to function which makes the request and gets the HttpWebResponse.

Mapping the XML Response

I could have serialized the XML to an object using the XmlSerializer, but since I was only needing a few of the fields from a rather large XML response, I decided to just map the XML to a CLR object manually using LINQ to XML.

var view = new GoodReadsUserShowViewModel {Updates = new List<GoodReadsUpdateViewModel>()};
XDocument doc = XDocument.Parse(xml);

 

Creating the MVC Partial View

Once I have my object built, I then pass it back to the MVC controller which will then intern pass it back to a partial view.

        [OutputCache(Duration = 60000, VaryByParam = "*")]
        public ActionResult Index(string id)
        {
            try
            {
                GoodReadsUserShowViewModel result = GetGoodReadsUserShowViewModel(id);
                return PartialView("_Goodreads", result);
            }
            catch(Exception ex)
            {
                ErrorSignal.FromCurrentContext().Raise(ex);
                return PartialView("_Goodreads"new GoodReadsUserShowViewModel {ErrorMessage = ex.Message});
            }
        }

Things to note up until this point. Since I know that this feed is not going to change much (I mean I can only read books so fast), I am caching the response for a fairly long time. In this case, performance is more important than timeliness because I don't care that you see the latest status the second after I update it.

Also, I could have passed back a JsonResult, but in this case, Backbone.js actually recommends that you have your data already bootstrapped into the request. Therefore, on my Goodreads partial view, I am taking the view model and passing it to a helper class that will serialize my model JSON and put it in a JavaScript variable.

<script src="@Url.Content("~/js/goodreads.js")" type="text/javascript"> </script>
<script type="text/javascript">
    var grApp = new GoodreadsApp(@Model.ToJson());
    grApp.start();
</script>

 

The helper class ToJson looks like this.

using System.Web.Mvc;
using Newtonsoft.Json;
 
namespace AviBlog.Core.Helpers
{
    public static class JavaScriptSerializerHelper
    {
 
        public static MvcHtmlString ToJson(this object model)
        {
            string json = JsonConvert.SerializeObject(model);
            return new MvcHtmlString(json);
        }
         
    }
}

So when the page is actually rendered the JavaScript model that is rendered will look something like this.

<script type="text/javascript">

    var grApp = new GoodreadsApp({
        "UserId":"3425042",
        "UserName":"avington",
        "Name":"Steve Moseley",
        . . . });

    grApp.start();

</script>

 

Backbone.js Implimentation

Here is what the Backbone.js website has to say about what backbone.js is.

"Backbone.js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a RESTful JSON interface."

It essentially provides a clean way to bind your front-end code to different events to provide a rich user interaction. In this case, I am using it in a very simple fashion and just binding the initial response of the Goodreads data to template and binding it using jQuery Templates. Later on I am probably going to switch out jQuery Templates for jsRender, but for now jQuery Templates it is.

Derick Baily who probably has the best blog articles on Backbone, put a response in StackOverflow as what the best way to render data on a page on the initial load and as you can see from his response and my code, I pretty much did the same thing. Here is my backjone.js code.

GoodreadsApp = (function (Backbone, _, $) {
    var GoodreadsModel = Backbone.Model.extend({});
 
    var GoodreadsCollection = Backbone.Collection.extend({
        model: GoodreadsModel
    });
 
    var GoodreadsView = Backbone.View.extend({
        el: $('.goodreads-widget'),
 
        initialize: function () {
            this.collection.bind("reset"this.render, this);
        },
 
        render: function () {
            var data = this.collection.models[0].attributes;
            if (data) {
                var $template = $('#goodreads-template');
                var goodreadsHtml = $template.tmpl(data);
                $(this.el).html(goodreadsHtml);
            }
        }
    });
 
    var goodreadsApp = function (initialModels) {
        this.start = function () {
            this.models = new GoodreadsCollection();
            this.myView = new GoodreadsView({ collection: this.models });
            this.models.reset(initialModels);
        };
    };
 
    return goodreadsApp;
})(Backbone, _, jQuery);

In this example taken from StackOverflow, when the start function is called, an empty model is initialized. The reset backbone even is called which clear out the collection of models and fills the collection with a new set (in this case taken from the serialized JSON data). The render event is hooked up to the reset function so that when it is called the template is rendered.

The jQuery template looks like this.

<script id="goodreads-template" type="text/x-jquery-tmpl">
    <h4>Goodreads Status</h4>
    <ul>
        {{each Updates}}
        <li class="clear">
            <img class="goodreads-book-img" src="${BookImageUrl}"/>
            <div><a href="${UserLink}">${Name}</a> {{html ActionText}}</div>
        </li>
        {{/each}}
        <li class="last-goodreads-item">Follow me on <a href="${UserLink}">Goodreads</a></li>
    </ul>
    
 
</script>

Conclusion

I would have put a picture of the result here, but if you take a look over here on the right. Yeah the right hand panel you can see it there for yourself. I think its a pretty cool section for only a couple hours of work. As always, you can see all the code for the application at Github.

Resources

 

May
1
2012

Creating a Simple Publishing-Subscribe Messaging App Using Node.js, Express, and Faye

Introduction

I have been playing around with Node.js for a bit. You know--the hot new kid on the programming block that everyone is talking about. I'm getting old and maybe this is just a mid-life crisis, but I like learning the cool, new stuff. JavaScript on the server! Who knew?

Why Node

Node is popular for a couple reasons. For one thing, it is JavaScript, so pretty much every web developer already knows it. The engine that executes the scripts server side is called V8. This is the very same engine that executes JavaScript scripts in your Chrome browser and it is fast. The thing that makes Node.js standout from other languages is that it is by it's JavaScript nature, non-IO blocking. There is some good information about it on Orielly, but essentially, what this means is Node.js handles it's I/O asynchronously and this makes it quite preforment.   Node.js does not tie up threads waiting on long running call-outs, instead it uses what is called an event loop to handle all of the call-outs at the same time. That's not to say that Node.js doesn't have it's issues, it does. It's still a bit immature (especially on a Windows machine) and you have to be careful with the whole asynchonous thing as it can cause some unusual results. Still, I like it. I am having fun learning it.

​Setting It Up:

First off, a quick shoutout to the guys at http://nodecasts.org/ for getting me started. The screencast was very helful. Because the age of the screencast, the code was a little bit depricated (Express was the culprit), but it didn't take much to find the new code syntax and correct the problems. I am going a little bit further in my example and making the message call via AJAX instead of in the command line via CURL.

To get Node.js up on your machine, go to http://nodejs.org and follow their installation instructions.

Once node is up and running, got http://npmjs.org/ and install the Node.js Package Manager. If your a .Net guy it is the Nuget equivalent, or the Ruby's Gem equivalent.

On my Windows machine, I've setup a directory call Node (i.e. c:\Node\ ). I then created a project folder ( C:\node\faye_sample ).

From the command line, navigate to to your project folder and run the following commands.

  • npm install express
  • npm install faye

These commands will install the Express and Faye packages on your machine that you can then reference in your code.

Express is basically a Node framework that gives you MVC styled architecture inspired by Sinatra in the Ruby world. For this example, I am only going to create a one file application and use Express for some shortcuts. There are some really good Express screencasts at NodeTuts, so if you want to go further with Node, I recomment that you give those a look at.

Faye is subscriber/publisher framework that allows any HTTP client to talk to a server real time as well many clients talk to each other real time via the server.

​The Server Code

Okay, lets take a look at the server side code. First thing I need to do is get a reference to Express and Faye.

var express = require('express'),
    faye = require('faye');

 

The next thing I need to do is initialize Faye on the server.

var bayeux = new faye.NodeAdapter({
    mount: '/faye',
    timeout: 45
});

In case you are wondering about that funny variable name. Faye uses the Bayeux protocol as its mechanism to transport the messages back and forth.

Next step, initialize the Node server using the Express wrappers.

var app = express.createServer();
app.configure(function() {
    app.use(express.bodyParser());
    app.use(express.static(__dirname + '/public'));
});

The express.bodyParser will parse the request coming in and place the data coming in into the req.body object. As you will later see, it will contain my incoming message that I can then get to rather easily.

The variable __dirname is the current directory my application is running in. the function express.static is basically telling Node that all my static content is located in the static folder inside the current application folder.

To capture the posts from the different browsers, I need to set up a route handler that will capture a post request and then broadcast that request to all of the client listeners.

app.post('/message'function(req, res) {
    bayeux.getClient().publish('/channel', { text: req.body.message });
    console.log('broadcast message:' + req.body.message);
    res.send(200);
});

The first line of code is capturing the post request from "/message" and passing in the request and response object on a callback function. The callback function then gets the message from the request and publishes it to the clients. I am also logging the message so I know that the server got the message. Because I called bodyParser when I configured my server, I now have access to the req.body.message which contains my message from the client.

The last thing I need to do is start up my node server. In this case, I am listening on port 8123.

bayeux.attach(app);
app.listen(8123);

The Client

Okay, for simplicity sake here, I am going to create two HTML files with exact same code in it and then hard code Client #1 and Client #2 in the respective files. So lets look at the client code.

In my HTML markup, I have the following elements.

                <h1>Chat Client #1</h1>
		<div id="messages"></div>
		<textarea rows="2" cols="35" id="chat"></textarea>
		<input type='button' value='Chat' id='fire' />

I need to grab a script reference to the latest jQuery, and also the Faye script on the client side.

<script src='/faye/browser/faye-browser.js'></script>

 

With the Faye reference, I can then setup a listener that will update my HTML when ever some sends out a message.

                        var client = new Faye.Client('/faye',{
				timeout: 20
			});
			
			client.subscribe('/channel'function(message) {
				$('#messages').append('<p>' + message.text + '</p>');
			});

 

Next: I am going to use jQuery to post a message when ever a user clicks on a chat button.

    		    var $chat = $('#chat');
    		    $('#fire').on('click',nullfunction() {
    		        var url = 'http://localhost:8123/message';
				
    		        var message = {message: 'Client 1: ' + $chat.val()};
    		        var dataType = 'json';
    		        $.ajax({
    		            type: 'POST',
    		            url: url,
    		            data: message,
    		            dataType: dataType,
    		        });
    		        $chat.val('');
    		    });
    		    

That's it!

I can now start up my server from the command line.

  • ​node server.js

Next, I open two browsers and start chatting.

2 browsers chatting via node server

Now, if I look at my command line server log I should see the messages because I put a line in the code to log them.

server log of faye messages

​Conclusion

I set up a GitHub repository with this code in it that you can take a look at. As I explore Node some more, I'll probably post some more posts on Node.js so stay tuned.

References

Feb
8
2012

Intro to the Bing Map API Simpler Than it Looks

Working with a map on a website, or any other application platform for that matter, may seem like a daunting task, but after working with it for a little bit, it is actually not all that difficult. The documentation for the API is a bit sparse, in my opinion, so I thought I would document what I did to get simple map functionality up and running so that others who might wanting to add a map to a webpage might find this article useful.

I am using the Bing API for this demonstration but you could use Google or Yahoo and not have to do too much different.

Get Your Key

First things first. You will need to go to the Map Account Center and create an account and obtain an application key. You can get development keys and production keys or use one key for both environments. The portal also has your usage stats, SDKs, a blog and other useful information.

The HTML

Once you have your key, your in business and can start configuring your web page to use the Bing Map API.

The only markup you need for your web page is a div tag.

 1: <div id="map_div" class="map">
 2: </div>

My style for this div looks like this.

 1: #search_main_conent .map 
 2: {
 3:     position: absolute; 
 4:     top: 20; left: 10; 
 5:     width: 400px; 
 6:     height: 400px; 
 7:     border:#555555 2px solid;
 8: }

The JavaScript

What I want to do for this particular map, as far as functionality, is load the map, and when the user clicks on the map and changes the view by moving it around or zooming in or out, is when the views finishes changing, fire an event that will populate two text boxes with the longitude and latitude.

Here is the JavaScript code to make that all work.

 1: $(document).ready(function () {
 2:     twitterMap.LoadMap();
 3: });
 4:  
 5: function twitterMap() { }
 6: twitterMap._map = null;
 7:  
 8: twitterMap.LoadMap = function () {
 9:     var $maps = $('#search_main_conent .map');
 10:     $maps.each(function (index) {
 11:         if (index === 0) {
 12:             twitterMap._map = new Microsoft.Maps.Map(
 13:                 this, {
 14:                     credentials: "Your Key",
 15:                     center: new Microsoft.Maps.Location(
 16:                         26.225948, 
 17:                         -80.188093),
 18:                     mapTypeId: Microsoft.Maps.MapTypeId.road,
 19:                     zoom: 7
 20:                 });
 21:  
 22:             Microsoft.Maps.Events.addHandler(
 23:                 twitterMap._map, 
 24:                 "viewchangeend", 
 25:                 twitterMap.onViewChangeEnd);
 26:         }
 27:     });
 28: };
 29:  
 30: twitterMap.onViewChangeEnd = function () {
 31:     var location = twitterMap._map.getCenter();
 32:     var longitude = location.longitude;
 33:     var latitude = location.latitude;
 34:     if (longitude) {
 35:         $('#search_main_conent #Longitude').val(longitude);
 36:     }
 37:     if (latitude) {
 38:         $('#search_main_conent #Latitude').val(latitude);
 39:     }
 40:     
 41:  
 42: };  
 
First off, you will notice that I was a good boy and namespaced my JavaScript code. Actually, if you check out the Nerd Dinner project, you will see where I got that inspiration from. They are using an older version of the API, but their JavaScript is very clean and easy to read. Kudos for a job well done.
 
Now back to this map. So what I am doing here is using a jQuery selector to get a reference to the div tag in the HTML shown above passing that reference into a new Microsoft Maps Map object. Notice, that here is where I pass in my key that I got from the portal.  At this point you can also customize your map so that it displays the way you want it to. In my example, I am setting the location to somewhere near where I live, and I am also setting the map type to show roads and not the satellite.
 
As I stated, I want to also capture the longitude and latitude when the view changes, so to do that I am defining an event. The event that I want to use is “viewchangedend” which fires when the map view has changed and has completed its changing.
 
My event handler then calls the map’s getCenter function to get the center coordinates of the center of the map in its current state. That functions returns a location object that contains the longitude and latitude properties that I want to use to fill the corresponding textboxes.
 
Once its completed my web page looks like this
map1
 
See, not that difficult.  When I move the map around, the longitude and latitude on the right hand side updates.
 
Hope that helps.
Oct
23
2010

Using the ASP.Net MVC & Jquery Template to repeat a HTML Fragment

I have been playing around with the new jQuery Template plugin and I’ve got to say that I am pretty impressed. I haven’t seen too many code samples so I thought I would share my approach as I had some somewhat sophisticated requirements that I haven’t really seen covered anywhere.

Approach

The repeating HTML is pretty busy so I wanted to isolate it because if it ever changes I could just publish an HTML file instead of doing a build or even republishing the view. (We use a CMS system that makes publishing content pretty easy to do.) I also wanted to reuse the fragment in different views while keeping things DRY (Don’t Repeat Yourself) at the same time. 

To do this, I created an HTML Helper that reads the HTML fragment caches it and renders inside the JavaScript tag. So this is my HTML tag:

   1: <li>
   2:     <div style='float: left;'>
   3:         <img style='border: 0px none; width: 200px;' alt='${Name}' src='${TopImagePath}'>
   4:     </div>
   5:     <div style='margin-left: 210px;'>
   6:         <div style='padding: 5px;'>
   7:             <b>
   8:                 <a href='/Activities/Activity/Detail/${Code}/'>${Activities}</a>
   9:             </b>
  10:         </div>
  11:         {{html ShortDescription}}<br>
  12:         <b>Duration:</b> ${Duration}<br>
  13:         <b>Code:</b> ${Code}<br>
  14:         <b>Level:</b> ${Level} <br>
  15:         <b>Activity Types:</b> ${Types}<br>
  16:         <b>Activity Level:</b> ${Level}<br><b>Price:</b> $<br>
  17:     </div>
  18: </li>
 

I’m going to move the styles to a CSS file, but you get the idea. This HTML is sitting in a content folder off the root of the website, and the name of the file is ActivityItem.htm.

Notice that the placeholders are designated as ${fieldName}, and if I want to encode the HTML, I could use the placeholder {{html fieldName}}.

I then render the HTML in the JavaScript tag. The JavaScript tag has an id that will referred to by jQuery Template plugin and also uses the specific type “html/text” so it does not force a syntax check from the JavaScript engine or display it prematurely in the browser.

   1: <script id="activityItemsTemplate" type="text/html">
   2:     <%= Html.RenderPath("Controls/activityItem.html") %>
   3: </script>

 

The only thing the RenderPath HTMLhelper does is get the HTML content from cache, and if it is not there; it reads the content from the HTML file, and adds back to cache, and renders it. The caching is nothing fancy. Its just the common caching technique used forever it seems like.

When the HTML Helper renders the content from cache the the JavaScript tag looks like this.

   1: <script id="activityItemsTemplate" type="text/html">
   1:  
   2:     <li>
   3:     <div style='float: left;'>
   4:         <img style='border: 0px none; width: 200px;' alt='${Name}' src='${TopImagePath}'>
   5:     </div>
   6:     <div style='margin-left: 210px;'>
   7:         <div style='padding: 5px;'>
   8:             <b>
   9:                 <a href='/Activities/Activity/Detail/${Code}/'>${Activities}</a>
  10:             </b>
  11:         </div>
  12:         {{html ShortDescription}}<br>
  13:         <b>Duration:</b> ${Duration}<br>
  14:         <b>Code:</b> ${Code}<br>
  15:         <b>Level:</b> ${Level} <br>
  16:         <b>Activity Types:</b> ${Types}<br>
  17:         <b>Activity Level:</b> ${Level}<br><b>Price:</b> $<br>
  18:     </div>
  19: </li>
</script>

 

Now all I have to do is make an AJAX call to get the JSON collection object and map it to the template.

   1: $.getJSON(url, { Prices: prices, Levels: levels, Experiences: experiences, currentPage: currentPage, pageSize: pageSize }, function (data) {
   2:  
   3:     var list = data.ActivityList;
   4:     
   5:     var markup = $("#activityItemsTemplate").html();
   6:  
   7:     $.template("activityItemsTemplate", markup);
   8:     $.tmpl("activityItemsTemplate", list).appendTo("#activityItemsContainer");
   9:     }
  10: });

 

The Container is just the ID of the UL tag.

   1: <ul id="activityItemsContainer">       
   2: </ul>
Hope that helps
Apr
3
2010

JQuery, Asp.Net MVC 2 Multi Project Areas and Other News Minutia

jQuery News

I know this nugget is a couple of weeks old now, but did anyone catch the news that Microsoft is throwing its support behind jQuery?

So it seems that if you have been using the Microsoft AJAX tools you are probably not going to be seeing much of an effort to improve those tools in the future. Not that it really matters, since it seems that most developers have abandon that toolset about a year ago, and with the emergance of MVC, they were becoming irrelevant anyway. Also, the earlier versions of the Microsoft AJax framework had performance problems under heavy load and although they have done a lot of work to make improvements, jQuery is still far and away much faster. Plus, when working with designers who are not Microsoft centric, jQuery built a bridge for the developer and designer to work together.

I have several examples on jQuery Ajax calls in case you are interested.

My first official large site using ASP.Net MVC 2

So this past month I started working on my first large MVC web application using MVC 2, and although I have been playing around with for more than a year now, I have learned a lot in the few months. The book that was really helpful was “Pro ASP.Net MVC” Framework by Steven Sanderson. I highly recommend it if you are getting started, but you might want to wait until the MVC 2 revision comes out. If you can't wait for the revision there is the eBook "Asp.Net MVC 2 In Action." This book is good in that it covers some more advanced topics like how to keep you controllers light. I think the two books together will get you well on the way to developing in ASP.Net MVC.

So far the only hang up I had was that I had set up my solution to incorporate multi project areas which was supported in the MVC 2 preview releases of Areas. However, when the RTM came out it was no longer supported. I searched and searched for solutions to my dilemma, but the only thing I could find was post by Jonathon who basically had the same experience I had, and a reference to an obscure message on a message board saying (by what appeared to be some one from the ASP Team) that it was not supported. To date, I haven't found any more formal post or article saying that was not the case.

I looked at trying the MvcContrib’s portable area alternative, and it’s a pretty nice approach, but I would like my views deployed to production as separate files and not as a resource file in a assembly, so I decided not go that route. I think for now, I am just going to include the areas in a single project and when the next version of MVC comes out—pull the different areas out into different projects then—if that is, they will have that feature in future releases.

TFS license for VS 2008

I heard this week, not from someone official, that once Visual Studio 2010 is released, they will also include Team Foundation Server for Visual Studio 2008 as a part of the MSDN license. That good news for me as I have been using the trial version for the past few weeks. I suppose the cool tool now is GIT so maybe I should take a look at that as well. At least for my VS 2010 stuff. My only thing is I am getting kind of tired of having to learn a new source control repository every year, there are other cooler things I could be learning, but I guess that is part of the job.

Mar
11
2010

Filtering Dropdowns in a ASP.Net MVC App using jQuery and jSon.

I have often come across the need to load a dropdown list based on the selection of another drop down list on a form.  In web forms it is really easy, especially if you were just doing a regular page post back.  You simply set the dropdown’s AutoPostBack property to true and then on the change event load the second dropdown based on the value of the first event.

Then along came the Ajax Toolkit that had Ajax server controls that would basically do that for you pretty simply without a full page postback.  You could see a demo of that functionality here.

How to do this with MVC

Now in MVC all the cool server controls are gone but with just a little extra work we can get the same functionality pretty easily.  Moreover, using the Json protocol makes the population of the second drop down much lighter accross the wire.

In my example, I have a pretend scenario where the user selects an event from the first drop down and based on the value of the first selection, the options of the second is dropdown is populated.

Here is the view code I am using for this example. (Note: I am using the Sparks View Engine for the View Framework instead of the Microsoft MVC View Framework because it makes the mark up much cleaner.

   1: <viewdata EventList="SelectList" />
   2:  
   3:  
   4: <content name="head">
   5:     <script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.3.2.min-vsdoc.js" type="text/javascript"></script>
   6:     <script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.3.2.min.js" type="text/javascript"></script>
   7: </content>
   8:  
   9:  
  10:  
  11:  
  12: #using (Html.BeginForm())
  13: #        {
  14:           
  15:  <div>
  16:             <fieldset>
  17:                 <legend>Event Information</legend>
  18:                 <p>
  19:                     <label for="eventname">Event name:</label>
  20:                     ${Html.DropDownList("eventname", EventList)}
  21:                 </p>
  22:                 <p>
  23:                     <label for="eventdate">Event Date:</label>
  24:                     <select id="eventdate">
  25:                         <option value="">Select Date:</option>
  26:                     </select>
  27:                 </p>
  28:                 <p>
  29:                     <input type="submit" value="See Event Info" />
  30:                 </p>
  31:             </fieldset>
  32:         </div>         
  33:           
  34:           
  35: #}

 

If you are wondering why my second selection list is not generated by the HtmlHelper class it is only because I was not able to append to it using jQuery.  Since it really wasn’t needed anyway I just decided not to bother.

The name of my view is called Events and it resides in my Home controller and the code for populates that view looks like this.

   1: public ActionResult Events()
   2:         {
   3:             IList<EventDto> events = new List<EventDto>();
   4:             const int upper = 5;
   5:             for (int i = 0; i < upper; i++)
   6:             {
   7:                 var eventItem = new EventDto() {EventCode = i.ToString(), Eventname = string.Format("Name {0}", i)};
   8:                 events.Add(eventItem);
   9:             }
  10:  
  11:             var eventList = new SelectList(events, "EventCode", "EventName");
  12:             ViewData["EventList"] = eventList;
  13:             return View();
  14:         }

 

Nothing special here.  I am just creating a list of events and then passing it to a SelectList so it can be populated in the view.

So when the page loads the HTML looks like this:

   1:  
   2:                 <legend>Event Information</legend>
   3:                 <p>
   4:                     <label for="eventname">Event name:</label>
   5:                     <select id="eventname" name="eventname"><option value="0">Name 0</option>
   6: <option value="1">Name 1</option>
   7: <option value="2">Name 2</option>
   8: <option value="3">Name 3</option>
   9: <option value="4">Name 4</option>
  10: </select>
  11:                 </p>
  12:                 <p>
  13:                     <label for="eventdate">Event Date:</label>
  14:                     <select id="eventdate">
  15:                         <option>Select Date:</option>
  16:                     </select>
  17:                 </p>
  18:                 <p>
  19:                     <input value="See Event Info" type="submit">
  20:                 </p>
  21:             

And the page looks like this:

page1

The JsonResult Function

ASP.Net MVC comes with different controller actions, one of them being the JsonResult action.  Like the ActionResult, the JsonResult sends data back to the view but in this case, the response is converted into JSON.

   1: [AcceptVerbs(HttpVerbs.Get)]
   2:         public JsonResult EventDates(string eventCode)
   3:         {
   4:             IList<EventDateDto> eventDates = BuildEventDates(eventCode);
   5:             return Json(eventDates);
   6:         }

In writing the function, first I have to tell it action to respond do.  In this case I want it to respond to “GET” requests, so I need to add the AcceptVerbs attribute to the function with the “GET” verb stated.  The function needs to have the parameter for the event code that the user selected in the dropdown, and then based on the parameter the application gets a list of dates.  These collection dates (are formatted as strings in this case) are then passed back by calling the Json() function.

 

The jQuery function:

So in order to get the data for the second dropdown we need to make an AJAX call to the server and pass it the value of the first dropdown when its “change” event is fired.

When the event is fired, we can then use the jQuery $.AJAX call to make a “GET” request to the URL as such:  …/Home/EventDates?eventCode=3.  When you look at the header of the request you also notice that “Accept” attribute is set to “application/json, text/javascript, */*”.

   1: <script type="text/javascript" charset="utf-8">
   2:         $(function(){
   3:           $("select#eventname").change(function(){
   4:                 var data = $(this).val();
   5:                 var json = {eventCode: data};
   6:                 
   7:                 
   8:                 $.ajax({
   9:                   type: "GET",
  10:                   url: "/Home/EventDates",
  11:                   data: json,
  12:                   dataType: "json",
  13:                   error: function(xhr, status, error) {
  14:                     alert("error routine");
  15:                   },
  16:                   success: function(res){
  17:                     var $dropdown = $("select#eventdate");
  18:                     $dropdown.find('option').remove().end();
  19:                     $dropdown.append('<option value="">Select Date</option>');
  20:                     for (var i = 0; i < res.length; i++) {
  21:                         $("select#eventdate").append('<option value="' + res[i].EventDateName + '">' + res[i].EventDateName + '</option>');
  22:                       }
  23:                       
  24:                   }
  25:                 });
  26:           });
  27:         })
  28:         </script>

Notice that the data we are passing must in the request must be in a jSon format.  So in the case the data we are passing is {eventCode:3}.  The response we get back is:

[{"EventDateCode":"10/27/2009","EventDateName":"10/27/2009"},{"EventDateCode":"10/30/2009","EventDateName":"10/30/2009"},
{"EventDateCode":"11/2/2009","EventDateName":"11/2/2009"},{"EventDateCode":"11/5/2009","EventDateName":"11/5/2009"},
{"EventDateCode":"11/8/2009","EventDateName":"11/8/2009"},{"EventDateCode":"11/11/2009","EventDateName":"11/11/2009"},
{"EventDateCode":"11/14/2009","EventDateName":"11/14/2009"},{"EventDateCode":"11/17/2009","EventDateName":"11/17/2009"},
{"EventDateCode":"11/20/2009","EventDateName":"11/20/2009"}]

 

If the response is successful, the callback function takes the collection that is returned and build a string of <option> tags and append it to the select object.

page1after

Update:

In the MVC 2 version the JsonResult response will by default throw an exception. This is because of a subtle vulnerability in which someone could gain access to sensitive information. You can get the details from Phil Haack's post. So the moral of the story is if you passing sensitive imformation in the response, it is probably better for you to make a POST rather than doing GET like I did in this example. If you are not passing sensitive information and you still would to use a GET request then you will need to change the JSon method from:

return JSon(eventDates);

to

return JSon(eventDates, JSonRequestBehavior.AllowGet);

Feb
24
2010

Build a REST Web Application with WCF and jQuery

Introduction

So at my work, we started on a new web application, and mainly because a lot of guys working on it were going to be new and also because it is somewhat a visible project; we figured we would keep the risk low and use the traditional web forms ASP.Net application.

On the other hand we still wanted the separation of concerns you get from a MVC style application, and because we were also planming on using designers for the web pages who were very proficient with jQuery but who did not know much about ASP.net. As a matter of fact, most of these guys write jQuery and HTML with a simple text editor…show offs :)

Also, for performance reasons and the fact the web designers were very familiar with the protocol, we wanted to pass JSON objects back and forth from the client to the server and vice versa.

Another goal is we want to keep as little as code as possible in the web project and place it in a different “Core” assembly. This would make deployments for us easier.

The Solution Layout

The solution looks like this:

The EventList.aspx page will be the page I will use for this demo. I deleted the associated code-behind files with and also removed the references of the code-behind from the page deplaration.

When I added the EventService.svc WCF Service into the project, Visual Studio added the EventService.svc.cs file and the IEventService service contract interface. I moved them to the Core project and changed the namespaces.

I also added a folder for the Unity service factory build up. You can see how I did it in a previous post.

The Service

Since I moved the associated files that were added when I added the WCF Service, I have to go in and change the declarations so they point to the proper place.

   1: <%@ ServiceHost 
   2:     Language="C#" 
   3:     Debug="true" 
   4:     Service="RestSample.Core.ServiceContracts.EventsService" 
   5:     Factory="RestSample.Core.ServiceContrainer.UnityServiceHostFactory" 
   6: %>

 

Also the code behind reference was moved and Factory attribute was added to point to my custom Unity Service Host Factory.

The Service Contract

The Service contract needs to be decorated with the ServiceContract attrubute; but I also need to specify that this message will take “POST” requests, and that the protocal will be JSON.

   1: using System.ServiceModel;
   2: using System.ServiceModel.Web;
   3: using RestSample.Core.DataContracts;
   4:  
   5: namespace RestSample.Core.ServiceContracts
   6: {
   7:     [ServiceContract]
   8:     public interface IEventsService
   9:     {
  10:         [OperationContract]
  11:         [WebInvoke(Method = "POST",
  12:             ResponseFormat = WebMessageFormat.Json,
  13:             BodyStyle = WebMessageBodyStyle.Bare)]
  14:         EventList GetLatestEvents();
  15:     }
  16: }

 

The implantation of this contract is injected with my controller where my response will be built up.

Here is the service contract implementation:

   1: using RestSample.Core.Controllers;
   2: using RestSample.Core.DataContracts;
   3:  
   4: namespace RestSample.Core.ServiceContracts
   5: {
   6:     public class EventsService : IEventsService
   7:     {
   8:         private readonly IEventController _eventController;
   9:  
  10:         public EventsService(IEventController eventController)
  11:         {
  12:             _eventController = eventController;
  13:         }
  14:  
  15:         #region IEventsService Members
  16:  
  17:         public EventList GetLatestEvents()
  18:         {
  19:             return _eventController.GetLatestEvents();
  20:         }
  21:  
  22:         #endregion
  23:     }
  24: }

 

Here is the controller class implementation:

   1: using System;
   2: using System.Collections.Generic;
   3: using RestSample.Core.DataContracts;
   4:  
   5: namespace RestSample.Core.Controllers
   6: {
   7:     public class EventController : IEventController
   8:     {
   9:         #region IEventController Members
  10:  
  11:         public EventList GetLatestEvents()
  12:         {
  13:             var list = new EventList {Items = new List<EventItem>()};
  14:             const int upper = 5;
  15:             for (int i = 0; i < upper; i++)
  16:             {
  17:                 list.Items.Add(new EventItem
  18:                                    {
  19:                                        EventCode = i.ToString(),
  20:                                        EventDate = DateTime.Today.AddDays(-1*i).ToLongDateString(),
  21:                                        EventName = string.Format("Name {0}", i)
  22:                                    });
  23:             }
  24:  
  25:             return list;
  26:         }
  27:  
  28:         #endregion
  29:     }
  30: }

Obviously I am just returning back trash, but you get the point. Here are the data contracts:

   1: using System.Runtime.Serialization;
   2:  
   3: namespace RestSample.Core.DataContracts
   4: {
   5:     [DataContract]
   6:     public class EventItem
   7:     {
   8:         [DataMember]
   9:         public string EventCode { get; set; }
  10:         [DataMember]
  11:         public string EventName { get; set; }
  12:         [DataMember]
  13:         public string EventDate { get; set; }
  14:     }
  15: }

 

   1: using System.Collections.Generic;
   2: using System.Runtime.Serialization;
   3:  
   4: namespace RestSample.Core.DataContracts
   5: {
   6:     [DataContract]
   7:     public class EventList
   8:     {
   9:         [DataMember]
  10:         public IList<EventItem> Items { get; set; }
  11:     }
  12: }

 

The web.config Set Up.

Because I want to return JSON, and because I moved the service files; I need to make some changes to the System.ServiceModel section of the web.config.

   1: <system.serviceModel>
   2:     <behaviors>
   3:         <serviceBehaviors>
   4:             <behavior name="RestSample.Core.ServiceContracts.EventsServiceBehavior">
   5:                 <serviceMetadata httpGetEnabled="true"/>
   6:                 <serviceDebug includeExceptionDetailInFaults="false"/>
   7:             </behavior>
   8:         </serviceBehaviors>
   9:   <endpointBehaviors>
  10:     <behavior name="WebHttpBehavior">
  11:       <webHttp />
  12:     </behavior>
  13:   </endpointBehaviors>
  14:     </behaviors>
  15:     <services>
  16:         <service behaviorConfiguration="RestSample.Core.ServiceContracts.EventsServiceBehavior" name="RestSample.Core.ServiceContracts.EventsService">
  17:             <endpoint address="" binding="webHttpBinding" contract="RestSample.Core.ServiceContracts.IEventsService" behaviorConfiguration="WebHttpBehavior">
  18:                 <identity>
  19:                     <dns value="localhost"/>
  20:                 </identity>
  21:             </endpoint>
  22:             <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
  23:         </service>
  24:     </services>
  25: </system.serviceModel>

I added an Endpoint Behavior and specified webHttp and then referenced it in the endpoint. This enables the “web programming model” for WCF which makes the service RESTful.

I also changed the name and contract location of the service so it points to new location in my solution.

Now my service is ready and if I run the service file at this point I get the standard service page to come up to show the everything is configured properly.

The Client

Now that the service is working I am just going to write some jQuery to make a call to the service when a button is click.

Here is the aspx page.

   1: <%@ Page Title="" Language="C#" MasterPageFile="~/Shared/Main.Master" %>
   2: <asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
   3:     <script src="../Scripts/json2.js" type="text/javascript"></script>
   4:     <script src="../Scripts/restsample.js" type="text/javascript"></script>
   5: </asp:Content>
   6: <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
   7:     
   8:     <input type="button" value="Make Call" id="restCall" />
   9: </asp:Content>

Here is the jQuery

   1:  
   2: $(document).ready(function() {
   3:     setInputFunction();
   4: });
   5:  
   6: function setInputFunction() {
   7:     $('input').click(function() {
   8:         makeEventCall();
   9:     });
  10: }
  11:  
  12: function makeEventCall() {
  13:     $.ajax({
  14:         url: 'http://localhost:56296/Services/EventsService.svc/GetLatestEvents',
  15:         type: "POST",
  16:         processData: false,
  17:         contentType: "application/json",
  18:         timeout: 10000,
  19:         dataType: "text",  // not "json" we'll parse
  20:         success: function(data) {
  21:             loadSuccessful(data);
  22:         }
  23:     });
  24: }
  25:  
  26: function loadSuccessful(data) {
  27:     var result = JSON.parse(data);
  28:     console.log('result', result);
  29: }
  30:  
  31:  

So the jQuery code above makes a call to the service when the button is clicked. Here are some things to notice about this jQuery code.

  • First off, you cannot see it here but I have a script reference to the jQuery 1.4.1 file located on the Microsoft CDN. More on that here.
  • The url is the a combination of the service url and the function GetLatestEvents.
  • I need to specify “POST” as the type because that is what I specified on my ServiceContract.
  • I am going to return back text and then parse it using Douglas Crockford's json2.js.
  • For this demo, I am just going to log the response to the Firebug console using the console.log feature. (Note: remember to take this out later because you will get a JavaScript error in other browsers).

So with I am done and now when I bring up that web page and click the button I get this JSON response back.

{"Items":[{"EventCode":"0","EventDate":"Saturday, February 20, 2010","EventName":"Name 0"},
{"EventCode":"1","EventDate":"Friday, February 19, 2010","EventName":"Name 1"},
{"EventCode":"2","EventDate":"Thursday, February 18, 2010","EventName":"Name 2"},
{"EventCode":"3","EventDate":"Wednesday, February 17, 2010","EventName":"Name 3"},
{"EventCode":"4","EventDate":"Tuesday, February 16, 2010","EventName":"Name 4"}]}

The response in Firebug looks like this:

response

 

Hope that helps :)

Dec
27
2009

Building an List of Links Asynchronously Using jQuery, AJAX, PURE, and Spark View Engine for MVC

Yes, I know the title is a mouthful, but its all the cool stuff I used to make this trick work.  I have mentioned before that I have been using the Spark View Engine with ASP.Net MVC just because I think it is much cleaner markup when it comes to intermingling HTML with C#.  I also mentioned that I have been working on my blog app and like my own home blog page, I want to have a blog roll.  So what I did was create a table with two records containing information about my two favorite blogs.

blog_table

Now what I want to do is on the Master Page that I am using is have to these two links display on the page asynchronously after the base HTML loads.

The Spark Master Page and Partial Rendering

There is pretty good documentation for Spark for getting started, so I won’t go into detail about it here; but there are a few things I wanted to mention.  The default way to use a Master Page in the Spark View Engine is to create a folder named “Layouts” inside the “View” folder and then to add a file named “Application.spark” in that folder.  This is the convention that will allow all other views to access that Master Page unless otherwise specified.

   1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
   2: <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
   3:   <head>
   4:     <title>${H(Title)}</title>
   5:     <link rel="stylesheet" href="~/Content/Site.css" type="text/css" />
   6:     <use content="head"/>
   7:      <script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.3.2.min-vsdoc.js" type="text/javascript"></script>
   8:     <script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.3.2.min.js" type="text/javascript"></script>
   9:     <script src="/Scripts/pure.js" type="text/javascript"></script>
  10:   </head>
  11:   <body>
  12:     <div id="wrapper">
  13:         <div id="header">
  14:             <div id="logo">
  15:                 <h1>Website Logo Goes Here</h1>
  16:             </div>
  17:             <div id="search">
  18:                 Search goes here
  19:             </div>
  20:         </div>
  21:         <div id="header_menu">
  22:             <ul id="menu">
  23:                 <li>
  24:                     <a title="" accesskey="1" href="#">Home</a>
  25:                 </li>
  26:                 <li>
  27:                     <span>|</span>
  28:                 </li>
  29:                 <li>
  30:                     <a title="" accesskey="2" href="#">Blog</a>
  31:                 </li>
  32:                 <li>
  33:                     <span>|</span>
  34:                 </li>
  35:                 <li>
  36:                     <a title="" accesskey="3" href="#">About Me</a>
  37:                 </li>
  38:                 <li>
  39:                     <span>|</span>
  40:                 </li>
  41:                 <li>
  42:                     <a title="" accesskey="4" href="#">Contact</a>
  43:                 </li>
  44:             </ul>
  45:             <div id="menu_spacer">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
  46:             </div>
  47:         </div>
  48:         <div id="content">
  49:             <div id="colOne">
  50:                 <div class="box">
  51:                      
  52:                     <myLinks />
  53:                 </div>
  54:             </div>
  55:             <div id="colTwo">
  56:                 <use content="view"/>
  57:             </div>
  58:         </div>
  59:         
  60:     
  61:   </body>
  62: </html>

Some things to notice on the master page here, is that I am accessing jQuery, and PURE.  Of course, jQuery is the JavaScript framework that makes almost everything JavaScript related really easy.  The other script tag for PURE is also a JavaScript framework but it is mainly to make the mapping of markup to your JSON response much easier.  We use it a lot where I work, so I have become a big fan of it.  I will demonstrate that later.

Also another thing to notice in this code is the tag <myLinks />.  This is how Spark does partial HTML rendering.  The convention for partial rendering in Spark is to create a file in the “Views/Shared” folder that is prefixed with “_” and has the extension “spark” like all the other views.  So for my example above, I have a file located at “<project_location>/View/Shared/_myLinks.spark”.

 

 

 

 

 

 

 

 

 

The Controller Class

Nothing special here. I have a controller class that returns a JSON response.  The only thing I should mention though is in order for PURE to work with a list of records you will need to rap your List object in another object.  Also, as mentioned in a previous post, I need to return a JsonResult to get the properly formatted response.  Since these links won’t change much (I am not that fickle) I am caching this action using the OutputCache attribute.

   1: using System.Collections.Generic;
   2: using System.Web.Mvc;
   3: using Aviblog.Core.Dto;
   4: using Aviblog.Core.Services;
   5:  
   6: namespace Aviblog.Web.Controllers
   7: {
   8:     public class LinksController : Controller
   9:     {
  10:         private readonly ILinksService _linksService;
  11:  
  12:         public LinksController(ILinksService linksService)
  13:         {
  14:             _linksService = linksService;
  15:         }
  16:  
  17:         [AcceptVerbs(HttpVerbs.Get)]
  18:         [OutputCache(Duration = 60, VaryByParam = "None")]
  19:         public JsonResult All()
  20:         {
  21:             Links links;
  22:             IList<LinkDto> result = _linksService.GetActiveLinks();
  23:             links = result != null ? new Links() {LinkList = result} : null;
  24:             return Json(links);
  25:         }
  26:     }
  27: }

My Links class looks like this:

   1: using System.Collections.Generic;
   2:  
   3: namespace Aviblog.Core.Dto
   4: {
   5:     public class Links
   6:     {
   7:         public IList<LinkDto> LinkList { get; set; }
   8:     }
   9: }

This will generate a JSON response as follows:

{"LinkList":
[{"LinkId":1,"Title":"Scott Gunthie","Description":"Scott Gunthrie\u0027s Blog","BlogUri":http://weblogs.asp.net/scuttgu/,
"FeedUri":null,"IsActive":true},{"LinkId":2,"Title":"Scott Hanselman",
"Description":"Scott Hanselman\u0027s Blog","BlogUri":"http://www.hansleman.com/","FeedUri":null,"IsActive":true}]}

Notice, that I now have my wrapper class called LinkList that I can use to tell PURE that this is my collection.

 

The Partial HTML that Loads the Links

So in my _myLinks.spark file I have the JavaScript that is going to make an AJAX call to get the links, and the PURE code that will map the response back to the unordered list.

   1: <script type="text/javascript">
   1:  
   2:     $(document).ready(function() {
   3:         $.ajax({
   4:             type: "GET",
   5:             url: "/Links/All",
   6:             dataType: "json",
   7:             success: function(res) {
   8:                 var $blogRollList = $("ul#blogRollList");
   9:                 
  10:                 var directive = {
  11:                     'li':{
  12:                         'link<-LinkList':{
  13:                             'a':'link.Title',
  14:                             'a@href':'link.BlogUri'
  15:                         }
  16:                     }
  17:                 };
  18:                 
  19:                  $blogRollList.render(res, directive);
  20:  
  21:             }
  22:         });
  23:     });
</script>
   2:  
   3:         
   4: <h3>Bloggroll</h3>
   5: <div id="blogRoll">
   6:     <ul id="blogRollList">
   7:         <li><a></a></li>
   8:     </ul>
   9: </div>

I am using the $.Ajax function to make a call out to my controller class “LinksController” and the action “All”.  If the code returns a successful response, the PURE code maps the response to the list.

The $blogRollList variable is the ul tag that I want my response to be loaded into.

The directive variable is how I tell PURE to map my response.  It is saying for each item in the response, create a list tag.  The “link<-LinkList” is saying for the collection LinkList there will be items named “link”.  Inside that declaration I am then using each “link” item that was defined and them mapping it to an anchor tag.

 

 

 

The Result

So now when the page loads I get the result:

blogroll