Sunday, July 15, 2012

Parsing A Complex List With JQuery / JSON

So I've been playing around with learning JQuery lately, and while I found some "Hello World" types of examples, I was really looking to jump right in and bind to a WCF service that returned a complex list of objects, particularly a list within a list, since if you can handle that, you can handle just about anything.

The common data structure I'm going to use for this example is a list of movies that contains a list of cast members.  So the first thing we need is a wcf service with some classes in it to represent this structure:

   [DataContract]  
   public class Movie  
   {  
     [DataMember]  
     public string Title { get; set; }  
     [DataMember]  
     public string Director { get; set; }  
     [DataMember]  
     public List<Actor> Cast { get; set; }   
   }  


   [DataContract]  
   public class Actor  
   {  
     [DataMember]  
     public string FirstName { get; set; }  
     [DataMember]  
     public string LastName { get; set; }  
   }  


   [DataContract]  
   public class MovieList  
   {  
     [DataMember]  
     public IList<Movie> Movies { get; set; }  
   }  

Basically what we'll have here is a service that returns a MovieList object.  In real life, we'd add things like success/fail and error messages to the MovieList class, but I'm keeping the clutter down.  For our service itself we'll expose an interface contract and just in-line a couple movies for sample purposes:


   [ServiceContract]  
   public interface IMovieProvider  
   {  
     [OperationContract]  
     [WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json)]  
     MovieList GetMovies();  
   }  


   [AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)]   
   public class MovieProvider : IMovieProvider  
   {  
     public MovieList GetMovies()  
     {  
       MovieList list = new MovieList();  
       IList<Movie> movies = new List<Movie>();  
       var movie = new Movie  
                {  
                  Title = "Spaceballs",  
                  Director = "Mel Brooks",  
                  Cast = new List<Actor> {new Actor() {FirstName = "John", LastName = "Candy"},  
                  new Actor() {FirstName = "Mel", LastName = "Brooks"},  
                  new Actor() {FirstName = "Bill", LastName = "Pullman"},  
                  new Actor() {FirstName = "Rick", LastName = "Moranis"}}  
                };  
       movies.Add(movie);  
       movies.Add(new Movie  
                {  
                  Title = "CaddyShack",  
                  Director = "Harold Ramus",  
                  Cast = new List<Actor> {new Actor() {FirstName = "Bill", LastName = "Murray"},  
                  new Actor() {FirstName = "Chevy", LastName = "Chase"}}  
                });  
       list.Movies = movies;  
       return list;  
     }  
   }  

Pretty simple stuff so far, now we have a service, and it will expose a JSON representation of this data when we call it.  By using a tool like firebug, we can see that the JSON emitted is going to look like so:


 {"Movies":
[{"Cast":[{"FirstName":"John","LastName":"Candy"},
{"FirstName":"Mel","LastName":"Brooks"},
{"FirstName":"Bill","LastName":"Pullman"},
{"FirstName":"Rick","LastName":"Moranis"}],
"Director":"Mel Brooks","Title":"Spaceballs"},
{"Cast":[{"FirstName":"Bill","LastName":"Murray"},
{"FirstName":"Chevy","LastName":"Chase"}],
"Director":"Harold Ramus","Title":"CaddyShack"}]}  

So now this service is all ready to publish.  Now all we have to do is create a page that contains some JQuery to call it, and then parse the JSON object in a loop to build and emit some html.  The power of this approach really shines in being able to explicitly define your html including any classes, div structure, etc.  It gives us similar flexibility to using a repeater.  For the html page, we'll just drop a simple button on the form and define a div to hold the results of our query like so:


   <h1>My Movies</h1>  
   <div>  
     <input id="btnGetMovies" type="button" value="Get Movies" />  
   </div>  
   <div id="movieList">  
   </div>  

In order to call the service, we're going to use the $.ajax method in jquery.  It has several properties including the url to call, formatting, and I will specify a success and failure method.  We're going to assign the click of the button when the document is ready to make the ajax call.  The JQuery script ends up looking like so:


     $(document).ready(function () {  
       $("#btnGetMovies").click(function(event) {  
         $.ajax({  
           type: "GET",  
           contentType: "application/json; charset=utf-8",  
           url: "http://localhost:81/MovieService/MovieProvider.svc/GetMovies",  
           data: "{}",  
           processdata: true,  
           dataType: "json",  
           success: function(msg) {  
             AjaxSucceeded(msg);  
           },  
           error: AjaxFailed  
         });  
       });  
     });  

The msg variable will contain the json object, and we'll declare a javascript function called AjaxSucceeded to parse it.  Now if you're a good little web developer, we'll try to avoid using tables to grant some flexibility for our designers.  I am definitely not a designer, but I'm going to emit the json into a header tag that contains the director and movie title, then we'll put another heading tag for the cast, and put the cast into an ordered list.  By wrapping this information into a div, we can assign a class to the div and give any future designer some flexibility over styling these objects.  As a general template, we want it to look something like this:


 <div id="movieList">  
   <h2>Spaceballs (Mel Brooks)</h2>  
   <div class="castList">  
    <h3>Cast</h3>  
    <ol>  
      <li>John Candy</li>  
      <li>Mel Brooks</li>  
      <li>Bill Pullman</li>  
      <li>Rick Moranis</li>  
    </ol>  
   </div>  
   <h2>CaddyShack (Harold Ramus)</h2>  
   <div class="castList">  
    <h3>Cast</h3>  
    <ol>  
      <li>Bill Murray</li>  
      <li>Chevy Chase</li>  
    </ol>  
   </div>  
 </div>  

To do that, we're going to take the msg in our function and do two for-loops in java script and dynamically build the html output we want.  Then at the end we'll use the JQuery selector to locate the movieList div and assign its content to the output we created.  Here's the code to do that:


     function AjaxSucceeded(result) {  
       var list;  
       list = "";  
       $.each(result.Movies, function(i, movie) {  
         list += '<h2>' + movie.Title + ' (' + movie.Director + ')</h2>';  
         list += '<div class="castList"><h3>Cast</h3><ol>';  
         $.each(movie.Cast, function(j, actor) {  
           list += '<li>' + actor.FirstName + ' ' + actor.LastName + '</li>';  
         });  
         list += '</ol></div>';  
       });  
       $('#movieList').html(list);  
     }  

Last thing to do is to slap some CSS on it using these classes and child selectors, I'm going to just toss an underline bar on the header and put a dotted line border around the cast, professional designers can mock me, I have thick skin.


 #movieList h2 { font-family: Arial;font-size: 14pt;font-weight: bold;border-bottom: 1px solid #000;}  
 .castList h3 { font-family: Arial;font-size: 12pt;font-weight: bold;margin: 0;padding: 0;}  
 .castList ol { list-style-type: none;margin: 0;padding: 0;border: 1px dashed #000; }  

And you're all set!  You have a working service that emits a list of objects that contain lists as properties.  It executes quick, it's ajax enabled, and you have total control over the mark-up.

Feel free to grab the code source from my public github repository.  I've called the project JQueryPlayground, and future blog posts on JQuery and ASP.NET will expand this project.  Enjoy!

No comments:

Post a Comment