Controls for ASP.NET MVC

 

Custom Controls in ASP.NET MVC can be created in two ways : declarative MVC Controls and HtmlHelper extension methods. A declarative MVC control example is

<mvc:GridView dataSourceController=”CarsController” dataSourceAction=”ListCars” />

whereas HtmlHelper extension methods are like

<%= Html.GridView(gridViewModel) %>

I have implemented an HtmlHelper extension method to write HTML+Javascript for Yahoo UI DataTable.

To start with let’s see the YUI DataTable documentation and samples at http://developer.yahoo.com/yui/examples/datatable/dt_basic.html.

A simple YUI DataTable requires a div tag and following JavaScript on the page:

Code Snippet
  1. <div id=”basic”></div>
  2.    <script type=”text/javascript”>
  3.        YAHOO.example.Data = {
  4.            bookorders: [
  5.         { id: “po-0167”, date: new Date(1980, 2, 24), quantity: 1, amount: 4, title: “A Book About Nothing” },
  6.         { id: “po-0783”, date: new Date(“January 3, 1983”), quantity: null, amount: 12.12345, title: “The Meaning of Life” },
  7.         { id: “po-0297”, date: new Date(1978, 11, 12), quantity: 12, amount: 1.25, title: “This Book Was Meant to Be Read Aloud” },
  8.         { id: “po-1482”, date: new Date(“March 11, 1985”), quantity: 6, amount: 3.5, title: “Read Me Twice” }
  9.     ]
  10.        }
  11.  
  12.        YAHOO.util.Event.addListener(window, “load”, function () {
  13.            YAHOO.example.Basic = function () {
  14.                var myColumnDefs = [
  15.             { key: “id”, sortable: true, resizeable: true },
  16.             { key: “date”, formatter: YAHOO.widget.DataTable.formatDate, sortable: true, sortOptions: { defaultDir: YAHOO.widget.DataTable.CLASS_DESC }, resizeable: true },
  17.             { key: “quantity”, formatter: YAHOO.widget.DataTable.formatNumber, sortable: true, resizeable: true },
  18.             { key: “amount”, formatter: YAHOO.widget.DataTable.formatCurrency, sortable: true, resizeable: true },
  19.             { key: “title”, sortable: true, resizeable: true }
  20.         ];
  21.  
  22.                var myDataSource = new YAHOO.util.DataSource(YAHOO.example.Data.bookorders);
  23.                myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
  24.                myDataSource.responseSchema = {
  25.                    fields: [“id”, “date”, “quantity”, “amount”, “title”]
  26.                };
  27.  
  28.                var myDataTable = new YAHOO.widget.DataTable(“basic”,
  29.                 myColumnDefs, myDataSource, { caption: “DataTable Caption” });
  30.  
  31.                return {
  32.                    oDS: myDataSource,
  33.                    oDT: myDataTable
  34.                };
  35.            } ();
  36.        });
  37.  
  38.    </script>

 

To represent the View Model classes YUIGridViewModel and YUIDataColumnDefinition as \

Code Snippet
  1. public class YUIGridViewModel
  2.     {
  3.         public IEnumerable<YUIDataColumnDefinition> ColumnDefintions { get; set; }
  4.  
  5.         public String DataSourceAction { get; set; }
  6.  
  7.         public String Title { get; set; }
  8.  
  9.         public String Name { get; set; }
  10.  
  11.         public String DivElement { get; set; }               
  12.     }

 

Code Snippet
  1. public class YUIDataColumnDefinition
  2.   {
  3.       public String Key { get; set; }
  4.       public String Label { get; set; }
  5.       public bool Resizable { get; set; }
  6.       public bool Visible { get; set; }
  7.       public bool Sortable { get; set; }
  8.       public FormatType Formatter { get; set; }
  9.  
  10.       public override string ToString()
  11.       {          
  12.           return String.Format(“{{ key : \”{0}\”, sortable : {1}, resizeable : {2}, label : \”{3}\” {4} }}”,
  13.                   Key,GetBoolString(Sortable), GetBoolString(Resizable), GetNonNullString(Label), GetFormatter());
  14.       
  15.       }
  16.  
  17.       private string GetBoolString(bool value)
  18.       {
  19.           return value ? “true” : “false”;
  20.       }
  21.  
  22.       private string GetNonNullString(string Label)
  23.       {
  24.           return String.IsNullOrEmpty(Label) ? String.Empty : Label;
  25.       }
  26.  
  27.       private string GetFormatter()
  28.       {
  29.           var formatString = String.Empty;// “YAHOO.widget.DataTable.formatDefault”;
  30.           switch (this.Formatter)
  31.           {
  32.               case FormatType.Date:
  33.                   formatString = “, formatter:YAHOO.widget.DataTable.formatDate “;
  34.                   break;
  35.               case FormatType.Number:
  36.                   formatString = “, formatter:YAHOO.widget.DataTable.formatNumber”;
  37.                   break;
  38.               case FormatType.Currency:
  39.                   formatString = “, formatter:YAHOO.widget.DataTable.formatCurrency”;
  40.                   break;
  41.           }
  42.           return formatString;
  43.       }
  44.   }
  45.  
  46.   public enum FormatType
  47.   {
  48.       String=0, //this makes it default??
  49.       Date,
  50.       Number,
  51.       Currency
  52.   }

 

Then I created a Repository to return the YUI DataTable View Model. Using Repository helps to abstract the ViewModel creation.

Code Snippet
  1. public class SearchRepository
  2.     {
  3.         private readonly Dictionary<string, YUIGridViewModel> _searchMetaItems;
  4.  
  5.         public SearchRepository()
  6.         {
  7.             _searchMetaItems = new Dictionary<string, YUIGridViewModel>();
  8.             InitializeYUIGridViewModel();
  9.         }
  10.  
  11.         public YUIGridViewModel GetSearchMeta(String searchName)
  12.         {
  13.             if (_searchMetaItems.Keys.Contains(searchName))
  14.                 return _searchMetaItems[searchName];
  15.             return null;
  16.         }
  17.  
  18.         private void InitializeYUIGridViewModel()
  19.         {
  20.             _searchMetaItems.Add(“ObjectModelSearch”, new YUIGridViewModel()
  21.             {
  22.                 Title = “Search Grid”,
  23.                 Name = “Grid from Object source”,
  24.                 DivElement = “IEGrid”,
  25.                 DataSourceAction = @”/Search/GetData?name=ObjectModelSearch”,
  26.                 ColumnDefintions = new List<YUIDataColumnDefinition>
  27.                 {
  28.                     new YUIDataColumnDefinition(){ Key=“id”, Label=“ID”, Sortable=true, Resizable=true,Visible=false},
  29.                     new YUIDataColumnDefinition{ Key=“date”, Label=“Date”, Formatter= FormatType.Date, Sortable=true, Resizable=true},
  30.                     new YUIDataColumnDefinition{ Key=“quantity”, Label=“Quantity”, Formatter=FormatType.Number, Sortable=true, Resizable=true},
  31.                     new YUIDataColumnDefinition{ Key=“amount”, Label=“Amount”, Formatter=FormatType.Currency, Sortable=true, Resizable=true},
  32.                     new YUIDataColumnDefinition{ Key=“title”, Label=“Title”, Sortable=true, Resizable=true}
  33.                 }
  34.             });
  35.  
  36.             _searchMetaItems.Add(“DataTableSearch”, new YUIGridViewModel()
  37.             {
  38.                 Title = “Search Grid”,
  39.                 Name = “Grid from ADO.NET DataTable source”,
  40.                 DivElement = “NEGrid”,
  41.                 DataSourceAction = @”/Search/GetData?name=DataTableSearch”,
  42.                 ColumnDefintions = new List<YUIDataColumnDefinition>
  43.                 {
  44.                     new YUIDataColumnDefinition(){ Key=“id”, Label=“ID”, Sortable=true, Resizable=true,Visible=false},
  45.                     new YUIDataColumnDefinition{ Key=“title”, Label=“Title”, Sortable=true, Resizable=true},
  46.                     new YUIDataColumnDefinition{ Key=“name”, Label=“Name”, Sortable=true, Resizable=true}
  47.                 }
  48.             });
  49.  
  50.         }
  51.  
  52.  
  53.         public object GetSearchData(string searchname)
  54.         {
  55.             if (“ObjectModelSearch”.Equals(searchname))
  56.             {
  57.                 return GetNEItems();
  58.             }
  59.             DataTable table = new DataTable();
  60.             table.TableName = “NEData”;
  61.             var column = new DataColumn(“id”, typeof(string));
  62.             table.Columns.Add(column);
  63.             column = new DataColumn(“title”, typeof(string));
  64.             table.Columns.Add(column);
  65.             column = new DataColumn(“name”, typeof(string));
  66.             table.Columns.Add(column);
  67.  
  68.             table.Rows.Add(“ct-0123”, “Ms.”, “Cathy Green”);
  69.             table.Rows.Add(“ct-1246”, “Mr.”, “John Meyer”);
  70.  
  71.             var result = GetDataAsDictionary(table);
  72.             return result;
  73.  
  74.         }
  75.  
  76.         private IEnumerable<SearchItemModel> GetNEItems()
  77.         {
  78.             var list = new List<SearchItemModel>(){
  79.             new SearchItemModel{id=“po-0167”, date=new DateTime(1980, 2, 24).ToShortDateString(), quantity=1, amount=4, title=“A Book About Nothing”},
  80.             new SearchItemModel {id=“po-0783”, date=new DateTime(1983, 3, 23).ToShortDateString(), quantity=10, amount=12.12345, title=“The Meaning of Life”},
  81.             new SearchItemModel {id=“po-0297”, date=new DateTime(1978, 11, 12).ToShortDateString(), quantity=12, amount=1.25, title=“This Book Was Meant to Be Read Aloud”},
  82.             new SearchItemModel {id=“po-1482”, date=new DateTime(1985,3,11).ToShortDateString(), quantity=6, amount=3.5, title=“Read Me Twice”}
  83.             };
  84.             return list;
  85.         }
  86.         private object GetDataAsDictionary(DataTable table)
  87.         {
  88.             var mainDictionary = new List<object>();
  89.  
  90.             foreach (DataRow row in table.Rows)
  91.             {
  92.                 var subDicitionary = new Dictionary<string, object>();
  93.                 foreach (DataColumn column in table.Columns)
  94.                 {
  95.                     subDicitionary[column.ColumnName] = row[column];
  96.                 }
  97.                 mainDictionary.Add(subDicitionary);
  98.             }
  99.             return mainDictionary;
  100.         }
  101.     }

 

In it’s constructor I have initialized two Grid View Models for the searches named ObjectModelSearch and DataTableSearch. The data for ObjectModelSearch is represented as a list of objects whereas for DataTableSearch as ADO.NET Datatable.

To display a YUI datatable I have created following exntension method that takes YUIGridViewModel as parameter.

Code Snippet
  1. public static class RenderGridExtensions
  2.     {
  3.         public static string RenderGrid(this HtmlHelper helper, YUIGridViewModel viewModel)
  4.         {
  5.             StringBuilder builder = new StringBuilder();
  6.             builder.Append(“<div id=\”” + viewModel.DivElement + “\”></div> “);
  7.             builder.Append(“<script type=\”text/javascript\”>  \n”);
  8.             builder.Append(” YAHOO.util.Event.addListener(window, \”load\”, function () {\n”);
  9.             builder.Append(” YAHOO.example.Basic = function () { \n”);
  10.             //Column Definitions
  11.                 builder.Append(”   var myColumnDefs = [\n”);
  12.                 builder.Append(String.Join(“,”, from p in viewModel.ColumnDefintions select p.ToString() ));
  13.                 builder.Append(”        ];\n”);
  14.             //***Column Definitions
  15.              builder.Append(“var myDataSource = new YAHOO.util.DataSource(\””+viewModel.DataSourceAction + “\”);\n”);
  16.              builder.Append(” myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;\n”);
  17.                  builder.Append(” myDataSource.responseSchema = { fields : [\n”);
  18.                  builder.Append(string.Join(“,”, (from colDef in viewModel.ColumnDefintions select (“\””+ colDef.Key + “\””)).ToArray()));                   
  19.                  builder.Append(”    ]};\n”);
  20.             builder.Append(” var myDataTable = new YAHOO.widget.DataTable(\”” + viewModel.DivElement +“\”, myColumnDefs, myDataSource, { caption: \”” + viewModel.Name + “\” });\n”);
  21.            
  22.             builder.Append(@”return {
  23.                    oDS: myDataSource,
  24.                    oDT: myDataTable
  25.                };”);
  26.             builder.Append(”    } ();\n”);
  27.             builder.Append(” });\n”);//end of addListerner
  28.             builder.Append(“</script>\n”);
  29.             var returnValue = builder.ToString();
  30.             return returnValue;
  31.         }
  32.     }

 

The Controller and View pages are :

Code Snippet
  1. public class SearchController : Controller
  2.     {
  3.  
  4.         public ActionResult Index(string name)
  5.         {
  6.             var srviewModel = new SearchRepository().GetSearchMeta(name);
  7.             return View(srviewModel);
  8.         }
  9.  
  10.         public JsonResult GetData(string name)
  11.         {            
  12.             if (String.IsNullOrEmpty(name))
  13.                 return null;
  14.  
  15.             var data = new SearchRepository().GetSearchData(name);
  16.             return Json(data,   JsonRequestBehavior.AllowGet);
  17.         }
  18.  
  19.     }

 

Code Snippet
  1. <%@ Page Title=”” Language=”C#” MasterPageFile=”~/Views/Shared/Site.Master” Inherits=”System.Web.Mvc.ViewPage<MvcApplicationWithYUI.Models.YUIGridViewModel>” %>
  2. <%@ Import Namespace=”MvcApplicationWithYUI.UIHelpers” %>
  3.  
  4. <asp:Content ID=”Content1″ ContentPlaceHolderID=”TitleContent” runat=”server”>
  5.     Index
  6. </asp:Content>
  7.  
  8. <asp:Content ID=”Content2″ ContentPlaceHolderID=”MainContent” runat=”server”>
  9.  
  10.    
  11.    <%= Html.RenderGrid(Model) %>
  12.   
  13. </asp:Content>

To get the data, YUI datatable’s source to the controller’s GetData action like http://localhost:49417/Search/GetData?name=ObjectModelSearch. The controller uses Search Reposiotory to get the data and sends back it in JSON format.

Sample project source code is attached ASPNETMVCControlForYUIDataTable