ASP.NET MVC4 and Single Page Application

A new Nuget package is provided by Microsoft for developing Single Page Application.

What is Single Page Application? Traditionally, the website/web applications have been using multiple pages to provide the intended functionalities. For e.g. an e-commerce web application might have Catalog, Cart and Payment pages.  This model of using multiple pages results in common problems of page reloads and UI flickering on navigation between pages. To address these problems, a single web page can be enhanced using AJAX and DOM manipulation to provide a similar user experience as in desktop applications.

How to decide between developing typical web applications vs Single Page applications?

Some points to consider are:

  1. Minimize page reloads and flickering on page navigation.
  2. To provide look and feel as well as user experience similar to desktop applications.

To read more about Single Page Application read the MSDN article Single-page Interface and AJAX Patterns.

Let see how to get started with ASP.NET MVC 4 SPA template.

Single Page Application template is not provided through MVC 4 beta installer but through Nuget package. See details on NuGet pacakge at http://nuget.org/packages/SinglePageApplication.

To install the template, I have opened a VS2010 solution (yuk, you need to open a solution before installing the package, otherwise Package Manger console throws “Install-Package : The current environment doesn’t have a solution open.”) and run command Install-Package SinglePageApplication

On creating a new ASP.NET MVC 4 web application project, you will get the following dialog to select the template to be used.

image

 

Major differences as compared to typical ASP.NET MVC project.

  1. New Javascript libraries to support SPA. knockout-2.0.0.js, upshot.js, nav.js, native.history.js and modernizer development version from Microsoft.
  2. The controller should derive from DataController which derives from ApiController.

After creating the project, to add functionalities you have to do following steps:

1. Add a model class

Here is the TodoItem model class.

using System;

using System.Collections.Generic;

using System.ComponentModel.DataAnnotations;

using System.Linq;

 

namespace MvcApplication7.Models

{   

 

    public class TodoItem

    {

        public int TodoItemId { get; set; }

        [Required]

        public string Title { get; set; }

        public bool IsDone { get; set; }

    }

}

2. Add controller by invoking add controller action on the Controllers folder. It opens a dialog, select in Template “Single Page Application with read/write actions and views, using Entity Framework”, select the Model class and select the context class or to create new context class select the option “<New data context…>”.

image

 

On adding controller, Visual Studio will generate code for Controller, Views, and JavaScript for this model.

The generated controller class looks like this:

using System.Linq;

using System.Web.Http;

using System.Web.Http.Data.EntityFramework;

 

namespace MvcApplication7.Controllers

{

    public partial class MvcApplication7Controller : DbDataController<MvcApplication7.Models.MvcApplication7Context>

    {

        public IQueryable<MvcApplication7.Models.TodoItem> GetTodoItems() {

            return DbContext.TodoItems.OrderBy(t => t.TodoItemId);

        }

 

        public void InsertTodoItem(MvcApplication7.Models.TodoItem entity) {

            InsertEntity(entity);

        }

 

        public void UpdateTodoItem(MvcApplication7.Models.TodoItem entity) {

            UpdateEntity(entity);

        }

 

        public void DeleteTodoItem(MvcApplication7.Models.TodoItem entity) {

            DeleteEntity(entity);

        }

    }

}

Under Views, Tasks folder is added with Index.cshtml, _Editor.cshtml, _Grid.cshtml and _Paging.cshtml views in it.

Index.cshtml

@{

    ViewBag.Title = "TodoItems";

    Layout = "~/Views/Shared/_SpaLayout.cshtml";

}

 

<div data-bind="visible: editingTodoItem">

    @Html.Partial("_Editor")

</div>

 

<div data-bind="visible: !editingTodoItem()">

    @Html.Partial("_Grid")

</div>

 

<div class="message-info message-success" data-bind="flash: { text: successMessage, duration: 5000 }"></div>

<div class="message-info message-error" data-bind="flash: { text: errorMessage, duration: 20000 }"></div>

 

 

<script type="text/javascript" src="@Url.Content("~/Scripts/TodoItemsViewModel.js")"></script>

 

<script type="text/javascript">

    $(function () {

        upshot.metadata(@(Html.Metadata<MvcApplication7.Controllers.MvcApplication7Controller>()));

 

        var viewModel = new MyApp.TodoItemsViewModel({

            serviceUrl: "@Url.Content("~/api/MvcApplication7")"

        });

        ko.applyBindings(viewModel);

    });

</script>

_Editor.cshtml

<h2>Edit TodoItem</h2>

 

<form data-bind="with: editingTodoItem, validate: validationConfig">

    <p data-bind="visible: TodoItemId">

        TodoItemId:

        <b data-bind="text: TodoItemId"></b>

    </p>

    <p>

        Title:

        <input name="Title" data-bind="value: Title, autovalidate: true" />

        <span class="error" data-bind="text: Title.ValidationError"></span>

    </p>

    <p>

        IsDone:

        <input name="IsDone" type="checkbox" data-bind="checked: IsDone, autovalidate: true" />

        <span class="error" data-bind="text: IsDone.ValidationError"></span>

    </p>

    <p>

        <button type="submit" data-bind="enable: CanSave">Save</button>

        <a href="#" data-bind="visible: TodoItemId, click: $parent.deleteTodoItem">Delete</a>

        <a href="#" data-bind="click: $parent.showGrid">Back to grid</a>

        <a href="#" data-bind="visible: IsUpdated, click: $parent.revertEdit">Undo changes</a>

    </p>

</form>

 

_Grid.cshtml

<h2>TodoItems (<span data-bind="text: paging.totalItems"></span>)</h2>

 

<div data-bind="visible: paging.totalItems() > 0">

    @Html.Partial("_Paging")

 

    <table>

        <thead>

            <tr>

                <th>Title</th>

                <th>IsDone</th>

            </tr>

        </thead>

        <tbody data-bind="foreach: todoItems">

            <tr data-bind="click: $parent.editTodoItem" style="cursor: pointer">

                <td data-bind="text: Title"></td>

                <td data-bind="text: IsDone"></td>

            </tr>

        </tbody>

    </table>

</div>

 

<p><button data-bind="click: createTodoItem">Create TodoItem</button></p>

 

_Paging.cshtml

<p data-bind="with: paging">

    Page <b data-bind="text: pageIndex"></b> 

    of <b data-bind="text: totalPages"></b>

    | Show <select data-bind="options: [5, 10, 20, 50], value: pageSize"></select> per page

 

    <a href="#" data-bind="click: moveFirst, visible: canMovePrev">&laquo; First</a>

    <a href="#" data-bind="click: movePrev, visible: canMovePrev">&laquo; Prev</a>

    <a href="#" data-bind="click: moveNext, visible: canMoveNext">Next &raquo;</a>

    <a href="#" data-bind="click: moveLast, visible: canMoveNext">Last &raquo;</a>

</p>

Under Scripts folder, TodoItemsViewModel.js is added.

TodoItemsViewModel.js

/// <reference path="_references.js" />

 

(function (window, undefined) {

    // Define the "MyApp" namespace

    var MyApp = window.MyApp = window.MyApp || {};

 

    // TodoItem class

    var entityType = "TodoItem:#MvcApplication7.Models";

    MyApp.TodoItem = function (data) {

        var self = this;

 

        // Underlying data

        self.TodoItemId = ko.observable(data.TodoItemId);

        self.Title = ko.observable(data.Title);

        self.IsDone = ko.observable(data.IsDone);

        upshot.addEntityProperties(self, entityType);

    }

 

    // TodoItemsViewModel class

    MyApp.TodoItemsViewModel = function (options) {

        var self = this;

 

        // Private properties

        var dataSourceOptions = {

            providerParameters: { url: options.serviceUrl, operationName: "GetTodoItems" },

            entityType: entityType,

            bufferChanges: true,

            mapping: MyApp.TodoItem

        };

        var gridDataSource = new upshot.RemoteDataSource(dataSourceOptions);

        var editorDataSource = new upshot.RemoteDataSource(dataSourceOptions);

 

        // Data

        self.todoItems = gridDataSource.getEntities();

        self.editingTodoItem = editorDataSource.getFirstEntity();

        self.successMessage = ko.observable().extend({ notify: "always" });

        self.errorMessage = ko.observable().extend({ notify: "always" });

        self.paging = new upshot.PagingModel(gridDataSource, {

            onPageChange: function (pageIndex, pageSize) {

                self.nav.navigate({ page: pageIndex, pageSize: pageSize });

            }

        });

        self.validationConfig = $.extend({

            resetFormOnChange: self.editingTodoItem,

            submitHandler: function () { self.saveEdit() }

        }, editorDataSource.getEntityValidationRules());

 

        // Client-side navigation

        self.nav = new NavHistory({

            params: { edit: null, page: 1, pageSize: 10 },

            onNavigate: function (navEntry, navInfo) {

                self.paging.moveTo(navEntry.params.page, navEntry.params.pageSize);

 

                // Wipe out any old data so that it is not displayed in the UI while new data is being loaded 

                editorDataSource.revertChanges();

                editorDataSource.reset();

 

                if (navEntry.params.edit) {

                    

                    if (navEntry.params.edit == "new") {

                        // Create and begin editing a new todoItem instance

                        editorDataSource.getEntities().push(new MyApp.TodoItem({}));

                    } else {

                        // Load and begin editing an existing todoItem instance

                        editorDataSource.setFilter({ property: "TodoItemId", value: Number(navEntry.params.edit) }).refresh();

                    }

                } else {

                    // Not editing, so load the requested page of data to display in the grid

                    gridDataSource.refresh();

                }

            }

        }).initialize({ linkToUrl: true });

 

        // Public operations

        self.saveEdit = function () {

            editorDataSource.commitChanges(function () {

                self.successMessage("Saved TodoItem").errorMessage("");

                self.showGrid();

            });

        }

        self.revertEdit = function () { editorDataSource.revertChanges() }

        self.editTodoItem = function (todoItem) { self.nav.navigate({ edit: todoItem.TodoItemId() }) }

        self.showGrid = function () { self.nav.navigate({ edit: null }) }

        self.createTodoItem = function () { self.nav.navigate({ edit: "new" }) }

        self.deleteTodoItem = function (todoItem) {

            editorDataSource.deleteEntity(todoItem);

            editorDataSource.commitChanges(function () {

                self.successMessage("Deleted TodoItem").errorMessage("");

                self.showGrid();

            });

        };

 

        // Error handling

        var handleServerError = function (httpStatus, message) {

            if (httpStatus === 200) {

                // Application domain error (e.g., validation error)

                self.errorMessage(message).successMessage("");

            } else {

                // System error (e.g., unhandled exception)

                self.errorMessage("Server error: HTTP status code: " + httpStatus + ", message: " + message).successMessage("");

            }

        }

        

        gridDataSource.bind({ refreshError: handleServerError, commitError: handleServerError });

        editorDataSource.bind({ refreshError: handleServerError, commitError: handleServerError });

    }

})(window);

 

Mobile Enabled Razor View Engine

Today I was trying to add jQuery Mobile to my project. To start with I found Scott Hanselman’s blog entry

A Better ASP.NET MVC Mobile Device Capabilities ViewEngine . I used the code by deriving the RazorViewEngine. My class looks like this:

 

WebEnabledViewEngine
  1. public class WebEnabledViewEngine : RazorViewEngine
  2.     {
  3.         public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
  4.         {
  5.             ViewEngineResult result = null;
  6.             var request = controllerContext.HttpContext.Request;
  7.  
  8.             // Avoid unnecessary checks if this device isn't suspected to be a mobile device
  9.             if (request.Browser.IsMobileDevice)
  10.             {
  11.                 result = base.FindView(controllerContext, "Mobile/" + viewName, masterName, useCache);
  12.             }
  13.  
  14.             //Fall back to desktop view if no other view has been selected
  15.             if (result == null || result.View == null)
  16.             {
  17.                 result = base.FindView(controllerContext, viewName, masterName, useCache);
  18.             }
  19.  
  20.             return result;
  21.         }
  22.     }

 

Apparently Scott has marked his blog entry as broken. The issue I was facing was that my jQuery Mobile site  was running good but the desktop version failed to load script files. So I end up adding following routing entry and now both version work’s good.

 

Global.asax.cs
  1. routes.IgnoreRoute("{*alljs}", new { alljs = @".*\.js(/.*)?" });

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