Table of contents
Introduction
In this article I will show how you can refresh an HTML table content via AJAX. If you are using regular HTML tables, reloading the table is not a too complicated task. All you need to do is send an AJAX request to the server-side page, take the response from the server, and put it in the table. I believe that this is something that you have done a lot of times. There is a simple jQuery code that can do this:$("table#employees tbody").load("TableContent.aspx");
This call will send an AJAX call to the server-side page TableContent.aspx,
read the response that TableContent.aspx provides, and put it into the
body of the table. In the TableContent.aspx page, you will need to return
a valid HTML representing the set of rows that will be placed in the table. This works fine if you have a simple table where you can replace content with response from the server. However, if you have some advanced functionalities such as pagination, sorting, filtering, this might get more complicated. When you send a request to the server, you will need to include information about the current page and table state, and make sure that you have loaded proper content, selected the correct page number in the pagination, etc. If you have more advanced tables, it would be better to use some existing component where the refresh functionality is already built-in.
My choice is the jQuery DataTables plug-in. With the jQuery DataTables plug-in, you can take a plain table and add pagination, filtering, and sorting using a single JavaScript line of code:
var oEmployeesTable = $('table#employees').dataTable();
In this example is taken a plain HTML table with id "employees" and the
DataTables plug-in is applied to the table. This call will add pagination,
sorting, and filtering on the table.You can also easily create a fully AJAXified table using a slightly different call:
var oEmployeesTable = $('table#employees').dataTable({
"bServerSide": true,
"sAjaxSource": "DataContent.aspx"
});
In this case are set two parameters specifying that information will be taken
not from the HTML but from the server-side page DataContent.aspx.If you use DataTables to refresh the table content, you will need to execute a single line of JavaScript code:
oEmployeesTable.fnDraw();
This call will refresh the table content. In this article, I will show how
you can implement refresh functionality in the ASP.NET MVC application.Background
The jQuery DataTables plug-in is an excellent jQuery library that enables you to create fully AJAXified tables with minimal effort. To create an AJAXified table with the jQuery DataTables plug-in, you will need to implement the following elements:- Put an empty table in the HTML of the page. This empty table will define the
structure of the table (e.g., columns, styles, etc.). The table should have only
a heading in the
THEAD
element, empty body, and an optional footer in theTFOOT
element. Rows in the table are not required because they will be loaded via an AJAX call. - Create some server-side page that will provide rows to the table when it is called via AJAX. In this article will be implemented an ASP.NET MVC controller that will provide data rows to the table.
- Initiate the jQuery DataTables plug-in to load table content from the controller.
As you can see, the jQuery DataTables plugin has taken a plain table with no content, populated it with rows via an AJAX call, and added elements for sorting, filtering, and pagination. You will see in the following sections that the only thing that is needed are a few lines of JavaScript code to initialize this table, and convert it to the fully functional AJAXified table.
You can find detailed instructions about the integration of the jQuery DataTables plug-in with the ASP.NET MVC controller in the jQuery DataTables and ASP.NET MVC Integration - Part I article.
Once you implement the controller and initialize the jQuery DataTables plug-in, it will handle the complete interaction with the user. Each time the user changes the state of the table (e.g., change page using pagination, sort rows by column, or perform some search) the DataTables plug-in will send information to the controller and refresh the content of the table.
However, in some cases, you will need to refresh the table content manually. Refreshing the table content is a common requirement in many applications - some examples are:
- You might want to add a Refresh button that enables the user to refresh the data displayed in the table.
- You might want to implement some periodical refresh of table content to provide the latest data to the user (e.g., some live scores implementation).
- You might want to implement some kind of AJAXified search where the user enters search criterion in the form and you want to refresh data in the table without refreshing the whole page.
- You might want to add some master table or drop-down so when the user selects some value in the table or dropdown, you might want to refresh the dependent table.
- You might want to add some filter to the table where the user will type some text or select some value in the dropdown list, so you will need to refresh the table content to match the filter.
This is the third article in the series explaining how the jQuery DataTables plug-in can be integrated in an ASP.NET MVC web application. If you are not familiar with the integration of the DataTables plug-in with the ASP.NET MVC server-side code, you might want to read the first article in this series before you proceed.
Using the code
For illustrative purposes, we'll use a simple ASP.NET MVC web application to list the employees. The first thing you need to do is to create a standard ASP.NET Model-View-Controller structure. There are three steps required for this setup:- Creating the model classes that represent a data structure to be shown.
- Creating the controller class that will react on the user events.
- Creating the view that will render the data and create the HTML code that is sent to the browser window.
- jQuery library v1.4.4., containing the standard classes used by the DataTables plug-in.
- jQuery DataTables plug-in v1.7.5., including the optional DataTables CSS style-sheets used for applying the default styles on the page.
Model
Two classes that contain information about companies and employees need to be added in the example. The classes are shown in the following listing:public class Company
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Town { get; set; }
}
public class Employee
{
public int EmployeeID { get; set; }
public string Name { get; set; }
public string Position { get; set; }
public int CompanyID { get; set; }
}
The employees are connected to the companies via the CompanyID
property. These classes will be used to show information on the page.View
The view is used to render data on the server-side and to send HTML code to the browser. There's one layout page that is used to include all the necessary CSS and JavaScript files that are used on the page. This layout page is shown below:<!DOCTYPE html>
<html>
<head>
<title>Refreshing table content using a JQuery DataTables plugin</title>
<link href="@Url.Content("~/Content/dataTables/demo_page.css")" rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/Content/dataTables/demo_table.css")" rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/Content/dataTables/demo_table_jui.css")" rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/Content/themes/base/jquery-ui.css")" rel="stylesheet" type="text/css" media="all" />
<link href="@Url.Content("~/Content/themes/smoothness/jquery-ui-1.7.2.custom.css")"rel="stylesheet" type="text/css" media="all" />
<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.dataTables.min.js")" type="text/javascript"></script>
@RenderSection("head", required: false)
</head>
<body id="dt_example">
<div id="container">
@RenderBody()
</div>
</body>
</html>
The layout page has two sections that can be populated on the page:- head section where the JavaScript calls from the page will be injected,
- body section that enables the page that uses this layout page to inject code to the page that will be shown on the page.
Case 1 - Adding the "Refresh" button
In the first example, I will show how you can add a Refresh button that will reload the content of the table when the user press it. An example is shown on the following figure:As described above, we would need an empty table that holds table data and a refresh button. The body of the page is shown in the following listing:
<button id="Refresh" type="button">Refresh<button/>
<table id="employees" class="display">
<thead>
<tr>
<th>ID</th>
<th>Employee</th>
<th>Position</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
Once we have prepared static HTML, we would need to initialize the DataTables
plug-in, and attach a click handler to the refresh button that will redraw the table.
Example of code is shown in the following listing. @section head{
<script language="javascript" type="text/javascript">
$(document).ready(function () {
var oEmployeesTable = $('#employees').dataTable({
"bJQueryUI": true,
"bServerSide": true,
"sAjaxSource": "MasterDetailsAjaxHandler"
});
$("#Refresh").click(function (e) {
oEmployeesTable.fnDraw();
});
});
</script>
}
This code should be placed in the head section of the view. The first
statement in the document ready function applies the DataTables plug-in on the
employees table, specified that it will work in server-side mode (meaning
that on each user action, the plug-in will ask the server-side page to provide new data
that should be displayed), defines that URL that will be called via an AJAX call is
MasterDetailsAjaxHandler
- this is the path of the controller that will provide
the content of the table. The bJQueryUI
flag is not mandatory - it is just used to apply
jQuery UI styles to the table.When the table is initialized, in the code is added a click handler for the refresh button that will draw the content of the table. On each draw call, the DataTables plug-in will call the server-side page again and take the new set of data which will be loaded in the table body.
The controller that provides content is described in the section controller.
Case 2 - Refreshing table content periodically
If you do not want to force the user to refresh the table, you can add a function that will be periodically called to refresh the table. The code is very similar to the code shown in case 1. The only difference is that the refresh button is not needed and instead of the click handler is used a set interval function as shown in the following listing:@section head{
<script language="javascript" type="text/javascript">
var oEmployeesTable = null;
function refresh() {
if (oEmployeesTable != null)
oEmployeesTable.fnDraw();
}
setInterval("refresh()", 1000);
$(document).ready(function () {
oEmployeesTable = $('#employees').dataTable({
"bJQueryUI": true,
"bServerSide": true,
"sAjaxSource": "/Home/MasterDetailsAjaxHandler"
});
});
</script>
}
In the global scope are defined references to the employees table, function
that refreshes the table, and a timer that calls the refresh function each second. Then, in
the document ready handler, the employees table is initialized with the DataTables plug-in
and a global reference is set.As a result, this table will be automatically reloaded each second. A trace of the AJAX calls that are sent to the server is shown on the figure below:
Note that the reference to the employees table, function and interval call are placed in the global scope and not in the body of the document ready handler, because these function references will be lost once the document ready handler finishes.
Case 3 - Refreshing table content when parent row is selected
In this example I will show how you can refresh the content of the dependent table (employees) when a company is selected in the master table. In this case, we will have two tables - a parent table containing the list of companies and a child table containing the list of employees. An example of that kind of page is shown in the following figure:When the user selects the company in the master table, in the child table will be shown the employees that belong to this company. The body of the page is shown in the following listing:
<div id="demo">
<table id="companies" class="display">
<thead>
<tr>
<th>Company name</th>
<th>Address</th>
<th>Town</th>
</tr>
</thead>
<tbody>
<tr id="0" class="masterlink">
<td>Emkay Entertainments</td>
<td>Nobel House, Regent Centre</td>
<td>Lothian</td>
</tr>
<tr id="1" class="masterlink">
<td>The Empire</td>
<td>Milton Keynes Leisure Plaza</td>
<td>Buckinghamshire</td>
</tr>
<tr id="2" class="masterlink">
<td>Asadul Ltd</td>
<td>Hophouse</td>
<td>Essex</td>
</tr>
<tr id="3" class="masterlink">
<td>Ashley Mark Publishing Company</td>
<td>1-2 Vance Court</td>
<td>Tyne & Wear</td>
</tr>
<tr id="4" class="masterlink">
<td>MuchMoreMusic Studios</td>
<td>Unit 29</td>
<td>London</td>
</tr>
<tr id="5" class="masterlink">
<td>Audio Records Studios</td>
<td>Oxford Street</td>
<td>London</td>
</tr>
</tbody>
</table>
<table id="employees" class="display">
<thead>
<tr>
<th>ID</th>
<th>Employee</th>
<th>Position</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
The first table with companies contains a list of five companies, and the
employees table is empty. The employees table will be populated with AJAX
JavaScript calls. Each row in the companies table contains the ID of the company
- this information will be used to load the employees for the selected company.The head section holds the JavaScript code that initializes and connects these two tables. The JavaScript initialization code is shown in the following listing:
<script language="javascript" type="text/javascript">
$(document).ready(function () {
/* Initialize master table - optionally */
var oCompaniesTable = $('#companies').dataTable({ "bJQueryUI": true });
/* Highlight selected row - optionally */
$("#companies tbody").click(function (event) {
$(oCompaniesTable.fnSettings().aoData).each(function () {
$(this.nTr).removeClass('row_selected');
});
$(event.target.parentNode).addClass('row_selected');
});
var MasterRecordID = null;
var oEmployeesTable = $('#employees').dataTable({
"sScrollY": "100px",
"bJQueryUI": true,
"bServerSide": true,
"sAjaxSource": "MasterDetailsAjaxHandler",
"bProcessing": true,
"fnServerData": function (sSource, aoData, fnCallback) {
aoData.push({ "name": "CompanyID", "value": MasterRecordID });
$.getJSON(sSource, aoData, function (json) {
fnCallback(json)
});
}
});
$(".masterlink").click(function (e) {
MasterRecordID = $(this).attr("id");
oEmployeesTable.fnDraw();
});
});
</script>
The first two statements are optional. The first statement initializes the
companies table with the jQuery DataTables plug-in in order to add pagination,
filtering, and sorting functionality (this is not required for the parent-child
relationship between the tables because the parent table can be a plain table).
The second statement adds the row_selected
class on the selected
row in the parent table. This is also not required, but it's useful to highlight
a company whose employees are shown in the child table.A local variable
MasterRecordID
is used to hold the ID of the
currently selected company. The fourth statement initializes the child employees
table. Most of the settings are optional and do not affect the parent-child
configuration because the only relevant statements in the initialization are:- Server-side processing configuration implemented using the
bServerSide
andsAjaxSource
parameters, fnServerData
method used to inject the ID of the selected company into the AJAX call sent to the server-side. This method is used to add the additional parameter calledCompanyID
with the value of theMasterRecordID
variable to the AJAX call sent to the server-side.
Case 4 - Refreshing table when form is posted
A common scenario when you might need to reload a table is you post some form content via AJAX and you want to reload the table to refresh the rows. As an example, you might add some add form in the page, and when a new item is successfully added, you might want to reload the table content. An example of that kind of form is shown on the following figure.In order to create that kind of functionality, we would need to add a button and a dialog to the view. The view body looks like the following HTML code:
<div id="dialog" title="Add new item">
<label for="name">Title</label>
<input type="text" id="name" name="name" value="" />
<button type="button" id="save">Save</button>
</div>
<button id="add">Add</button>
<table id="employees" class="display">
<thead>
<tr>
<th>ID</th>
<th>Employee</th>
<th>Position</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
A dialog is added in HTML where are placed text input for the new item and
a save button. Also a button that is used to open a dialog is added above the
table. The table that is enhanced with the DataTables plug-in is the same as in the previous
cases.Now we would need to refresh the table after the Save button posts request to the server-side. In this case, we would need to call the function
fnDraw
in the
success handler of the AJAX call. An example of that kind of JavaScript code is
shown in the following listing: var oEmployeesTable = $('#employees').dataTable({
"bJQueryUI": true,
"bServerSide": true,
"sAjaxSource": "/Home/MasterDetailsAjaxHandler"
});
$("#dialog").dialog({ autoOpen: false });
$("#add").click(function (e) {
$("#dialog").dialog("open");
});
$("#save").click(function (e) {
e.preventDefault();
e.stopPropagation();
$.ajax({
url: "/Home/AddItem",
data: {
name: function(){$("#name").val();}
},
success: function () {
$("#dialog").dialog("close");
oEmployeesTable.fnDraw();
}
});
});
The first part is a standard initialization of DataTable as in the previous
examples. Then the dialog is initialized (it is not automatically opened), on the
Add
button is added a handler that opens a dialog, and on the save button is added a
handler that posts AJAX requests to the server-side page and sends a name that is
entered. In the success handler of the AJAX call, the dialog is closed and the table is
redrawn.As a result, each time you post the form, you will see that the table is reloaded.
Case 5 - Filtering using form elements
Implementation of other cases of usage are similar to the three approaches described above. You can easily add some select dropdown, check boxes, or even the entire form that will refresh the table content.All you will need to do is attach an event handler that will call the
fnDraw
function and inject parameters you want to send to the controller via an AJAX call
in the fnServerData
settings in the initialization. When fnDraw
is called,
the DataTables plug-in will send a new AJAX request, pass the parameters defined
in fnServerData
, accept results, and put them in the table. Therefore
I will not
show implementations of other cases because they are very similar. If you want to refresh using the form elements that are directly related to the columns in the table, there is an easier option. You can use the jQuery DataTables ColumnFilter add-on that can add form filters to the DataTable. You can see how this form filter works if you add the filters in the columns or in the separate form. This filter can be applied either on the row data that are already populated in the table or on the AJAXified table. With this plug-in, you do not need to implement event handlers and an AJAX request. All you need to do is to apply a column filter plug-in on the DataTables plug-in configured in the server side mode. An example of the code that initializes the columnfilter plug-in with DataTables in the server-side processing mode is shown in the following listing:
$('#employees').dataTable({
"bJQueryUI": true,
"bServerSide": true,
"sAjaxSource": "MasterDetailsAjaxHandler"
}).columnFilter({
aoColumns: [ { type: "select"},
{ type: "text" },
{ type: "text" },
{ type: "text" }
]
});
This example adds a select list for filtering by first column and text boxes
for filtering by other columns. If the DataTables plug-in is initialized in server-side mode, filters will also
be sent to the server-side via AJAX calls.
You can see more details about the column filter configuration on the
jQuery DataTables ColumnFilter site.The last required part of the example is a controller that will handle the requests.
Controller
The controller handles the request sent from the browser and provides view/data that will be shown in the browser. Here, the controller has two methods that handle a request:Load
method that returns the view page when the page is loaded,- Employees AJAX handler that returns the employees when jQuery DataTables sends the AJAX request. This action will return employees data when the jQuery DataTables plug-in wants to refresh the content, therefore it should accept standard parameters (current page, number of items per page, etc.) that define the current view state of the table. However, to support refreshing a table for a selected company (case 3 above), in this controller action is added the company ID parameter that will be used to additionally filter employees by company.
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
The second controller method is crucial - it returns the employees for the
employees table. This method is shown in the following listing:public class HomeController : Controller
{
public ActionResult MasterDetailsAjaxHandler(
JQueryDataTableParamModel param, int? CompanyID)
{
var employees = DataRepository.GetEmployees();
//"Business logic" method that filters employees by the employer id
var companyEmployees = (from e in employees
where (CompanyID == null || e.CompanyID == CompanyID)
select e).ToList();
//UI processing logic that filter company employees by name and paginates them
var filteredEmployees = (from e in companyEmployees
where (param.sSearch == null ||
e.Name.ToLower().Contains(param.sSearch.ToLower()))
select e).ToList();
var result = from emp in filteredEmployees.Skip(
param.iDisplayStart).Take(param.iDisplayLength)
select new[] { Convert.ToString(emp.EmployeeID),
emp.Name, emp.Position };
return Json(new
{
sEcho = param.sEcho,
iTotalRecords = companyEmployees.Count,
iTotalDisplayRecords = filteredEmployees.Count,
aaData = result
},
JsonRequestBehavior.AllowGet);
}
}
The name of the method must match the sAjaxSource
parameter set
in the employees data table. This method accepts an object that encapsulates the
parameters sent from the DataTables plug-in (current page, sort direction,
number of items that should be displayed per page, etc.) More details about the
server side processing parameters can be found in the
first article in this series. Besides this parameter, an additional
parameter called CompanyID
is added in the method signature. This
parameters is relevant only for case three and it is used to filter employees by
the selected company ID. The name of this parameter must match the name of the
parameter that is added in the fnServerData
function in case 3.
The other code in the body of the method just filters the employee data and
returns it in JSON format as it is expected by the jQuery DataTables plug-in.
More details about the server-side configuration can be found in the
first article in this series. Summary
This article shows how you can easily reload table content in ASP.NET MVC using the jQuery DataTables plug-in. Minimal code is required on the client-side, and on the server-side, we need standard processing functions. This plug-in allows you to create an effective, AJAXified, Web 2.0 interface with minimal effort and straightforward implementation guidelines. You can download the example project implemented in ASP.NET MVC here.You might also be interested in the other articles in this series showing:
- How to implement server-side processing in ASP.NET MVC with the jQuery DataTables plug-in
- How to implement a fully editable table in ASP.NET MVC with jQuery DataTables and several jQuery plug-ins that enable complete data management functionality.
0 comments:
Post a Comment