Building a little menu with a some JSON, LINQ and the JQuery Template Plugin

This is a little tutorial I thought I´d write about a little problem I recently solved with a couple of different technologies that worked very well together.

Background

From a customer I got a request to build some sort of navigation (or menu) for some items, let´s for the sake of this little tutorial pretend that they (the items) are books. The issue was that the user accessing the application had access to some books but to some other books the user had no access but we still wanted to show them in the UI along with some preview-like styling (like a teaser one might say). It will look something like this when it´s finished:

Note: I have not made an effort to make it look all that pretty for the purpose of this tutorial though…

The Model



For this tutorial I have constructed a very simple book entity that has a Title property and a HasAccess property that will be a flag indicating if the user has access to the book or not.



Next is a simple service contract (the IBookService) that I will call in order to get me all the books in the repository (be it any sort of data repository).



For the purpose of this tutorial I made a mockup of a service implementation of the IBookService that will returns 20 books and every other book will be inaccessible (e.g. even/odd) like you can see in the screen shots above.

LINQ and JSON

I was thinking of exposing my data in service-like manner (be it asmx, wcf, ashx or what have you) that will be requested from the client via JQuery and exposed via JSON (e.g. it will return a JSON serialized string). I made a simple ashx http handler to act as my service for the sake of this tutorial. I was also thinking about grouping all the books that are accessible in one group and the other ones in another group so my code will look like this:

The service request (JQuery call)

                    $.ajax({
                        type: "GET",
                        dataType: "json",
                        cache: false,
                        contentType: "application/json;charset=utf-8",
                        url: 'handler.ashx',
                        data: { grouping: 'HasAccess' },
                        success: function (data) {
                            $("#tmpl-menu-items").tmpl(data).appendTo("#menu-container");
                        }
                    });

As you can see I´m passing a parameter along with my service call that tells the service to group the books by accessibility (I know that´s not the proper name for it but who cares, right?).

The service response (the http handler)

        public void ProcessRequest(HttpContext context)
        {

            string grouping = context.Request.Params["grouping"];

            context.Response.Clear();
            context.Response.ContentType = "application/json";
            JavaScriptSerializer jsSer = new JavaScriptSerializer();
            string json = string.Empty;

            var books = RequestBooks();

            switch (grouping)
            {
                case "HasAccess":
                    var query = from book in books
                                orderby book.HasAccess descending
                                group book by book.HasAccess into AccessibleBooks
                                select new
                                {
                                    SectionName = AccessibleBooks.Key ? "Your books" : "Some other books",
                                    IsAccessible = AccessibleBooks.Key,
                                    Books = from book in AccessibleBooks
                                                 select book
                                };

                    json = jsSer.Serialize(query);
                    break;
                default:
                    break;
            }

            context.Response.Write(json);
        }

I´ll start by getting all the books from my “fake” service and then grouping them into books that I have access to and books that I have no access to using a little LINQ and anonymous type magic at the end. Finally I use the JavaScriptSerializer to serialize my “new” anonymous enumerable into a JSON string that is sent as a response.

The JSON response

This is the JSON response from the service and as you can see I will get two objects in the response. The first one has all the books I have access to and the other object will holder all the other ones (e.g. IsAccessible is either true or false). Underneath each “topmost” object I will have a collection of books which corresponds to the LINQ query that I set up in the ashx handler with that anonymous type.

The JQuery Template Plugin

I have I new “baby” that have just begun to use but already I have great hopes for this plugin which has been contributed to JQuery by Microsoft. It is just super easy to use and resembles, in syntax, how we would databind in Silverlight. To use the plugin you just download the js file or use the CDN and once that is done you hook it up like this:

The HTML container

<div id="menu-container"></div>

This is just like a placeholder where the template will be rendered once it has been completed.

Instantiate the template rendering

The template is instantiated from the JQuery $ajax success function like this:

$("#tmpl-menu-items").tmpl(data).appendTo("#menu-container");

It will actually be called twice (in this example) because the JSON response holds two objects (remember?).

The template/s

Templates can then be defined in script tags with a type text/x-jquery-tmpl . I say “can” because there are a couple of ways and I just think this is the simplest one.

<script id="tmpl-menu-items" type="text/x-jquery-tmpl">
  {{tmpl "#tmpl-section-header"}}  
      
  <ul class="section-items">            
    {{tmpl(Books) "#tmpl-section-item"}}
  </ul>    

</script>

The first template the starts by calling a second template through the tmpl syntax that will render the header in a p-tag with the sections name (from the anonymous type) using the ${…} syntax.

    <script id="tmpl-section-header" type="text/x-jquery-tmpl">
        <p class="section-header">
            ${SectionName}
        </p>
    </script>

Next an ul-tag is rendered and inside that ul a third template is called but here we pass the Books array as an argument which will then work like a foreach loop on them calling the template once for each item in the Books array.

    <script id="tmpl-section-item" type="text/x-jquery-tmpl">
            <li class="book{{if HasAccess == false}} no-access{{/if}}">${Title}</li>
    </script>

Here I´m making use of the {{if …}} syntax which makes me able to render different content based on that HasAccess property.

The entire aspx/html looks like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.5.1.min.js" type="text/javascript"></script>
    <script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js" type="text/javascript"></script>

    <style type="text/css">       
        ul
        {
            list-style:none;    
        }
        
        .section-header
        {
            font-weight: bold;
            cursor: pointer;   
            background-color: Gray;
            padding: 3px; 
        }
        
        #menu-container
        {
            width: 200px;    
        }
        
        li.book
        {
            background: url(img/book.png) no-repeat center left;
            padding-left: 20px;
            margin-top: 4px;    
        }
        
        li.no-access
        {
            font-style:italic;    
        }
    </style>

</head>
<body>
    <form id="form1" runat="server">

            <script type="text/javascript">

                $(document).ready(function () {

                    $(".section-header", "#menu-container").live("click", function (e) {
                        $(this).next("ul.section-items").slideToggle("slow");
                    });

                    $.ajax({
                        type: "GET",
                        dataType: "json",
                        cache: false,
                        contentType: "application/json;charset=utf-8",
                        url: 'handler.ashx',
                        data: { grouping: 'HasAccess' },
                        success: function (data) {
                            $("#tmpl-menu-items").tmpl(data).appendTo("#menu-container");
                        }
                    });
                });

    </script>
    
    <script id="tmpl-menu-items" type="text/x-jquery-tmpl">
        {{tmpl "#tmpl-section-header"}}

        <ul class="section-items">
            {{tmpl(Books) "#tmpl-section-item"}}
        </ul>
    </script>

    <%--renders the header of each section--%>
    <script id="tmpl-section-header" type="text/x-jquery-tmpl">
        <p class="section-header">
            ${SectionName}
        </p>
    </script>

    <%--renders each item in the section--%>
    <script id="tmpl-section-item" type="text/x-jquery-tmpl">
            <li class="book{{if HasAccess == false}} no-access{{/if}}">${Title}</li>
    </script>



    <div id="menu-container">
    </div>

    </form>
</body>
</html>

Easy, right…?!!

Advertisements

, , ,

  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: