Working modal with datatables example, need help with sorting.
Working modal with datatables example, need help with sorting.
Ironwil616
Posts: 50Questions: 0Answers: 0
The project I'm working on is using ASP.NET MVC 3 and C#.
I'm trying to have a modal popup with hour details from a given project listed. The idea is to allow users to edit, delete, and add hours to a project. Everything is working fine so far, except for the sorting. When I click on a header, the arrow changes position as if a sort is taking place, but nothing else happens in the table. The modal is activated by clicking on a element in the projects list. The modal itself is a PartialView, loaded after the data is fetched from the server. Here's the code that creates the modal (I'll post all related code in case anyone else needs to get something like this running):
[code]
$(".hours").click(function() { // ".hours" are the cells with the hours input textboxes.
var projectID = $(this).attr("projectID");
$.ajax({
"url": '<%: Url.Action("HourDetailsList") %>', // This is a code snippet that generates the URL.
data: ({ projectID: projectID }),
beforeSend: function( xhr ) {
$("#HourDetailsActivityIndicator").show();
},
"success": function(result){
$("#HourDetailsListContainer").html(result); // This is the ID of the modal. The PartialView content is loaded into it.
$("#HourDetailsActivityIndicator").hide();
}
});
});[/code]
This is the server Controller code that generates the PartialView with datatables in it:
[code]
// MVC way of annotating POST/GET operations on a Controller method. Requires MVC 2 or higher.
[HttpGet]
public ActionResult HourDetailsList(int projectID)
{
string projectName = service.GetProjectNameByProjectID(projectID);
// "HourDetails" is a user-defined class. I'm using C#'s shorthand for initializing the object.
HourDetails hourDetails = new HourDetails
{
ProjectID = projectID, ProjectName = projectName , UserID = loggedInUser.ID
};
// "s_HourDetail" is the database table name for this object. I'm using LINQ to SQL ORM for data access.
List s_hourDetails = service.GetHourDetailsByProjectID(projectID)
.Where(hd => hd.UserID == loggedInUser.ID).ToList();
foreach (s_HourDetail hd in s_hourDetails)
{
hourDetails.HourDetailItems.Add(new HourDetailItem
{
HourDetailID = hd.ID,
UserID = hd.UserID,
Username = tpsService.GetNameByUserID(hd.UserID),
EntryDate = hd.Date,
Hours = hd.Hours
});
}
return View(hourDetails);
}
[/code]
Here's the complete View code that renders the PartialView with datatables in it: Some of this will doubtlessly look like jibberish to people that don't use C#/ASP.NET MVC. The odd naming I have for the repeated values in the foreach loop is to allow the POST method I'll list at the end to bind the values in this PartialView to a generic List of user-defined objects. Saves a lot of code on converting types and querying the server variables. In the "fnDrawCallback" function I'm adding two table rows. I need them to appear in a certain location relative to the rows created in the loop, and if I don't add them this way, they appear at the top of the table, even if in the code they're listed afterwards. This is probably due to the fact that it takes a few microseconds to render the rows out in the loop, and they're already created.
[code]
$(function()
{
$("#HourDetailsList").dataTable({
"sDom": 'rtlip',
"bSort": true,
"aaSorting": [[2, 'desc']],
"iDisplayLength": 5,
"aLengthMenu": [[5, 10, 20, 30, -1], [5, 10, 20, 30, "All"]],
"aoColumnDefs": [
{ "sType": "us_date", "aTargets": [2] }
],
"fnInfoCallback": function (oSettings, iStart, iEnd, iMax, iTotal, sPre)
{
if (iEnd == 0) return "0 records";
return iStart + " to " + iEnd + " of " + iTotal;
},
"fnDrawCallback": function() {
$("#HourDetailsList tr").eq(1).before('Add Hours<%: Html.TextBox("hourDetails.NewHourDetails.Hours", null, new { @class = "hoursInput" })%><%: Html.TextBox("hourDetails.NewHourDetails.EntryDate", null, new { @class = "datepicker" }) %>');
$("#HourDetailsList tr:last").after('');
initDatepicker();
}
});
// Open this PartialView as a modal dialog box.
$( "#HourDetailsModal" ).dialog({
modal: true,
resizable: false,
width: 500
});
$(".hoursInput").numeric(); // Make sure only numbers appear using the numeric plugin.
});
<% using (Html.BeginForm()) { %>
<%: Html.Hidden("hourDetails.ProjectID", Model.ProjectID) %>
<%: Html.Hidden("hourDetails.UserID", Model.UserID) %>
Hours
Date
Delete
<% int index = 0;
foreach (var hourDetail in Model.HourDetailItems) { %>
<%: Html.DisplayFor(m => hourDetail.Username) %>
<%: Html.Hidden("hourDetails.HourDetailItems[" + index.ToString() + "].HourDetailID",
hourDetail.HourDetailID) %>
<%: Html.TextBox("hourDetails.HourDetailItems[" + index.ToString() + "].Hours",
hourDetail.Hours, new { @class = "hoursInput" })%>
<%: Html.TextBox("hourDetails.HourDetailItems[" + index.ToString() + "].EntryDate",
hourDetail.EntryDate.ToShortDateString(), new { @class = "datepicker" }) %>
<%: Html.CheckBox("hourDetails.HourDetailItems[" + index.ToString() + "].Delete",
false, new { @class = "deleteHours" })%>
<% index++; } %>
<% } %>
[/code]
Everything works exactly as I expected, with the exception that sorting doesn't work at all. The user interface updates with the arrow at the correct column and in the correct direction, but the rows don't move to a newly sorted position. So far I have no idea why this occurs, except that maybe jQuery UI's dialog modal does something that interferes with the sorting. I'm just grasping at straws there. This next bit of code has nothing to do with the quest, but I'm including it for completeness as I said, in case anyone else is trying to get something like this to work.
[code]
// Method has the same name as the GET version, but the annotation below directs which to call.
[HttpPost]
public ActionResult HourDetailsList(HourDetails hourDetails)
{
// Usually in ASP.NET the above method argument would be of type "FormCollection", but I'm able to map the PartialView
// directly to my Model, and access the properties of the object and its collection of items just like any other object.
service.EditProjectHours(hourDetails);
return RedirectToAction("Index");
}
[/code]
I'm trying to have a modal popup with hour details from a given project listed. The idea is to allow users to edit, delete, and add hours to a project. Everything is working fine so far, except for the sorting. When I click on a header, the arrow changes position as if a sort is taking place, but nothing else happens in the table. The modal is activated by clicking on a element in the projects list. The modal itself is a PartialView, loaded after the data is fetched from the server. Here's the code that creates the modal (I'll post all related code in case anyone else needs to get something like this running):
[code]
$(".hours").click(function() { // ".hours" are the cells with the hours input textboxes.
var projectID = $(this).attr("projectID");
$.ajax({
"url": '<%: Url.Action("HourDetailsList") %>', // This is a code snippet that generates the URL.
data: ({ projectID: projectID }),
beforeSend: function( xhr ) {
$("#HourDetailsActivityIndicator").show();
},
"success": function(result){
$("#HourDetailsListContainer").html(result); // This is the ID of the modal. The PartialView content is loaded into it.
$("#HourDetailsActivityIndicator").hide();
}
});
});[/code]
This is the server Controller code that generates the PartialView with datatables in it:
[code]
// MVC way of annotating POST/GET operations on a Controller method. Requires MVC 2 or higher.
[HttpGet]
public ActionResult HourDetailsList(int projectID)
{
string projectName = service.GetProjectNameByProjectID(projectID);
// "HourDetails" is a user-defined class. I'm using C#'s shorthand for initializing the object.
HourDetails hourDetails = new HourDetails
{
ProjectID = projectID, ProjectName = projectName , UserID = loggedInUser.ID
};
// "s_HourDetail" is the database table name for this object. I'm using LINQ to SQL ORM for data access.
List s_hourDetails = service.GetHourDetailsByProjectID(projectID)
.Where(hd => hd.UserID == loggedInUser.ID).ToList();
foreach (s_HourDetail hd in s_hourDetails)
{
hourDetails.HourDetailItems.Add(new HourDetailItem
{
HourDetailID = hd.ID,
UserID = hd.UserID,
Username = tpsService.GetNameByUserID(hd.UserID),
EntryDate = hd.Date,
Hours = hd.Hours
});
}
return View(hourDetails);
}
[/code]
Here's the complete View code that renders the PartialView with datatables in it: Some of this will doubtlessly look like jibberish to people that don't use C#/ASP.NET MVC. The odd naming I have for the repeated values in the foreach loop is to allow the POST method I'll list at the end to bind the values in this PartialView to a generic List of user-defined objects. Saves a lot of code on converting types and querying the server variables. In the "fnDrawCallback" function I'm adding two table rows. I need them to appear in a certain location relative to the rows created in the loop, and if I don't add them this way, they appear at the top of the table, even if in the code they're listed afterwards. This is probably due to the fact that it takes a few microseconds to render the rows out in the loop, and they're already created.
[code]
$(function()
{
$("#HourDetailsList").dataTable({
"sDom": 'rtlip',
"bSort": true,
"aaSorting": [[2, 'desc']],
"iDisplayLength": 5,
"aLengthMenu": [[5, 10, 20, 30, -1], [5, 10, 20, 30, "All"]],
"aoColumnDefs": [
{ "sType": "us_date", "aTargets": [2] }
],
"fnInfoCallback": function (oSettings, iStart, iEnd, iMax, iTotal, sPre)
{
if (iEnd == 0) return "0 records";
return iStart + " to " + iEnd + " of " + iTotal;
},
"fnDrawCallback": function() {
$("#HourDetailsList tr").eq(1).before('Add Hours<%: Html.TextBox("hourDetails.NewHourDetails.Hours", null, new { @class = "hoursInput" })%><%: Html.TextBox("hourDetails.NewHourDetails.EntryDate", null, new { @class = "datepicker" }) %>');
$("#HourDetailsList tr:last").after('');
initDatepicker();
}
});
// Open this PartialView as a modal dialog box.
$( "#HourDetailsModal" ).dialog({
modal: true,
resizable: false,
width: 500
});
$(".hoursInput").numeric(); // Make sure only numbers appear using the numeric plugin.
});
<% using (Html.BeginForm()) { %>
<%: Html.Hidden("hourDetails.ProjectID", Model.ProjectID) %>
<%: Html.Hidden("hourDetails.UserID", Model.UserID) %>
Hours
Date
Delete
<% int index = 0;
foreach (var hourDetail in Model.HourDetailItems) { %>
<%: Html.DisplayFor(m => hourDetail.Username) %>
<%: Html.Hidden("hourDetails.HourDetailItems[" + index.ToString() + "].HourDetailID",
hourDetail.HourDetailID) %>
<%: Html.TextBox("hourDetails.HourDetailItems[" + index.ToString() + "].Hours",
hourDetail.Hours, new { @class = "hoursInput" })%>
<%: Html.TextBox("hourDetails.HourDetailItems[" + index.ToString() + "].EntryDate",
hourDetail.EntryDate.ToShortDateString(), new { @class = "datepicker" }) %>
<%: Html.CheckBox("hourDetails.HourDetailItems[" + index.ToString() + "].Delete",
false, new { @class = "deleteHours" })%>
<% index++; } %>
<% } %>
[/code]
Everything works exactly as I expected, with the exception that sorting doesn't work at all. The user interface updates with the arrow at the correct column and in the correct direction, but the rows don't move to a newly sorted position. So far I have no idea why this occurs, except that maybe jQuery UI's dialog modal does something that interferes with the sorting. I'm just grasping at straws there. This next bit of code has nothing to do with the quest, but I'm including it for completeness as I said, in case anyone else is trying to get something like this to work.
[code]
// Method has the same name as the GET version, but the annotation below directs which to call.
[HttpPost]
public ActionResult HourDetailsList(HourDetails hourDetails)
{
// Usually in ASP.NET the above method argument would be of type "FormCollection", but I'm able to map the PartialView
// directly to my Model, and access the properties of the object and its collection of items just like any other object.
service.EditProjectHours(hourDetails);
return RedirectToAction("Index");
}
[/code]
This discussion has been closed.