A practical guide to building an Asp.Net 8 MVC application that uses jQuery component DataTables.net. This is a continuation of article Part5.
1 ASP.NET8 using jQuery DataTables.net
I was evaluating the jQuery DataTables.net component [1] for usage in ASP.NET8 projects and created several prototype (proof-of-concept) applications that are presented in these articles.
1.1 Articles in this series
Articles in this series are:
- ASP.NET8 using DataTables.net – Part1 – Foundation
- ASP.NET8 using DataTables.net – Part2 – Action buttons
- ASP.NET8 using DataTables.net – Part3 – State saving
- ASP.NET8 using DataTables.net – Part4 – Multilingual
- ASP.NET8 using DataTables.net – Part5 – Passing additional parameters in AJAX
- ASP.NET8 using DataTables.net – Part6 – Returning additional parameters in AJAX
- ASP.NET8 using DataTables.net – Part7 – Buttons regular
- ASP.NET8 using DataTables.net – Part8 – Select rows
- ASP.NET8 using DataTables.net – Part9 – Advanced Filters
2 Final result
The goal of this article is to create a proof-of-concept application that demos DataTables.net component returning additional parameters in AJAX. Let us present the result of this article.
The point is, the component DataTables.net lets you pass some additional parameters to return in AJAX call to the client end. I am showing that data on the form. That data is passed from the back-end to the front-end in an AJAX call.
3 Client-side DataTables.net component
Here I will just show what the ASP.NET view using DataTables component looks like.
<!--
<partial name="_LoadingDatatablesJsAndCss" />
@{
<div class="text-center">
<h3 class="display-4">Employees table</h3>
</div>
<!-- Here is our table HTML element defined. JavaScript library Datatables
will do all the magic to turn it into interactive component -->
<table id="EmployeesTable01" class="table table-striped table-bordered ">
</table>
<div class="text-center">
<h6>Passed parameters</h6>
rParam1: <span id="rParam1"></span> <br />
rParam2: <span id="rParam2"></span>
</div>
}
<script>
document.addEventListener("DOMContentLoaded", InitializeDatatable);
function InitializeDatatable() {
$("#EmployeesTable01").dataTable({
processing: true,
paging: true,
info: true,
ordering: true,
searching: true,
search: {
return: true
},
autoWidth: true,
lengthMenu: [10, 15, 25, 50, 100],
pageLength: 10,
order: [[1, 'asc']],
serverSide: true,
ajax: {
"url": "@Url.Action("EmployeesDT", "Home")",
"type": "POST",
"datatype": "json",
"dataSrc": extractReturnParameters
},
columns: [
{
name: 'id',
data: 'id',
title: "Employee Id",
orderable: true,
searchable: false,
type: 'num',
visible: false
},
{
name: 'givenName',
data: "givenName",
title: "Given Name",
orderable: true,
searchable: true,
type: 'string',
visible: true
},
{
name: 'familyName',
data: "familyName",
title: "Family Name",
orderable: true,
searchable: true,
type: 'string',
visible: true
},
{
name: 'town',
data: "town",
title: "Town",
orderable: true,
searchable: true,
type: 'string',
visible: true
},
{
name: 'country',
data: "country",
title: "Country",
orderable: true,
searchable: true,
type: 'string',
visible: true
},
{
name: 'email',
data: "email",
title: "Email",
orderable: true,
searchable: true,
type: 'string',
visible: true
},
{
name: 'phoneNo',
data: "phoneNo",
title: "Phone Number",
orderable: false,
searchable: true,
type: 'string',
visible: true
}
]
});
function extractReturnParameters(json) {
let rParam1 = json.rParam1;
let rParam2 = json.rParam2;
setTimeout(() => doWorkWithParamters(rParam1, rParam2), 0);
return json.data;
}
function doWorkWithParamters(rParam1, rParam2) {
let span1 = document.getElementById('rParam1');
span1.innerHTML = rParam1;
let span2 = document.getElementById('rParam2');
span2.innerHTML = rParam2;
}
}
</script>
Note that in ajax property we used property dataSrc to pass a reference to JavaScript function extractReturnParameters that is going to extract returned parameters.
More about these properties can be found in the manual at [1]. The application here is just a proof of concept for ASP.NET environment.
4 Sample AJAX call
To better understand what is going on, I used Chrome DevTools to capture data returned back in a sample AJAX call.
Here is the AJAX call response:
{
"draw": 1,
"recordsTotal": 1000,
"recordsFiltered": 1000,
"data": [
{
"id": 459,
"givenName": "Abbe",
"familyName": "Crouse",
"town": "San Jerónimo",
"country": "Peru",
"email": "acrousecq@rakuten.co.jp",
"phoneNo": "590-568-7650"
},
{
"id": 854,
"givenName": "Abbe",
"familyName": "Geraldo",
"town": "Kornyn",
"country": "Ukraine",
"email": "ageraldonp@adobe.com",
"phoneNo": "346-510-7193"
},
{
"id": 726,
"givenName": "Abigael",
"familyName": "Davidai",
"town": "Rukaj",
"country": "Albania",
"email": "adavidaik5@blog.com",
"phoneNo": "750-275-0919"
},
{
"id": 256,
"givenName": "Adaline",
"familyName": "Waren",
"town": "Pruszcz Gdański",
"country": "Poland",
"email": "awaren73@netscape.com",
"phoneNo": "887-568-2194"
},
{
"id": 375,
"givenName": "Adelaide",
"familyName": "O'Dowgaine",
"town": "Fajsławice",
"country": "Poland",
"email": "aodowgaineae@networkadvertising.org",
"phoneNo": "304-825-5438"
},
{
"id": 145,
"givenName": "Adelheid",
"familyName": "Fer",
"town": "Tužno",
"country": "Croatia",
"email": "afer40@bravesites.com",
"phoneNo": "983-283-7629"
},
{
"id": 340,
"givenName": "Adrian",
"familyName": "Thunnercliffe",
"town": "Avelinha",
"country": "Portugal",
"email": "athunnercliffe9f@hibu.com",
"phoneNo": "819-620-4630"
},
{
"id": 585,
"givenName": "Ag",
"familyName": "Winchcombe",
"town": "Tinghir",
"country": "Morocco",
"email": "awinchcombeg8@istockphoto.com",
"phoneNo": "573-856-3366"
},
{
"id": 389,
"givenName": "Aggy",
"familyName": "Dacre",
"town": "Rosignol",
"country": "Guyana",
"email": "adacreas@china.com.cn",
"phoneNo": "941-421-7755"
},
{
"id": 464,
"givenName": "Aharon",
"familyName": "Wathall",
"town": "Mimbaan Timur",
"country": "Indonesia",
"email": "awathallcv@bbc.co.uk",
"phoneNo": "108-340-8110"
}
],
"rParam1": "My parameter",
"rParam2": 12345
}
I of course extracted only relevant data parts. Every better Web programmer should be able to understand what is going on from the above data.
5 Bug in DataTables.AspNet.Core library
Unfortunately, there is a bug in C#/.NET library DataTables.AspNet.Core related to returning parameters in an AJAX call. There is a workaround for that bug, and I am using it in this example. Here is Helper class that workaround that bug.
using DataTables.AspNet.AspNetCore;
using Newtonsoft.Json;
using System.Collections;
namespace Example06
{
public class DataTablesResponseHelper : DataTables.AspNet.AspNetCore.DataTablesResponse
{
protected DataTablesResponseHelper(int draw, string errorMessage) :
base(draw, errorMessage)
{
}
protected DataTablesResponseHelper(int draw, string errorMessage,
IDictionary<string, object> additionalParameters) :
base(draw, errorMessage, additionalParameters)
{
}
protected DataTablesResponseHelper(int draw, int totalRecords, int totalRecordsFiltered, object data) :
base(draw, totalRecords, totalRecordsFiltered, data)
{
}
protected DataTablesResponseHelper(int draw, int totalRecords, int totalRecordsFiltered,
object data, IDictionary<string, object> additionalParameters) :
base(draw, totalRecords, totalRecordsFiltered, data, additionalParameters)
{
}
public DataTablesResponseHelper(DataTablesResponse response) :
this(response.Draw, response.TotalRecords, response.TotalRecordsFiltered,
response.Data, response.AdditionalParameters)
{
}
private bool IsSuccessResponse()
{
return this.Data != null && string.IsNullOrWhiteSpace(this.Error);
}
public override string ToString()
{
using (StringWriter stringWriter = new StringWriter())
{
using (JsonTextWriter jsonTextWriter = new JsonTextWriter((TextWriter)stringWriter))
{
if (this.IsSuccessResponse())
{
jsonTextWriter.WriteStartObject();
jsonTextWriter.WritePropertyName(Configuration.Options.ResponseNameConvention.Draw, true);
jsonTextWriter.WriteValue(this.Draw);
jsonTextWriter.WritePropertyName(Configuration.Options.ResponseNameConvention.TotalRecords, true);
jsonTextWriter.WriteValue(this.TotalRecords);
jsonTextWriter.WritePropertyName(Configuration.Options.ResponseNameConvention.TotalRecordsFiltered, true);
jsonTextWriter.WriteValue(this.TotalRecordsFiltered);
jsonTextWriter.WritePropertyName(Configuration.Options.ResponseNameConvention.Data, true);
jsonTextWriter.WriteRawValue(this.SerializeData(this.Data));
if (this.AdditionalParameters != null)
{
foreach (KeyValuePair<string, object> additionalParameter in (IEnumerable<KeyValuePair<string, object>>)this.AdditionalParameters)
{
jsonTextWriter.WritePropertyName(additionalParameter.Key, true);
if (additionalParameter.Value is IEnumerable)
{
jsonTextWriter.WriteRawValue(SerializeData(additionalParameter.Value));
}
else
{
jsonTextWriter.WriteValue(additionalParameter.Value);
}
}
}
jsonTextWriter.WriteEndObject();
}
else
{
jsonTextWriter.WriteStartObject();
jsonTextWriter.WritePropertyName(Configuration.Options.ResponseNameConvention.Draw, true);
jsonTextWriter.WriteValue(this.Draw);
jsonTextWriter.WritePropertyName(Configuration.Options.ResponseNameConvention.Error, true);
jsonTextWriter.WriteValue(this.Error);
if (this.AdditionalParameters != null)
{
foreach (KeyValuePair<string, object> additionalParameter in
(IEnumerable<KeyValuePair<string, object>>) this.AdditionalParameters)
{
jsonTextWriter.WritePropertyName(additionalParameter.Key, true);
if (additionalParameter.Value is IEnumerable)
{
jsonTextWriter.WriteRawValue(SerializeData(additionalParameter.Value));
}
else
{
jsonTextWriter.WriteValue(additionalParameter.Value);
}
}
}
jsonTextWriter.WriteEndObject();
}
jsonTextWriter.Flush();
return stringWriter.ToString();
}
}
}
}
}
6 ASP.NET back-end processing
So, we are now at C#/.NET part, writing our ASP.NET code. Here is the solution I came up with. I will not pretend it is easy, it assumes a good understanding of libraries DataTables.AspNet.Core and System.Linq.Dynamic.Core.
Here is the code:
public IActionResult EmployeesDT(DataTables.AspNet.Core.IDataTablesRequest request)
{
try
{
IQueryable<Employee> employees = MockDatabase.MockDatabase.Instance.EmployeesTable.AsQueryable();
int totalRecordsCount = employees.Count();
var iQueryableOfAnonymous = employees.Select(p => new
{
id = p.Id,
givenName = p.FirstName,
familyName = p.LastName,
town = p.City,
country = p.Country,
email = p.Email,
phoneNo = p.Phone,
});
iQueryableOfAnonymous = FilterRowsPerRequestParameters(iQueryableOfAnonymous, request);
int filteredRecordsCount = iQueryableOfAnonymous.Count();
iQueryableOfAnonymous = SortRowsPerRequestParamters(iQueryableOfAnonymous, request);
iQueryableOfAnonymous = iQueryableOfAnonymous.Skip(request.Start).Take(request.Length);
var dataPage = iQueryableOfAnonymous.ToList();
var returnParameters = new Dictionary<string, object>()
{
{ "rParam1", "My parameter" },
{ "rParam2", 12345 }
};
var response = DataTablesResponse.Create(request, totalRecordsCount, filteredRecordsCount, dataPage, returnParameters);
var response2 = new DataTablesResponseHelper(response);
return new DataTablesJsonResult(response2, true);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
var response = DataTablesResponse.Create(request, "Error processing AJAX call on server side");
return new DataTablesJsonResult(response, false);
}
}
namespace Example06
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
DataTablesAspNetRegistration(builder);
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
}
public static void DataTablesAspNetRegistration(WebApplicationBuilder? builder)
{
if (builder == null) { throw new Exception("builder == null"); };
var options = new DataTables.AspNet.AspNetCore.Options()
.EnableRequestAdditionalParameters()
.EnableResponseAdditionalParameters();
builder.Services.RegisterDataTables(options);
}
}
}
7 Conclusion
The full example code project can be downloaded.
8 References
[1] https://datatables.net/
[21] ASP.NET8 using DataTables.net – Part1 – Foundation
https://www.codeproject.com/Articles/5385033/ASP-NET-8-Using-DataTables-net-Part1-Foundation
[22] ASP.NET8 using DataTables.net – Part2 – Action buttons
https://www.codeproject.com/Articles/5385098/ASP-NET8-using-DataTables-net-Part2-Action-buttons
[23] ASP.NET8 using DataTables.net – Part3 – State saving
https://www.codeproject.com/Articles/5385308/ASP-NET8-using-DataTables-net-Part3-State-saving
[24] ASP.NET8 using DataTables.net – Part4 – Multilingual
https://www.codeproject.com/Articles/5385407/ASP-NET8-using-DataTables-net-Part4-Multilingual
[25] ASP.NET8 using DataTables.net – Part5 – Passing additional parameters in AJAX
https://www.codeproject.com/Articles/5385575/ASP-NET8-using-DataTables-net-Part5-Passing-additi
[26] ASP.NET8 using DataTables.net – Part6 – Returning additional parameters in AJAX
https://www.codeproject.com/Articles/5385692/ASP-NET8-using-DataTables-net-Part6-Returning-addi
[27] ASP.NET8 using DataTables.net – Part7 – Buttons regular
https://www.codeproject.com/Articles/5385828/ASP-NET8-using-DataTables-net-Part7-Buttons-regula
[28] ASP.NET8 using DataTables.net – Part8 – Select rows
https://www.codeproject.com/Articles/5386103/ASP-NET8-using-DataTables-net-Part8-Select-rows
[29] ASP.NET8 using DataTables.net – Part9 – Advanced Filters
https://www.codeproject.com/Articles/5386263/ASP-NET8-using-DataTables-net-Part9-Advanced-Filte