Thursday 26 May 2011

Navigation Panel

Navigation Panel

One of the designers on our UI team requested a screen mockup with a page layout that is similar to your typical email client.  The page is divided vertically into 2 panes.  The left pane contains a 2 level hierarchy of categories and subcategories.  As the user selects different subcategories the designer wants the right pane's content to be updated with the corresponding information.  Just like Outlook, the designer wants the subcategories to be displayed within expanding and collapsing panels.
I am familiar enough with the AjaxControlToolkit's Accordion control to know that it should be the right tool for the job here, but I haven't actually used it before.  So I did a little research on the Accordion and created a small demo page that we used to get a feel for how the control behaves and what it takes to build and style it.



image

Overview of the Accordion AjaxControlToolkit Control

In case the Accordion control is brand new to you, here is the description of the control taken from the toolkit's documentation.

The Accordion is not the only toolkit control that allows you to provide multiple panes of content and display them one at a time, the Tab control does this as well.  The trick is deciding what control better meets your requirements.  For my scenario the choice was simple - my UI design team had already made this decision for me ;).

While there are some visual differences between the Tab and Accordion controls, the markup for these two controls share a similar structure.  Just like the Tab control, there are templates provided for specifying the markup for the header and content of each of the AccordionPanes. 
Below are 2 sample screen shots of a simple Accordion Control that contains 2 panes of content: Mail and Notes.  Just below these screen shots is the markup I used to create these screen shots.  I in-lined some basic styles to make it easier to map the AccordionPanes markup to the screen shots .
image   image
image

Building the AccordionPane Content with the ListView

After I had the feel for what it takes to creating the individual AccordionPanes and a rough idea for how I wanted to present the sub-categories, I then started replacing the pane content with the new ListView control.  To do this, I removed the static UL/LI structure from the AccordionPane's Content section and replaced it with a ListView (which is now responsible for generating this content).  Making this replacement allows me to dynamically generate the UL/LI structure no matter how many subcategories there are.  Below is the new markup for the Notes panel.  
image

Styling the Accordion

Now I have the basic structure I was looking for, but the style needs some work. 
Styling the Pane's Header
To style the pane headers, I used 2 images.  One for the background and another for the mouseover.
 
Styling the Pane's Content Background
To style the content background, I used the following background image.

Styling the Pane's UL/LI  
Styling the UL/LI was a little bit more difficult for cross browser support.  I started off using the LI's list-style-image property, but because of the differences in padding and margins used by the different browsers, this never rendered how I wanted in IE7.  So instead I followed this advise and used the LI's background property instead.  This worked much better.  Here are a few of the images I used for the Mail subcategories:
  
Because these images are specific to each individual item, I generated the in-line style rule using a databinding expression like so
image
By the way, all of the images for this sample were taken from ComponentArt's great looking Outlook 2003 NavBar example.  The download includes both PNG and GIF image formats.  I used GIF because I wanted to support IE6, but I included both just anyone would find them useful.

Important Accordion Properties

After I had the AccordionPane content rendering as desired, I started tweaking the behavior by modifying some the of properties the Accordion control exposes.  Here are a few of the ones that I found useful:
  • FadeTransitions
    • If you set this to true, a Fade Animation will be applied to the Accordion as the user transitions between selected panes.
  • RequireOpenPane
    • When this is set to true, the Accordion control will always have one pane open.  If you set it to false, the user can close the currently open pane by clicking on its header (leaving all of the panes closed).
  • AutoSize
image
  • SuppressHeaderPostbacks
    • When this is set to true, the Accordion will automatically suppress any postbacks that originate from controls contained within the AccordionPanes Header's.

Wednesday 25 May 2011

Ajax Folder Explorer

 Ajax Folder Explorer with ASP.NET 3.5


One thing my original implementation is clearly lacking is a professional look and feel.  So I recently had the opportunity to upgrade it to .Net 3.5 and in the process I decided it was time to overhaul the style.  Using the Vista folder explorer as my template, I made the following changes to the original sample ...
  • Replaced the plus/minus icons with dark and light arrows
  • Added soft blue rounded corners to the explorers container
  • Added modified date column to the right hand pane
  • Added a footer the displays the name and modified date of the currently selected folder



The FileSystemDataSource

The MSDN documentation for the HierarchicalDataSourceControl provides a sample implementation for binding to files and folders.  I took this sample implementation and made a handful of minor modifications, including specifying if you would like folders and files retrieved or just folders.  Here is a sample configuration that is used for the above screen shot.        
image    
If you do not want the root folder to be displayed, just set the IncludeRoot attribute to false and the folders will render as follows (notice there is no top level root node anymore).  In my original post I received quite a few questions about how this could be done so I figured I would add it in with this example.
image

Configuring the Folder TreeView

After setting up the datasource, I went ahead and created the markup for the TreeView that displays that folders.  Most of the markup is for controlling the styling.  I have added CssClasses for the tree's container as well as custom classes for nodes and selected nodes.  I have also added an event handler for the SelectedNodeChanged event so I can update the right hand pane with the selected folders contents as the user selects different folders from the left hand pane. 
image 

Folder Contents ListView

I am using ASP.NET 3.5's new ListView control to handle rendering the right hand pane of the folder browser.  This is just a standard HTML table with 3 columns: Name, Date modified and Size.  I also added an EmptyDataTemplate that renders the 'This folder is empty' where there are no items in the selected folder.  When the user selects a folder from the left hand pane, I programmatically fetch all of the FileSystemInfo objects for the selected folder and assign this collection to the ListView's DataSource property.
image
I am also attaching to the ListView's ItemDataBound and ItemCommand event handlers.  From the ItemDataBound handler, I need to check if the FileSystemInfo object being bound to is a FileInfo or DirectoryInfo object.  If it is a DirectoryInfo, I need to assign the folder icon.  If it is a file I need to calculate the size of the file as well as set the pdf file icon.  For the case where the databound item is a FileInfo object I am also registering the image and link buttons as full postback controls so the file can be properly written to the Response stream when the user clicks on either of the buttons.  Failing to do this will cause that dreaded PageRequestManagerParserErrorException - the UpdatePanel doesn't like the Response object to be modified directly.
Note: If you have a terrible memory like me, make sure you bookmark the ListViewDataItem class.  It shows an example of how to access the databound item from the ItemDataBound event handler.  If the ItemType of the ListViewItem is a DataItem, you can cast the ListViewItem as a ListViewDataItem and use the DataItem property to get at the databound item (FileSystemInfo's for my example).  From using the GridView, I got used to getting right at the data item from the DataBound event handler - with the ListView you have to do just a little bit more work. 
image

Folder Detail Footer

If you look back at the demo page or screen shot below, you will also notice that I included some additional context information for the selected folder in the browsers footer.  This is just a DetailsView that I bind as the user changes the selected folder.
image

The IE6 Doubled Float-Margin Bug

I chose to use a floating DIV for the left pane portion of the control.  In doing so I bumped into an IE6 bug that I am sure most of you are familiar with - in certain situations IE6 doubles the margins of containers that are floated.  Here is a good description of the problem and a solution.  Another browser specific quirk to try to remember when troubleshooting ...      

ToDo

I like my new Vista styled explorer - much better looking than the original incarnation.  But I didn't get to everything I wanted.  Here are a few of the remaining items that I still need to add ...
  • Add sorting to the column headers
  • Add a progress indicator while the folder listings are being retrieved
  • Add support for scrolling the right hand pane.  Ideally, freeze the column headers and save a little room to the right of the pane for the scroll bar.

jQuery progressbar

The controls found in the AjaxControlToolkit fit perfectly into the ASP.NET programming model (as to be expected).  You can configure the controls properties through the markup which in turn affects how the control's display and behavior on the client.  Many of the control's properties support databinding so using the controls within a databound control like a ListView or a GridView is no big deal.  Take for example the ProgressBar Toolkit control.  One of the places that I use this control is in a GridView's TemplateField.  Instead of displaying the percentage as a text value, I use the ProgressBar to display the percentage in a more visual manner.  It looks something like this ...



With the Toolkit, setting this up is really simple.  Just use a databinding expression to set the Value property of the ProgressBar control to the value of the PercentComplete property of the databound item.  Done.
   1: <mb:ProgressControl 
   2:     ID="ProgressControl17" runat="server" 
   3:     Value='<%# Eval("PercentComplete") %>' 
   4:     Animate="true" Mode="Manual" ShowStatusText="false" 
   5: />

And if I start using and building jQuery plugins I am thinking there will be cases where I am going to miss how simple this is.  So after learning about jQuery's metadata plugin, I thought it would be interesting to see explore it a bit more to see if it could be used to enable some of the common scenarios.  There are 2 things the metadata plugin has going for it that I think will help it fit into the ASP.NET programming model ...
  • If a jQuery plugin supports the metadata plugin in, you can specify the plugin options on a per element basis by using some JSON within the elements class attribute to specify the option properties you would like to use.  To me this really doesn't feel too different than pointing a Toolkit control (using TargetControlID) at an existing web control.
  • It can all be done via markup - no need to emit any extra javascript

Creating the Plugin

Before I show how this can be done using the ListView, I thought I would show what the jQuery plugin looks like (granted it doesn't have nearly as many features as the Toolkit version, but it still very useful for the data grid scenario).  Here is how the plugin works ...
  1. Coalesce the default options together with any options that are explicitly provided when the progressbar plugin is applied (Line #5)
  2. Inject the DIV elements that are used for styling the progress bar (Line #9)
  3. Check to see if the metedata plugin is available.  If it is override any of the element specific options (Line 12)
  4. Finally, use find to locate the progress_indicator DIV whose background image is set to the progress image (this is applied via the stylesheet).  Set the title attribute of this element and animate the width to the specified value. (Lines #16-#20)
Could it get any simpler?
   1: (function($) {
   2:  
   3:     $.fn.progressbar = function(options) {
   4:         // build main options before element iteration
   5:         var opts = $.extend({}, {value:0, tooltip:''}, options);
   6:  
   7:         return this.each(function() {
   8:             //  add the progress DOM structure
   9:             $(this).html('<div class="progress_outer"><div class="progress_inner"><div class="progress_indicator"></div></div></div>');
  10:             
  11:             //  if the metadata plug-in is installed, use it to build the options
  12:             var o = $.metadata ? $.extend({}, opts, $(this).metadata()) : opts;
  13:             
  14:             //  locate the DOM element that contains
  15:             //  the progress image        
  16:             $(this).find('.progress_indicator')
  17:                 //  add the tooltip
  18:                 .attr('title', o.tooltip)            
  19:                 //  and animate the width
  20:                 .animate({width: o.value + '%'}, 'slow');
  21:         });
  22:     };
  23:  
  24: })(jQuery);

Using the jQuery progressbar Plugin with the ListView

Then, I can use databinding expressions to encode the tooltip and value options using the databinding expression.  It isn't as pretty, but it defiantly works.  All of the magic happens in line #16.  And if you set the control to runat server, you could populate this value from the codebehind as well. 
   1: <asp:ListView ID="lvWorkItems" runat="server" DataSourceID="ldsWorkItems">
   2:     <LayoutTemplate>
   3:         <table class="yui-grid" cellspacing="0" cellpadding="0">
   4:             <tr class="hdr">
   5:                 <th><asp:LinkButton ID="btnIDSort" runat="server" Text="ID" CommandName="Sort" CommandArgument="ID" /></th>
   6:                 <th><asp:LinkButton ID="LinkButton1" runat="server" Text="Name" CommandName="Sort" CommandArgument="Name" /></th>
   7:                 <th><asp:LinkButton ID="LinkButton2" runat="server" Text="Percent Complete" CommandName="Sort" CommandArgument="PercentComplete" /></th>
   8:             </tr>
   9:             <tr id="itemPlaceholder" runat="server" />
  10:         </table>
  11:     </LayoutTemplate>
  12:     <ItemTemplate>
  13:         <tr class='<%# Container.DataItemIndex % 2 == 0 ? "row" : "altrow" %>'>
  14:             <td><%# Eval("ID") %></td>
  15:             <td><%# Eval("Name") %></td>
  16:             <td><div class='progressBar {value: "<%# Eval("PercentComplete") %>", tooltip:"<%# string.Format("Task {0} is {1}% complete!", Eval("ID"), Eval("PercentComplete")) %>"}'></div></td>
  17:         </tr>
  18:     </ItemTemplate>
  19: </asp:ListView> 

Applying the Plugin

And the only remaining bit of awkwardness is that the progressbar plugin needs to be applied twice.  Once when the page first loads.
   1: function pageLoad(sender, args){
   2:     if(!args.get_isPartialLoad()){ 
   3:         //  apply the 
   4:         $('.progressBar').progressbar();
   5:     }
   6: }

And then again just after an UpdatePanel is refreshed (I apply it to the panels new contents) ...
   1: Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(function(sender, args){
   2:     var updatedPanels = args.get_panelsUpdated();
   3:     if(updatedPanels && updatedPanels.length > 0){
   4:         for(var i = 0; i < updatedPanels.length; i++) {
   5:              $('.progressBar', updatedPanels[i]).progressbar();
   6:         }
   7:     }                    
   8: });

Footprint

And as promised, here is the JavaScript and CSS footprint for this example.  I am using ASP.NET AJAX's ScriptManager and UpdatePanel controls - so those are the axd JavaScript references.  The JavaScript for my plugin is ~1 KB unminified.
JavaScript: 113 KB
image
CSS: 2 KB
image

Tuesday 24 May 2011

HTML and JavaScript used to configure the grid.



The html file looks like this:
<html> <head> <title>jqGrid Demo</title> <link rel="stylesheet" type="text/css" media="screen" href="themes/basic/grid.css" /> <link rel="stylesheet" type="text/css" media="screen" href="themes/jqModal.css" /> <script src="jquery.js" type="text/javascript"></script> <script src="jquery.jqGrid.js" type="text/javascript"></script> <script src="js/jqModal.js" type="text/javascript"></script> <script src="js/jqDnR.js" type="text/javascript"></script> <script type="text/javascript"> jQuery(document).ready(function(){ jQuery("#list").jqGrid({ url:'example.php', datatype: 'xml', mtype: 'GET', colNames:['Inv No','Date', 'Amount','Tax','Total','Notes'], colModel :[ {name:'invid', index:'invid', width:55}, {name:'invdate', index:'invdate', width:90}, {name:'amount', index:'amount', width:80, align:'right'}, {name:'tax', index:'tax', width:80, align:'right'}, {name:'total', index:'total', width:80, align:'right'}, {name:'note', index:'note', width:150, sortable:false} ], pager: jQuery('#pager'), rowNum:10, rowList:[10,20,30], sortname: 'id', sortorder: "desc", viewrecords: true, imgpath: 'themes/basic/images', caption: 'My first grid' }); }); </script> </head> <body> <table id="list" class="scroll"></table> <div id="pager" class="scroll" style="text-align:center;"></div> </body> </html>
The assumption that makes the above work is that the saved file is in the directory where you placed the jqGrid files. If it is not, you will need to change the pathing appropriately.
First, we need to include the files required to construct the grid. This is done between the <head> tags in the html document.

<head> <link rel="stylesheet" type="text/css" media="screen" href="themes/basic/grid.css" /> <script src="jquery.js" type="text/javascript"></script> <script src="jquery.jqGrid.js" type="text/javascript"></script></head>
  • The <link…/> tag loads the style sheet for jqGrid,
  • The first <script ../> tag loads the jquery library,
  • The second <script ../> tag loads the required jqGrid plugins,
  • The third and fourth <script ../> tags load the additional modules required for some functions, and
  • The last script tag is where we write the commands needed to construct the grid. A detailed description of this area appears below.
Between the <body> tags you define where you want to place the grid.

<body><table id="list" class="scroll"></table> <div id="pager" class="scroll" style="text-align:center;"></div></body>
The definition of the grid is done via the html tag <table>. To make our life easy it is good idea to give the table an id that is unique in this html document. The second step is to assign a class "scroll" so that we can use the style definitions in the CSS provided with jqGrid.
Cellspacing, cellpadding and border attributes are added by jqGrid and shoudl not be included in the definition of your table.
We want to use a paging mechanism too, so we define the navigation layer. This can be done with the commonly-used <div> tag. Giving the class "scroll" of the navigator specifies that we want to use the CSS provided with jqGrid. It is important to note that the navigation layer can be placed arbitrarily any place in the html document. Normally, and in this case, it is under the grid.
We use the jQuery document.ready function to run our script at the appropriate time. For more information on this, refer to the jQuery documentation.
The syntax for constructing the grid is:
jQuery('#grid_selector').jqGrid( options )
where:

  • grid_selector is the unique id of the grid table (list using our example above)
  • jqGrid is the plugin, and
  • options is an array, in our example several lines, of the information needed to construct the grid.
Let’s begin with the options array, which looks like this: (These options can appear in any order)

{ url:'example.php', datatype: 'xml', mtype: 'GET', colNames:['Inv No','Date', 'Amount','Tax','Total','Notes'], colModel :[ {name:'invid', index:'invid', width:55}, {name:'invdate', index:'invdate', width:90}, {name:'amount', index:'amount', width:80, align:'right'}, {name:'tax', index:'tax', width:80, align:'right'}, {name:'total', index:'total', width:80,align:'right'}, {name:'note', index:'note', width:150, sortable:false} ], pager: jQuery('#pager'), rowNum:10, rowList:[10,20,30], sortname: 'id', sortorder: 'desc', viewrecords: true, imgpath: 'themes/basic/images', caption: 'My first grid' }
The settings and options used above are described here; listings of all settings and options can be found in API Methods and colModel API.


Property Description
url tells us where to get the data. Typically this is a server-side function with a connection to a database which returns the appropriate information to be filled into the Body layer in the grid
datatype this tells jqGrid the type of information being returned so it can construct the grid. In this case we tell the grid that we expect xml data to be returned from the server, but other formats are possible. For a list of all available datatypes refer to API Methods
mtype tells us how to make the ajax call: either 'GET' or 'POST'. In this case we will use the GET method to retrieve data from the server
colNames an array in which we place the names of the columns. This is the text that appears in the head of the grid (Header layer). The names are separated with commas
colModel an array that describes the model of the columns. This is the most important part of the grid. Here I explain only the options used above. For the complete list of options see colModel API:
name
the name of the column. This name does not have to be the name from database table, but later we will see how we can use this when we have different data formats
index
the name passed to the server on which to sort the data (note that we could pass column numbers instead). Typically this is the name (or names) from database -- this is server-side sorting, so what you pass depends on what your server expects to receive
width
the width of the column, in pixels
align
the alignment of the column
sortable
specifies if the data in the grid can be sorted on this column; if false, clicking on the header has no effect
pager defines that we want to use a pager bar to navigate through the records. This must be a valid html element; in our example we gave the div the id of "pager", but any name is acceptable. Note that the Navigation layer (the "pager" div) can be positioned anywhere you want, determined by your html; in our example we specified that the pager will appear after the Body layer.
rowNum sets how many records we want to view in the grid. This parameter is passed to the url for use by the server routine retrieving the data
rowList an array to construct a select box element in the pager in which we can change the number of the visible rows. When changed during the execution, this parameter replaces the rowNum parameter that is passed to the url
sortname sets the initial sorting column. Can be a name or number. This parameter is added to the url for use by the server routine
sortorder sets the sorting order. This parameter is added to the url
viewrecords defines whether we want to display the number of total records from the query in the pager bar
imgpath the path to the images needed for the grid. The path should not end with '/'
caption sets the caption for the grid. If this parameter is not set the Caption layer will be not visible
Having done this, we have now done half the work. The next step is to construct the server-side manipulation -- in the file pointed to in the url parameter in the grid.



  Last Updated: 25/5/2011 | © Sriseshaa.com jqGrid - a jQuery Plugin, 2011

JSON



JSON data is handled in a fashion very similar to that of xml data. What is important is that the definition of the jsonReader matches the data being receiveddatatype: json, (or jsonstring)
The default definition of the jsonreader is as follows:
jsonReader : { root: "rows", page: "page", total: "total", records: "records", repeatitems: true, cell: "cell", id: "id", userdata: "userdata", subgrid: {root:"rows", repeatitems: true, cell:"cell" } }
datastr:
If the parameter datatype is 'json', jqGrid expects the following default format for json data.
{ total: "xxx", page: "yyy", records: "zzz", rows : [ {id:"1", cell:["cell11", "cell12", "cell13"]}, {id:"2", cell:["cell21", "cell22", "cell23"]}, ... ] }
The tags used in this example are described in the following table:

Tag Description
total total pages for the query
page current page of the query
records total number of records for the query
rows an array that contains the actual data
id the unique id of the row
cell an array that contains the data for a row
In this case, the number of the elements in the cell array should equal the number of elements in colModel.
Let's consider our example in PHP and MySQL with JSON data. In this case I assume that the json service is enabled in PHP.
<?php include("dbconfig.php"); $page = $_REQUEST['page']; // get the requested page $limit = $_REQUEST['rows']; // get how many rows we want to have into the grid $sidx = $_REQUEST['sidx']; // get index row - i.e. user click to sort $sord = $_REQUEST['sord']; // get the direction if(!$sidx) $sidx =1; // connect to the database $db = mysql_connect($dbhost, $dbuser, $dbpassword) or die("Connection Error: " . mysql_error()); mysql_select_db($database) or die("Error conecting to db."); $result = mysql_query("SELECT COUNT(*) AS count FROM invheader a, clients b WHERE a.client_id=b.client_id".$wh); $row = mysql_fetch_array($result,MYSQL_ASSOC); $count = $row['count']; if( $count >0 ) { $total_pages = ceil($count/$limit); } else { $total_pages = 0; } if ($page > $total_pages) $page=$total_pages; $start = $limit*$page - $limit; // do not put $limit*($page - 1) if ($start<0) $start = 0; $SQL = "SELECT invid,invdate,amount,tax,total,note FROM invheader ORDER BY ".$sidx." ".$sord. " LIMIT ".$start." , ".$limit; $result = mysql_query( $SQL ) or die("Could not execute query.".mysql_error()); // Construct the json data $response->page = $page; // current page $response->total = $total_pages; // total pages $response->records = $count; // total records $i=0; while($row = mysql_fetch_array($result,MYSQL_ASSOC)) { $response->rows[$i]['id']=$row[invid]; //id $response->rows[$i]['cell']=array($row[invid],$row[invdate],$row[amount],$row[tax],$row[total],$row[note]); $i++; } echo json_encode($response); ?>
The structure of the jsonReader is very similar to the xmlReader. The only missing part is the row element which is not needed for JSON data. Let's begin our walk through the jsonReader.
The first element is a root element. This element describes where our data begins. In other words, this points to the array that contains the data. If we set
jsonReader: { root:"invdata" }
then the returned string should be
{ total: "xxx", page: "yyy", records: "zzz", invdata: [ {id:"1", cell:["cell11", "cell12", "cell13"]}, {id:"2", cell:["cell21", "cell22", "cell23"]} ] }

The page, total and record elements describe the information needed for the pager. For example, if the jsonReader is set as follows,
jsonReader:{ root: "invdata", page: "currpage" total: "totalpages" records: "totalrecords" }
then the data should be
{ totalpages: "xxx", currpage: "yyy", totalrecords: "zzz", invdata: [ {id:"1", cell:["cell11", "cell12", "cell13"]}, {id:"2", cell:["cell21", "cell22", "cell23"]} ] }
The cell element describes the array which contains the data for the row.
jsonReader:{ root: "invdata", page: "currpage" total: "totalpages" records: "totalrecords", cell: "invrow" }
The data to match this description would be
{ totalpages: "xxx", currpage: "yyy", totalrecords: "zzz", invdata : [ {id:"1", invrow:["cell11", "cell12", "cell13"]}, {id:"2", invrow:["cell21", "cell22", "cell23"]} ] }
The id element descibes the unique id for the row
jsonReader:{ root: "invdata", page: "currpage" total: "totalpages" records: "totalrecords", cell: "invrow", id: "invid" }
The data for this description is:
{ totalpages: "xxx", currpage: "yyy", totalrecords: "zzz", invdata : [ {invid:"1", invrow:["cell11", "cell12", "cell13"]}, {invid:"2", invrow:["cell21", "cell22", "cell23"]} ] }
It is possible to set the cell element to an empty string. And, it is possible to set the id as number. Here is an example of these possibilities:
jsonReader:{ root: "invdata", page: "currpage" total: "totalpages" records: "totalrecords", cell: "", id: "0" }
In this case the id is the first element from the row data
{ totalpages: "xxx", currpage: "yyy", totalrecords: "zzz", invdata: [ {"1", "cell11", "cell12", "cell13"}, {"2", "cell21", "cell22", "cell23"} ] }
The repeatitems element tells jqGrid that the information for the data in the row is repeatable - i.e. the elements have the same tag cell described in cell element. Setting this option to false instructs jqGrid to search elements in the json data by name. This is the name from colModel or the name described with the jsonmap option in colModel.
Here is an example:
jsonReader:{ root: "invdata", page: "currpage" total: "totalpages" records: "totalrecords", repeatitems: false, id: "0" }
The resulting data in our example should be:
{ totalpages: "xxx", currpage: "yyy", totalrecords: "zzz", invdata: [ {invid:"1",invdate:"cell11", amount:"cell12", tax:"cell13", total:"1234", note:"somenote" }, {invid:"2",invdate:"cell21", amount:"cell22", tax:"cell23", total:"2345", note:"some note" } ] }
The id element in this case is 'invid'.
A very useful feature here is that there is no need to include all the data that is represented in colModel. Since we make a search by name, the order does not need to match the order in colModel. Hence the following string will be correctly interpreted in jqGrid.
{ totalpages: "xxx", currpage: "yyy", totalrecords: "zzz", invdata: [ {invid:"1",invdate:"cell11", note:"somenote" }, {invid:"2", amount:"cell22", tax:"cell23", total:"2345" } ] }



  Last Updated: 25/5/2011 | © Sriseshaa.com jqGrid - a jQuery Plugin, 2011

jquery Installation



First you need to download the jQuery JavaScript library. This library can be downloaded from www.jquery.com. Please download the latest stable version of jQuery library and not a development version.You need to download the jqGrid plugin.
Create a directory on your web server, so that you can access it: http://myserver/mydir/, where mydir is the name that you have created.
Place the jQuery library in that directory; unpack the jqGrid.zip in the same directory. You should have this directory structure:

  • jquery.js
  • jquery.jqGrid.js
  • js
    • grid.base.js
    • grid.celledit.js
    • grid.common.js
    • grid.custom.js
    • grid.formedit.js
    • grid.inlinedit.js
    • grid.locale-en.js
    • grid.postext.js
    • grid.setcolumns.js
    • grid.subgrid.js
    • grid.tbltogrid.js
    • grid.treegrid.js
    • jqDnR.js
    • jqModal.js
    • jquery.tablednd.js
    • jquery.fmatter.js
    • json2.js
    • JsonXml.js
    • min
      • grid.base-min.js
      • grid.celledit-min.js
      • grid.common-min.js
      • grid.custom-min.js
      • grid.formedit-min.js
      • grid.inlinedit-min.js
      • grid.locale-en-min.js
      • grid.postext-min.js
      • grid.setcolumns-min.js
      • grid.subgrid-min.js
      • grid.tbltogrid-min.js
      • grid.treegrid-min.js
      • jquery.fmatter-min.js
      • json2-min.js
      • JsonXml-min.js
    • packed
    • packall
  • themes
    • basic (a folder containing several files related to this theme)
    • coffee (another folder with theme files)
    • green (jqGrid comes with the five themes shown here)
    • sand (you can easily add your own)
    • steel
    • jqModal.css


where:
  • jquery.js is the jQuery library,
  • jquery.jqGrid.js is the main module for including different plugins depending on your needs.
  • grid.base.js is the main plugin. Without this plugin, all other plugins are unusable.
  • grid.celledit.js a plugin used if you want to have cell editing
  • grid.common.js a required module containing code common to many areas of jqGrid
  • grid.custom.js a plugin used if you want to use advanced grid methods
  • grid.formedit.js a plugin used for form editing, including adding and deleting data.
  • grid.inlinedit.js a plugin used if you want to have inline editing
  • grid.locale-en.js a plugin used if you want to dynamically change the language.
  • grid.postext.js a plugin (available separately) used to manipulate the post data array
  • grid.setcolumns.js a plugin used if you want to allow users to choose which columns to show or hide
  • grid.subgrid.js a plugin used if you want to use subgrids
  • grid.tbltogrid.js a plugin used if you want to convert html tables to a jqGrid
  • grid.treegrid.js a plugin used if you want to use a tree grid
  • jqModal.js a plugin used for form editing (modal dialogs)
  • jqDnR.js a plugin used for form editing (drag and resize)
  • jqQuery.TableDnD.js a plugin used for rearranging rows (drag and drop) in the grid
  • grid.import.js a plugin used for importing and exporting grid configuration
  • json2.js json utilities used in import/export module
  • JsonXml.js xmljson utilities used in import/export module
  • min the directory/folder containing the minified versions of the javascript files, suitable for production
  • packed the directory/folder containing all the modules in packed variant (named the same as those in js), suitable for production use.
  • packall the directory/folder containing a single file, grid.pack.js, which contains the entire suite of jqGrid files without any language file, suitable for production use.
  • themes the directory/folder containing the different styles for the grid.

If you want to use all of the features of jqGrid you do not need to do anything more.
If you want to use only some of the features or only the basic functions of jqGrid, you may want to edit the jquery.jqGrid.js file and remove the files you will not be using. This file must also be edited if you place the javascript files in other locations than those specified above. This file is simple and can be easily configured.
// we make it simple as possible function jqGridInclude() { var pathtojsfiles = "js/"; // need to be ajusted // if you do not want some module to be included // set include to false. // by default all modules are included. var minver = false; var modules = [ { include: true, incfile:'grid.locale-en.js',minfile: 'min/grid.locale-en-min.js'}, // jqGrid translation { include: true, incfile:'grid.base.js',minfile: 'min/grid.base-min.js'}, // jqGrid base { include: true, incfile:'grid.common.js',minfile: 'min/grid.common-min.js' }, // jqGrid common for editing { include: true, incfile:'grid.formedit.js',minfile: 'min/grid.formedit-min.js' }, // jqGrid Form editing { include: true, incfile:'grid.inlinedit.js',minfile: 'min/grid.inlinedit-min.js' }, // jqGrid inline editing { include: true, incfile:'grid.celledit.js',minfile: 'min/grid.celledit-min.js' }, // jqGrid cell editing { include: true, incfile:'grid.subgrid.js',minfile: 'min/grid.subgrid-min.js'}, //jqGrid subgrid { include: true, incfile:'grid.treegrid.js',minfile: 'min/grid.treegrid-min.js'}, //jqGrid treegrid { include: true, incfile:'grid.custom.js',minfile: 'min/grid.custom-min.js'}, //jqGrid custom { include: true, incfile:'grid.postext.js',minfile: 'min/grid.postext-min.js'}, //jqGrid postext { include: true, incfile:'grid.tbltogrid.js',minfile: 'min/grid.tbltogrid-min.js'}, //jqGrid table to grid function { include: true, incfile:'grid.setcolumns.js',minfile: 'min/grid.setcolumns-min.js'} //jqGrid hide/show columns function { include: true, incfile:'grid.import.js',minfile: 'min/grid.import-min.js'}, //jqGrid import { include: true, incfile:'jquery.fmatter.js',minfile: 'min/jquery.fmatter-min.js'}, //jqGrid formatter { include: true, incfile:'json2.js',minfile: 'min/json2-min.js'}, //json utils { include: true, incfile:'JsonXml.js',minfile: 'min/JsonXml-min.js'} //xmljson utils ]; for(var i=0;i<modules.length; i++) { if(modules[i].include === true) { if (minver !== true) IncludeJavaScript(pathtojsfiles+modules[i].incfile,CallMe); else IncludeJavaScript(pathtojsfiles+modules[i].minfile,CallMe); } } function IncludeJavaScript(jsFile,oCallback) { var oHead = document.getElementsByTagName('head')[0]; var oScript = document.createElement('script'); oScript.type = 'text/javascript'; oScript.src = jsFile; oHead.appendChild(oScript); } } jqGridInclude();

If you have a different path to javascript files you must change the value of the variable pathtojsfiles appropriately. This path is relative to your application (or the server application), not to jquery.jqGrid.js; so if your path to jquery.jqGrid.js is "..scripts\", this will need to be "..\scripts\js\".
If you want to exclude some modules you simply change the value of the associated include from true to false, in the modules array.
If you plan to use the form editing module you should include jqModal.js, jqDnR.js and jqModal.css files in your html page.

Using Packed Versions

If we want to use the packed variant we need only to change the variable pathtojsfiles to point to this folder. If the original pathtojsfiles is "js/", then for the packed version we change pathtojsfiles to"js/packed/".To use grid.pack.js in packall, we need first to load the appropriate language file and after that this file. Or more simply, we can set the include property in the loader to false and add new two lines for the language and the packed version.


  Last Updated: 5/25/2011 | © Sriseshaa.com - a jQuery Plugin, 2011