Dynamic Data - Experimenting with YUI's DataTable and DataSource Controls

I spent a few hours putting together a Dynamic Data web site using the YUI DataTable and DataSource components.  I mostly just did this out of curiosity to see how easy or difficult it would be to use the DD API with other components besides the GridView and DetailsView controls.  So I created a DD web site that provides read-only access to Northwind's Customer, Employee and Supplier tables, but I am not using ASP.NET's GridView, DataSource and UpdatePanel controls to render the grid.  Instead I have replaced these components with YUI's client side DataTable and DataSource and serve the data using a web service (paging included!).  Below are a few of the highlights, and don't forget to download the sample and try it out for yourself.  Its experimental, but if you are new to DD or YUI you might find it interesting. 

[Update: 9/21/2008]: Added live demo link 

Download | Live Demo

image  

 

The List Page Template

The markup for my List page template is pretty simple and fairly similar to the DD sites I have blogged about previously.  I use the table's DisplayName and Description attributes to render the title bar.  But, for this sample I am also including three extra DIV's - #paging-top, #grid and #paging-bottom.  These three DIV's will be used as the containers for the YUI pager and grid widgets. 

image

After the markup is added, I included a bit of JavaScript for configuring the YUI DataTable and DataSource components.  The source for the script is below.  There is quite a bit going on so right after the source is a line-by-line guide of what it is doing ...

   1: //  render the YUI script ...
   2: YAHOO.util.Event.addListener(window, "load", function() {
   3:     
   4:     //  create the datasource and point it at the webservice's FetchAll method
   5:     var myDataSource = new YAHOO.util.DataSource('/dd_yui/NorthwindService.asmx/FetchAll', { connMethodPost: true });
   6:     
   7:     //  set the content-type to JSON
   8:     myDataSource.connMgr = YAHOO.util.Connect;
   9:     myDataSource.connMgr.initHeader('Content-Type', 'application/json; charset=utf-8', true);
  10:     myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;            
  11:     
  12:     //  setup the response schema
  13:     myDataSource.responseSchema = 
  14:     { 
  15:         resultsList : 'd.records',
  16:         fields: <%= this.FetchDataFields() %>,
  17:         metaFields : { totalRecords: 'd.totalRecords' }                 
  18:     };
  19:    
  20:     //  configure the data pager
  21:     var myPaginator = new YAHOO.widget.Paginator({
  22:         //  2 data pagers - both top and bottom
  23:         containers : ['paging-top', 'paging-bottom'],
  24:         pageLinks : 5,
  25:         rowsPerPage : 15,
  26:         template : "{PreviousPageLink} {PageLinks} {NextPageLink}"
  27:     });                       
  28:     
  29:     //  setup the table settings
  30:     var myTableConfig = {
  31:         //  JSON object that maps to the parameters of my WebMethod
  32:         initialRequest : '{startIndex:0, pageSize:15, tableName:"<%= this.table.Name %>"}',
  33:         // A custom function to translate the js paging request into a JSON
  34:         generateRequest : function(state, dt){
  35:             return '{startIndex:' + state.pagination.recordOffset + ', pageSize:' + state.pagination.rowsPerPage + ', tableName:"<%= this.table.Name %>"}'
  36:         },
  37:         paginator : myPaginator,
  38:         paginationEventHandler : YAHOO.widget.DataTable.handleDataSourcePagination
  39:     };            
  40:     
  41:     //  create the datatable
  42:     this.myDataTable = new YAHOO.widget.DataTable("grid", <%= this.FetchColumnDefinitions() %>, myDataSource, myTableConfig);
  43: });
  • Line 5 - 10:  Create the YUI DataSource. 
    • The YUI DataSource can grab data from just about anywhere - an XML DOM object, JavaScript array, an HTML Table, or from a remote server.  For this example I created a FetchAll web method that will serve the Northwind data so I have configured the DataSource to use this web method.
  • Lines 13 - 18: Describe what the response is going to look like.
    • You need to tell the DataSource what the elements returned from your data look like.  In lines 15 - 17 I am letting the DataSource know what it can expect to be returned from my web service.  If you notice the fields array is dynamically created by invoking the FetchDataFields method on my List.aspx template page.  FetchDataFields queries the MetaTable for the current request and includes the names of all of the columns that need to be scaffolded.  Here is the source for this method.
    • Notice that I am retrieving the reference to the MetaTable from the DynamicDataRouteHandler - keeping my template unaware of the entity type the page is being requested for (line 4)
   1: public string FetchDataFields()
   2: {
   3:     //  get a reference to the MetaTable for the current route/request
   4:     MetaTable table = DynamicDataRouteHandler.GetRequestMetaTable(HttpContext.Current);
   5:  
   6:     var columns =
   7:         from c in table.Columns
   8:         where c.Scaffold
   9:         select c.Name;
  10:  
  11:     return new JavaScriptSerializer().Serialize(columns.ToArray());
  12: }
  • Lines 21 - 27: Initialize the data pager controls. 
    • I wanted to use 2 pagers - one above the grid and another below it so I added DIV's to the List template for both of these items.  When I create the Paginator widget I supply YUI with the ID's of the DIV's that should contain the paging controls.
  • Lines 30 - 39: Setup the config options for the table.
  • Line 42: Create the DataTable.
    • I need to tell the DataTable about some basic metadata regarding the grids columns.  For this example I have included only the most basic information - the mapping between my column headers and the fields from the object returned from my web method that fills the grid ...
   1: public string FetchColumnDefinitions()
   2: {
   3:     //  get a reference to the MetaTable for the current route/request
   4:     MetaTable table = DynamicDataRouteHandler.GetRequestMetaTable(HttpContext.Current);
   5:  
   6:     //  grab the names of the columns we are scaffolding.
   7:     //  YUI uses the key/label to map the fields returned
   8:     //  by our web method to the columns in the DataTable
   9:     var columns =
  10:         from c in table.Columns
  11:         where c.Scaffold
  12:         select new
  13:         {
  14:             key = c.Name,
  15:             label = c.DisplayName,
  16:             resizable = true
  17:         };
  18:  
  19:     return new JavaScriptSerializer().Serialize(columns.ToArray());
  20: }
    • Finally, I use all of the settings and objects created previously to initialize a new DataTable instance - pointing it to the #grid DIV that I have already added to the page

 

The FetchAll Web Method

Finally, here is the source listing for my FetchAll web method.  I have configured the YUI DataSource to pass through the name of the table to retrieve the data for.  This is what is allowing me to use the same web method for all of the tables.  So this is the first parameter, the second and third parameters are the paging arguments.  Other than that the method is pretty simple ...

  • Line 6 - 8: Use the tableName argument to get the MetaTable its corresponding IQuerable objects
  • Line 11: We need to total row count for our paging math - so execute a count(*)
  • Line 14: We only need to retrieve the columns we are scaffolding, so get the list of columns we want to include in the select
  • Line 20 - 26: Create the object that we will return to the YUI DataSource
    • Include the total row count
    • Use Skip and Take to handling the paging parameters
    • Use Select to make sure only the columns we are using are being returned
   1: [WebMethod]
   2: [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
   3: public object FetchAll(string tableName, int startIndex, int pageSize) 
   4: {
   5:     //  get the MetaTable for this table
   6:     MetaTable table = MetaModel.GetModel(typeof(NorthwindDataContext)).GetTable(tableName, typeof(NorthwindDataContext));
   7:     //  get the query
   8:     IQueryable query = table.GetQuery();
   9:  
  10:     //  total row count - used for the paging controls
  11:     int rowCount = query.Count();
  12:  
  13:     //  only pull back the columns we are scaffolding
  14:     var columns = 
  15:         from c in table.Columns
  16:         where c.Scaffold
  17:         select c.Name;
  18:  
  19:     //  return the collection
  20:     return new
  21:     {
  22:         //  include the total row count
  23:         totalRecords = rowCount,
  24:         //  get the current page
  25:         records = query.Skip(startIndex).Take(pageSize).Select(string.Format("new({0})", string.Join(",", columns.ToArray())))
  26:     };
  27: }

 

Conclusion

Well, like I said it was just an experiment.  Probably not ready for production and I haven't quite worked out how the edit/delete scenarios are going to work, but I thought it was interesting enough to write up a quick post about.  And DiscountASP is upgrading to .NET 3.5 SP1 sometime this week, so I should have the demo's up for my DD posts soon.

 

That's it.  Enjoy!


TrackBack

TrackBack URL for this entry:
http://mattberseth.com/blog-mt/mt-tb.fcgi/151

Listed below are links to weblogs that reference Dynamic Data - Experimenting with YUI's DataTable and DataSource Controls:

» ASP.NET Demo Gallery from Thomas Bandt
Matt Berseth hat eine ganze Latte interessanter ASP.NET-, Ajax- und Silverlight Beispiele zusammengefasst und veröffentlicht. Darunter finden sich wirklich nützliche Sachen die man immer wieder braucht, wie eine Upload-Progress-Bar, ein Datei-Explore... [Read More]

Comments


Good one, although it is an experiment. I like experiments! Anyway, I see you are using Google Chrome. What are your thought on it? I was a little pessimistic about it.

Well congrats on the idea. By a way or another, edit is going to work. It will just need some investigation to build it up and make it ready. But already you did a great job make a combination between YUI and ASP.NET Dynamic Data.
That was really interesting post and interesting experiments. And myself I appreciate the time you spent to build the sample and write this informative post Matt.
Thank you

Excellent!

I am looking search capacity

Have we able to do that with dynamic data?

Hey Janko -
I am really digging chrome so far - it's fast, pretty and hasn't crashed on me yet. So far so good ;)

Hi Muhammad -
Yea, I am thinking about maybe looking into the edit scenarios next. We'll see how that goes. I will no doubt post what I find.

Siraj -
Like a column filter? You get a certain amount of that if you the dynamic data controls. You should setup a DD web site (use the template that comes with SP1) and point it at Northwind to get a feeling for what you can do with it

Hi Matt, great post. The live demo is giving a "Data error" when the page first loads. Firebug shows a "HTTP Error 403.1 - Forbidden" error for the response from:

http://mattberseth2.com/dd_yui/NorthwindService.asmx/FetchAll

I'll try it again later ... thanks.

Thanks for the heads up - this demo appears to be broken for FF. I am working on it ...

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Consulting Services

Yep - I also offer consulting services. And heck, I'll do just about anything. If you enjoy my blog just drop me an email describing the work you need done.

Recent Comments

  • Matt Berseth wrote: Thanks for the heads up - this demo appears to be broken for FF. I am working on it ......
  • Ben Amada wrote: Hi Matt, great post. The live demo is giving a "Data error" when the page first loads. Firebug sho...
  • Matt Berseth wrote: Hey Janko - I am really digging chrome so far - it's fast, pretty and hasn't crashed on me y...
  • Siraj Gadhia wrote: Excellent! I am looking search capacity Have we able to do that with dynamic data? ...
  • Muhammad Mosa wrote: Well congrats on the idea. By a way or another, edit is going to work. It will just need some invest...
  • Janko wrote: Good one, although it is an experiment. I like experiments! Anyway, I see you are using Google Chrom...