Data Driven Programming with JQuery – Part 1

Data Driven programming is centered around the idea that data is central to user interaction and that an application should work only with data on the client side.  In this matter, the application adheres strictly to the less is more principle of Ajax development, in that we send only what the server needs and we return only what the client needs.  You can think of it as a push/pull model where our client will push only what it has to, to the server and then pull only what it ABSOLUTELY needs and display the information on the client.

Because of their strict adherence to the “less is more” principle of Ajax development as well as heavily relying on the use of JavaScript Object Notation (JSON) as a transmission protocol, data driven application ends to be much lighter and perform better over traditional web applications.

To actually properly develop these types of applications a framework, such as JQuery is essential, and also this methodology makes use of a number of features of JavaScript/JQuery.  JQuery selectors, in particular, are crucial to designing a lighter applications.  By having a thorough understanding of these selectors we are able to write less code which is more concise.  To briefly review the basic JQuery selectors allow us to select elements by ID, Class, and Tag Name, like such:

$("#someId");   // returns all elements with id="someId:
$(".someClass"); // returns all elements with class="someClass"
$("span");  // returns all  on the page
$("span input"); // returns all  within 
$("span > input"); // returns all  direct children of any 

These work very well as basic selectors and really become the foundation of any application, however, when you begin to get into cases where JQuery is working on a generated HTML layout, such as with ASP .NET you quickly find yourself having to make concessions on the cleanliness and conciseness of your HTML to assist JQuery with target elements whose ID is constantly changing.  For example, when I first started programming with JQuery I tried all sorts of ways to simplify targeting an element inside multiple Naming Containers.  Now I have a solid understanding Attribute and Filter based selection like such:

$("input[maxlength]");  // returns all  with attribute maxlength
$("input[id=someId]");  // returns all  with id="someId"
$("input[id$=someId");  // returns all  whose id ends with "someId"
$("input[id^=someId"); // returns all  whose id starts with "someId"
$("input[id*=someId"); // returns all  whose id contains "someId"

This type of selection works VERY well with ASP .NET due to the fact that the NamingContainer always place the ID the developer users at the end we can make good use of the $= syntax to select these elements.  The next set of selectors are Filters, which are based heavily on CSS3 and their primary use is to “filter” the set of all HTML elements on a page to a subset based on criteria and state.  The aim with filters in general is to give CSS developers more control over how elements look and act depending on their state.  Here are a few of them:

$(":text"); // returns all elements with type="text"
$(":visible"); // returns all elements not visible
$(":radio");  // returns all elements with type="radio"
$(":checked"); // returns all elements that are checked

Using JQuery selectors, in particular those outside the realm of the basic selectors, can greatly simplify the code you have to write and amplify the ways you have to target elements.  But why is understanding this so important:

When we design software we often talking about coupling and cohesion, mostly relating to class maintainability.  The same principles apply here.  Designs will change and, in the past, this often meant that JavaScript had to change with it.  However, with advanced frameworks like JQuery, it doesn’t have to as you can know properly separate your design from your interaction and thus allow changes to made that do not, or have little impact on your interaction layer.

So now that we have covered JQuery selectors there is one final piece of technology to talk about: JSON.  In the past, many developers used XML as the primary protocol for transmitting data back to the server, and while JavaScript is very good for parsing XML (mainly because its use in parsing the DOM), it is a very heavy protocol and contains a lot of bloat that we do not need.  JSON is a much simpler protocol that utilizes the serialization of JavaScript objects and transmits a string back to JavaScript which is then evaluated into a simple JavaScript objects.  Most of the JQuery widgets utilize JSON as the means for supplying the data we will display, because of its clean format and immense flexibility.

As an example of data driven programming I am going to show how one might construct a filterable grid that permits adding of new data.  Something to notice is the lack of use for hidden fields.  We store all state data using JavaScript closure, which I will explain later.  So the first part is to understand the nature of templates, since each row in the grid will share the same HTML, so here is what the main table could look like:

<table id="dataTable">
    <thead>
         <tr>
              <th></th>
              <th>First Name</th>
              <th>Last Name</th>
              <th>Age</th>
         </tr>
    </thead>
    <tbody>
          <tr>
             <td class="rowIndex"></td>
             <td class="firstname"></td>
             <td class="lastname"></td>
             <td class="age"></td>             
          </tr>
    </tbody>
</table>

Because this is a table, to make it easier for us to target specific sections, I am using the

and

tags.  Our goal here is to take the rowTemplate HTML and store it somewhere that we can easily reuse it.  That place is the JQuery data cache, as shown below:

var $row = $("#dataTable > tbody");
if ($row.data("rowTemplate") == null) {
     $row.data("rowTemplate", $row.html());
}

Using JQuery, each element on the page can be given a data cache to store relevant data in. This data is stored in the JavaScript engine and is tracked by JQuery, you will find no reference to this data anywhere in the HTML source. When I say each element, I mean “each element”, thus you could store the original data presented in each row and read it without fear of the user modifying it with a tool such as FireBug and knowing the data is unique.  In this case I am storing the template HTML to the table because contextually it is the table body that cares about the HTML. In addition, we could be adding/removing rows, so storing the HTML on the rows themselves would not be a good idea.

In modern JavaScript it is considered bad practice to create lengthy amounts of HTML in JavaScript primarily for maintainability concerns as well as bloat and added coupling, remember we want to separate the HTML from the JavaScript as much as we can, that is why we are targeting a block of HTML and storing it with JavaScript.  This way the block can be updated without much change to

The follow code uses the ChainJS plugin to create a row based on the template for each item in the array – the template is defined by the HTML contained within the targeted block:

var $body = $("#dataTable > tbody");
$body.empty().html($body.data("rowTemplate"))
    .items( jsonResult ).chain(function() {
    var data = this.item();
});

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Note: I always precede any stored references to JQuery objects with a “$”, this is merely my convention, and is not anything official or leveraging anything special.

So what is happening here.  We make a reference to the tbody element with JQuery (this may already exist from when you checked the cache) and we call empty.  empty() will clear all the HTML from within the targeted node; we write our code this way to permit easy rebinding as we filtering or reorder the results.  Next we access the data cache for the “tbody” and place the HTML we stored earlier into the tbody, this is the beauty of this technique, we can now very easily and cleanly reuse this HTML as much as we like.

The next call starts the chaining process: we pass a variable (jsonResult) to the items functions that expects an array of JSON objects.  This prepares the set for display, and our call to chain finishes the process.  chain takes an optional anonymous function called a builder.  In many ways this builder is similar to the ItemDataBound and RowDataBound events, and they operate the same way.  Finally we make a call to item() within the builder to get a reference to the current item being bound; note also that $(this) would reference the current container (in this case

).

One of the interesting things about ChainJS is you do get some primitive automatic binding for your data based on class name; if you recall the HTML excerpt above:

<tbody>
          <tr>
             <td class="rowIndex"></td>
             <td class="firstname"></td>
             <td class="lastname"></td>
             <td class="age"></td>             
          </tr>
</tbody>

In this case, if the JSON objects in the set contained properties firstname, lastname, age then the values of these fields would be placed in the elements with the corresponding class names BEFORE the builder function is executed.  When the builder function executes it also contains the scope of variables inside and outside the function, thus the follow can be done:

var index = 0;
$row.empty().html($row.data("rowTemplate")).items(
    jsonResult ).chain(function() {

    var data = this.item();
    $(this).find(".rowIndex").text(index++);
});

In this case you see that even though index is declared outside the anonymous function we have the ability to see it and update it.

So this should give you an idea of what you can do understand the use of data centric principles as they relate to application design.  Remember, by leveraging new tools and the advanced features of modern frameworks like JQuery you can cut out working directly with HTML by utilizing templates and the JQuery data cache. Finally, you can add a layer of obfuscation to hide your codes purpose as well as the data from the transparent web.

In the next posting, well talk about how to utilize this strategy to clean-up event binding code as well as how the use of a service oriented approach and the related positives and negatives with that approach.

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Advertisements

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