jQuery and MVC–Part 3

By James at November 19, 2010 19:11
Filed Under: JavaScript, MVC, jQuery, jQuery Plugins

In the last article I talked about MVC and doing some nifty Ajax functionalities with jQuery. In this article I will go over jQuery plugins, what they are and how to create them.

jQuery Plugins

jQuery has a plugin architecture which allows you to package up commonly used functions into a plugin. Chances are if you have a complex JavaScript function, someone has more than likely written a plugin to handle that particular functionality. The jQuery.com site has an extensive plugin library and it can sometimes be fun to browse through it to see what people have come up with.

Plugins are fairly easy to write, once you have gotten the basics of jQuery down. Here is a tooltip plugin that I wrote in just a few minutes.

   1: (function ($) {
   2:     $.fn.tooltip = function (options) { // make sure to name the function
   3:                                         // the same as your tooltip
   4:         var methods = {
   5:             
   6:             //initialize stuff
   7:             init: function (options) { },
   8:             
   9:             //destroy and clean up
  10:             destroy: function () {
  11:                 return this.each(function () {
  12:                     $(window).unbind("tooltip");
  13:                 })
  14:             }
  15:         };
  16:  
  17:         // default settings if they aren't passed in
  18:         var settings = {
  19:             'backgroundColor': 'red',
  20:             'color': 'blue',
  21:             'toolTipText': 'Hi',
  22:             'fontWeight': 'bold'
  23:         };
  24:  
  25:         // add the settings into the options
  26:         return this.each(function () {
  27:             if (options) {
  28:                 $.extend(settings, options);
  29:             }
  30:  
  31:             var $this = $(this);
  32:             $this.mouseenter(function (event) {
  33:                 $this.css("color", settings.colorEnter);
  34:                 
  35:                 // if the div#toolTip isn't in the DOM, create a new one
  36:                 if (!$("#toolTip").length) {
  37:                     // create the element
  38:                     $('<div id="toolTip"></div>')
  39:                         // set the text to passed option
  40:                         .html(settings.toolTipText)
  41:                         // from the style sheet
  42:                         .addClass("toolTip")
  43:                         // insert the div into the DOM just after $this
  44:                         .insertAfter($this)
  45:                         // position the div
  46:                         .css({ "left": $this.width(),
  47:                         // set the backgroundColor
  48:                         "backgroundColor": settings.backgroundColor,
  49:                         // set the font
  50:                         "fontWeight": settings.fontWeight,
  51:                         // set the color
  52:                         "color": settings.color
  53:                     }); 
  54:                 }
  55:                 $("#toolTip").fadeIn(500);
  56:             });
  57:  
  58:             $this.mouseleave(function () {
  59:                 $this.css("color", settings.colorLeave);
  60:                 $("#toolTip").fadeOut(500);
  61:             });
  62:         });
  63:     };
  64: })(jQuery);

It’s not a pretty thing, or even that functional, but it does show off how fast you can write a plugin. Let’s take a look at how this plugin is implemented.

1. In the <head> tag of your page include the references to your jQuery and JavaScript files.

   1: <script src="Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
   2: <script src="Scripts/jquery.tooltip.js" type="text/javascript"></script>
   3: <script src="Scripts/AppScripts.js" type="text/javascript"></script>

2. Somewhere on your page add an element that will call the plugin

   1: <div class="test">This is the element that will have the assignment</div>

3. In your custom JavaScript, attach the plugin to the element and pass in any options you need to.

   1: $(document).ready(function () {
   2:     $("div.test").tooltip({ "toolTipText": "Hello from ComponentOne", 
   3:         "color":"white" })
   4:         .click(function () { alert("hi"); });
   5: });

4. Add the following to your style sheet to style up the test div.

   1: .test{width:200px;
   2:    float:left;background:silver;
   3:    color:White;border:1px solid White;
   4:    cursor:pointer;padding:5px}

5. Open the page in a browser, then hover over the test div. If all goes well, you should see the following.

 

1447.image_12F3BB96

5287.image_2A3EC007

Obviously this isn’t the world’s greatest plugin, but does cover the basics of getting started. Information on building more functional plugins can be found at http://docs.jquery.com/Plugins/Authoring

TableSorter, a more functional plugin

Since we are working with tabular data in this application, I want to use a plugin that will modify an HTML table and make it sortable. Searching through the plugins library I found “TableSorter” at http://tablesorter.com. It is written by Christian Bach at www.lovepeacenukes.com and does a great job of providing a pretty nifty data grid from a static HTML Table.

Let’s take a look.

2744.image_3AD6BAF5

In the head tag of the HTML file place a reference to the TableSorter plugin right after the reference to the jQuery file.

2474.image_05114FCE

In the View in which you want to show the table, write your code to render a table with the data you want to show. One thing about the TableSorter plugin is that it requires valid HTML tables complete with <thead> and <tbody> tags. The following markup is written with the new MVC 3 Razor View Engine.

   1: <table id="studentTable" class="studentTable {sortlist: [[6,0]]}">
   2:     // sortlist is a function of the plugin which automatically sorts
   3:     // on a specific column
   4:     <thead>
   5:     <tr>            
   6:         <th>First Name</th>
   7:         <th>Last Name</th>
   8:         <th>Email</th>
   9:         <th class="{sorter: false}">Comments</th> 
  10:         // sorter class is a plugin option
  11:         <th>Submit Date</th>
  12:         <th>Approved Date</th>
  13:         <th>Approved</th>
  14:     </tr>
  15:     </thead>
  16:     <tbody>    
  17:     @foreach (var item in Model)
  18:     {        
  19:         var approvedDate = string.Empty;
  20:         if (item.ApprovedDate.HasValue) { 
  21:             approvedDate = item.ApprovedDate.Value.ToShortDateString(); 
  22:         }
  23:           <tr>               
  24:             <td>@item.FirstName</td>
  25:             <td>@item.LastName</td>
  26:             <td>@item.Email</td>
  27:             <td>@item.Comments</td>
  28:             <td>@item.SubmitDate.ToShortDateString()</td>
  29:             <td>@approvedDate</td>
  30:             <td>
  31:               <span style="display:none" class="hiddenSpan">@item.Approved</span>
  32:               @Html.Hidden("Approved", item.Approved)
  33:               @Html.Hidden("studentId", item.Id)
  34:               @{var chk = "chkGraphic chkUnChecked";}
  35:               @if (item.Approved.HasValue && item.Approved.Value.Equals(true))
  36:               {
  37:                   chk = "chkGraphic chkChecked";
  38:               }
  39:               <div class="@chk"></div>
  40:               <img src="../../Content/Images/ajax-loader.gif" 
  41:               class="ajaxLoader ajaxLoaderSmall" />
  42:             </td>
  43:         </tr>
  44:     }    
  45:     </tbody>
  46: </table>

Since we are rendering a table, we can use the $(document).ready(function(){});

Add the following in your custom JavaScript file:

   1: /// <reference path = "/Scripts/jquery-1.4.1-vsdoc.js"/>
   2:  
   3: $(document).ready(function () {
   4:     $("#studentTable").tablesorter({ widgets: ['zebra'] });
   5:     // zebra is a “widget” included in the plugin which will alternate
   6:     // the row colors of the table
   7: });

Run the page and sort the columns. One thing to note in the markup is in the last column. The plugin sorts on the data contained in the columns of each row. Since I want to sort on “Approved”, I add a hidden field with that data as the first element in the cell.

7024.image_5BBA07CF

2133.image_5713D748

The TableSorter plugin has many more options than this brief demo show. If you want to explore it more, download it at http://tablesorter.com.

A Freebie

If you look at lines 30 - 42 in the markup you may be wondering what all the other stuff is, including the <div class=”@chk”></div>. I wanted to spiff up the form and use graphics for the checkboxes, but still keep the functionality of a checkbox. Here is how to do it.

1. In the style sheet add the following classes.

   1: .chkGraphic{cursor:pointer;width:22px;height:22px;float:left;margin-right:5px}
   2: .chkChecked{background:url(Images/checkBoxOn.png)no-repeat}
   3: .chkUnChecked{background:url(Images/checkBoxOff.png)no-repeat}

2. In your custom JavaScript file, add the following functions.

   1: $("div.chkGraphic").live("click", function () { toggleGraphicCheckBox(this); });
   2:  
   3: function toggleGraphicCheckBox(elem) {
   4:     var $elem = $(elem);
   5:     $elem.siblings("img.ajaxLoader").show();
   6:     var valueField = $elem.siblings("input[type=hidden][name=Approved]");
   7:     if ($elem.hasClass("chkChecked")) {
   8:         $elem.removeClass("chkChecked").addClass("chkUnChecked");
   9:         valueField.val("False");
  10:     } else {
  11:         $elem.removeClass("chkUnChecked").addClass("chkChecked");
  12:         valueField.val("True");
  13:     }
  14:     $("span.hiddenSpan").html(valueField.val());
  15:     toggleStudentApproval($elem);
  16: }
  17:  
  18: function toggleStudentApproval(elem) {    
  19:     var approved = elem.siblings("input[type=hidden][name=Approved]").val();
  20:     var studentId = elem.siblings("input[type=hidden][name=studentId]").val();
  21:     $.ajax({
  22:         url: "/Home/GetStudent/" + studentId + "/" + approved,
  23:         type: "GET",
  24:         success: function (result) {
  25:             showStudentApproval(result, elem);
  26:         }
  27:     });
  28: }
  29:  
  30: function showStudentApproval(result, elem) {    
  31:     var id = result.Id;
  32:     var td = elem.parent("td").prev("td");
  33:     td.html("");
  34:     if (result.ApprovedDate != null)
  35:         td.html(result.ApprovedDate);
  36:     elem.siblings("img.ajaxLoader").fadeOut(500);
  37: }

Here’s the markup again.

   30: <td>
   32:  <span style="display:none" class="hiddenSpan">@item.Approved</span>
   33:  @Html.Hidden("Approved", item.Approved)
   34:  @Html.Hidden("studentId", item.Id)
   35:  @{var chk = "chkGraphic chkUnChecked";}
   36:  @if (item.Approved.HasValue && item.Approved.Value.Equals(true))
   37:  {
   38:     chk = "chkGraphic chkChecked";
   39:  }
  40:  <div class="@chk"></div>
  41:  <img src="../../Content/Images/ajax-loader.gif" 
  42:        class="ajaxLoader ajaxLoaderSmall" />
  43: </td>

Now based on what you’ve read so far in this and the previous articles, take a look at all the code and see if you can figure out what will happen when the graphical checkbox is clicked. Add a comment to this article and let me know what you think it will do.

Just for grins and giggles, I converted the JavaScript above to a plugin, jquery.graphicCheckBox.js

   1: (function ($) {
   2:     $.fn.graphicCheckBox = function (options) {
   3:     var methods = {
   4:         // initialize stuff
   5:         init: function (options) { },
   6:         //destroy and clean up
   7:         destroy: function () {
   8:             return this.each(function () {
   9:                 $(window).undbind("graphicCheckBox");
  10:             })
  11:         }
  12:     };
  13:     // default settings if they aren't passed in
  14:     var settings = {
  15:         'checked': 'chkChecked',
  16:         'unchecked': 'chkUnChecked',
  17:         'urlToAction': '/Home/GetStudent/'
  18:     };
  19:         // add the settings into the options
  20:     return this.each(function () {
  21:         if (options) {
  22:             $.extend(settings, options);
  23:         }
  24:  
  25:         var $this = $(this);
  26:         $this.click(function (event) {
  27:             $this.siblings("img.ajaxLoader").show();
  28:             var valueField = $this.siblings("input[type=hidden][name=Approved]");
  29:             if ($this.hasClass(settings.checked)) {
  30:                 $this.removeClass(settings.checked).addClass(settings.unchecked);
  31:                 valueField.val("False");
  32:             } else {
  33:                 $this.removeClass(settings.unchecked).addClass(settings.checked);
  34:                 valueField.val("True");
  35:             }
  36:             $("span.hiddenSpan").html(valueField.val());
  37:             toggleApproval($this);
  38:         });
  39:     });
  40:     function toggleApproval(elem) {
  41:         var approved = elem.siblings("input[type=hidden][name=Approved]").val();
  42:         var id = elem.siblings("input[type=hidden][name=studentId]").val();
  43:         $.ajax({
  44:             url: settings.urlToAction + id + "/" + approved,
  45:             type: "GET",
  46:             success: function (result) {
  47:                 showApproval(result, elem);
  48:             }
  49:         });
  50:     }
  51:     function showApproval (result, elem) {
  52:         var id = result.Id;
  53:         var hid = elem.siblings("input[type=hidden][name=studentId]").val();
  54:         var td = elem.parent("td").prev("td");
  55:         td.html("");
  56:         if (result.ApprovedDate != null)
  57:             td.html(result.ApprovedDate);
  58:         elem.siblings("img.ajaxLoader").fadeOut(500);
  59:     }
  60: }
  61: })(jQuery);

In this article we talked about extending jQuery with plugins, how to write a simple plugin, and explored a fully functional table sorter plugin. I even threw in a groovy freebie for you.

Happy Programming,

James

Add comment




  Country flag
biuquote
  • Comment
  • Preview
Loading


About the author

James James is a five time and current Microsoft MVP in Client App Development, a Telerik Insider, a past Director on the INETA North America Board, a husband and dad, and has been developing software since the early days of Laser Discs and HyperCard stacks. As the Founder and President of the Inland Empire .NET User's Group, he has fondly watched it grow from a twice-a-month, early Saturday morning group of five in 2003, to a robust and rambunctious gathering of all types and sizes of .NET developers.

James loves to dig deep into the latest cutting edge technologies - sometimes with spectacular disasters - and spread the word about the latest and greatest bits, getting people excited about developing web sites and applications on the .NET platform, and using the best tools for the job. He tries to blog as often as he can, but usually gets distracted by EF, LINQ, MVC, ASP, SQL, XML, and most other types of acronyms. To keep calm James plays a mean Djembe and tries to practice his violin. You can follow him on twitter at @latringo.

And as usual, the comments, suggestions, writings and rants are my own, and really shouldn't reflect the opinions of my employer. That is, unless it really does.

James Twitter Feed

Recent Comments

Comment RSS

Month List