JQuery Datatables and ASP.NET MVC Parameter to ActionResult - asp.net-mvc

I'm trying to return a View with a Jquery datatable, whose action is launched from a previous page with the ActionLink--
#Html.ActionLink("View Audit", "Audit", new { id= Model.ID })
The Jquery datatable is then pre-filtered with the ID passed from the Model ID.
Here is my JS file...(incidentally, a static value e.g. 10005 works here in the fnServerParams, but I need the value to be dynamic based on whatever Model ID is chosen from theprevious screen)
var oTable = $('#myAuditTable').dataTable({
"sAjaxSource": "GetAuditLog",
"fnServerParams": function ( aoData )
{ aoData.push({ "name": "ID", "value": 10005 })
},
"aoColumns": [
....
Here is my Audit.cshtml page.
#model IEnumerable<Models.AuditLog>
<table id="myAuditTable" width="100%">
<tr>...</tr>
</table>
and in the Controller
public ActionResult GetAuditLog(int ID){
var logs = db.AuditLog.Where(c => c.ID == ID).ToList();
var result = from c in logs
select new[] { ....
};
return Json(new
{
aaData = result
}, "text/x-json", JsonRequestBehavior.AllowGet);
}
So I normally would pass a parameter in MVC like so:
public ActionResult Audit(int ID)
{
return View();
}
But since the GetAuditLog is the action getting results, how do I get the int ID to the GetAuditLog action in order to pass the filter the records, which in turn get passed as JSON. I can't call GetAuditLog in the ActionLink, because its job is to pull JSON, not render a View.
I'm not sure what I'm missing. I've gone through this guy's articles cause they are pretty comprehensive as far as integrating ASP.NET and datatables.
http://www.codeproject.com/Articles/155422/jQuery-DataTables-and-ASP-NET-MVC-Integration-Part
But cannot find an exact fit to my problem.
This post seems to come close...
How do I access the JQuery DataTables plugin aoData values in MVC3?
But doesn't quite apply since he seems to be working with a successfully passed-in parameter.
Thanks for any help.

Hi you can achieve this by two ways:
First create a hidden field having value from Model.Id and then assign this hidden field value to your datatable() function in like this
in view:
<input type="hidden" id="ID" value="#Model.ID" name="ID" />
and then put your below peace of code under document.ready and assign value ID from hidden field like this :
$(document).ready(function(){
var oTable = $('#myAuditTable').dataTable({
"sAjaxSource": "GetAuditLog",
"fnServerParams": function ( aoData )
{ aoData.push({ "name": "ID", "value": $("#ID").val() })
},
"aoColumns": [
....
});
Second: Put your datatable() function in your view under script tag and assign Model.Id directly like this:
<script type="text/javascript">
var oTable = $('#myAuditTable').dataTable({
"sAjaxSource": "GetAuditLog",
"fnServerParams": function ( aoData )
{ aoData.push({ "name": "ID", "value": '#Model.ID' })
},
"aoColumns": [
....
</script>
Hope this will resolve this issue.

Related

Making a button visible based on number of records in datatable

I am using jquery datatable in asp.net mvc and i want to show a submit button which will be saving the data to the database only if there is atleast one row in the datatable.
I am trying this code, however its not working
<tr id="trbtnSubmit">
<td colspan="9" align="center">
<input type="submit" name="btnSubmit" value="Save"
class="btn btn-edit btn-text" />
</td>
</tr>
<script>
var PopUp, dataTable;
$(document).ready(function () {
dataTable = $("#tblCustomerList").DataTable({
"ajax": {
"url": "/Customer/GetCustomers",
"type": "GET",
"data": "json"
},
"lengthChange": false,
"pageLength": 10,
"columns": [
{ "data": "Number" },
{ "data": "Name" },
{ "data": "fileName" },
{ "data": "mD5Hash" },
{ "data": "dataSizeInGB" },
{
"data": "Id",
"render": function () {
return "<a href='#'><i class='fa fa-eye'></a></i><a href='#' style='margin-left:5px'><i class='fa fa-pencil'></i></a><a href='#' style='margin-left:5px'><i class='fa fa-trash'></a></i>";
},
"orderable": false,
"width": "40px"
},
],
"language": {
"emptyTable": "No Customers , click on <b>New Customer</b> to add Customers"
}
});
var table = $('#tblCustomerList').DataTable();
if (!table.data().any()) {
$('#trbtnSubmit').hide();
} else {
$('#trbtnSubmit').show();
}
});
</script>
Since you didn't specify the version of datatables, I assume it's v1.10.
And there are 2 side notes I want to make before going into your problem:
Difference between .datatable() and .DataTable()
Enable server-side processing
Difference Between .datatable() and .DataTable()
I saw you declared another variable, var table, at the bottom of your sample code to get another instance of DataTables and check if there is any data? You actually don't need to.
.DataTable() returns a DataTables API instance, while .datatable() returns a jQuery object.
So if you intent to make usages on the DataTables APIs after you initialize the table, you can just use the varirable you declared from the beginning, var dataTable since you used .DataTable() way.
Enable Server-side Processing
Server-side processing is enabled by turning on the serverSide option, and configuring the ajax option. You're missing the first one, whose default is false.
So you might need to add serverSide option in your code:
dataTable = $("#tblCustomerList").DataTable({
serverSide: true,
ajax: {
...
},
...
});
Enough said. Now looking at your problem ...
DataTables Callbacks
There are many ways to achieve what you want to do, and I like to use callbacks so that you can configure your DataTables in one place.
There are lots of callbacks you can use, and the one I would use is drawCallback:
dataTable = $("#tblCustomerList").DataTable({
serverSide: true,
...,
language: {
emptyTable: "No Customers , click on <b>New Customer</b> to add Customers"
},
drawCallback: function(settings) {
$('#trbtnSubmit').toggle(settings.aoData.length > 0);
}
});
Hopefully my code is readable enough without any additional explanations :)

Kendo Grid doesn't display JSON Dates

I'm using a Kendo Grid, built with their MVC Helpers to display some remote JSON data.
Everything works fine except the date columns.
Here's my setup.
View:
#(Html.Kendo().Grid<TrainingDTO>().Name("grid_training")
.Columns(c => {
c.Bound(a => a.Id).Width(50);
c.Bound(a => a.Start).ClientTemplate("#= (Start == null) ? ' ' : kendo.toString(Start, 'dd-MM-yyyy') #");
c.Bound(a => a.End).Format("{0:dd/MM/yyyy HH:mm:ss}");
c.Bound(a => a.Description);
})
.Pageable()
.Scrollable().DataSource(ds => ds.Ajax().PageSize(5).Model(m => m.Id(p => p.Id)).Read(R => R.Action("GridData", "Home", new { providerId = Model.Id }).Type(HttpVerbs.Post)))
)
And the controller:
[HttpPost]
public JsonResult GridData([DataSourceRequest]DataSourceRequest request) {
var data = new Service().LoadSomeData().ToDataSourceResult(request, A => new {
Id = A.Id,
Start = A.Start,
End = A.End,
Description = A.Description
});
return Json(data, JsonRequestBehavior.AllowGet);
}
Which returns (in json)
{
"Data": [
{
"Id": 1,
"Start": "\/Date(1412031600000)\/",
"End": "\/Date(1415318400000)\/",
"Description": "test teste test"
}
],
"Total": 1,
"AggregateResults": null,
"Errors": null
}
The Start and End columns appear empty. These properties on the TrainingDTO POCO are DateTime Nullables (ie, DateTime?)
I've tried many options to format the data but to no avail.
Is there a way to print these JSON dates on the grid (without resorting to formatting them as strings on the controller) ?
I have had this problem before.
I assume you are looking to use UK date format rather than the default US format.
If you take off the formatting I expect you will see the dates formatted correctly.
As you appear to be using a flattened model (date's don't work correctly in complex viewmodels) then the simplest thing to do is add the culture file into your project and then set it to UK so that everything is picked up correctly.
so in your layout page put this after the kendo scripts:
<script src="/Scripts/kendo/{you version here}/cultures/kendo.culture.en-GB.min.js"></script>
<script type="text/javascript">
kendo.culture("en-GB");
</script>
If the culture files are not part of the project just go through the telerik upgrade and include them or just copy them into the project.

Using Asp.Net C# MVC4 and Json, how can I get my chart to update in line with my paged datatable

I am using datatables and charts. I would prefer to return standard data through the model but cant for some odd reason, and the controls only seem to work with Json. Perhaps it's just my understanding. But at this point I am completely lost.
What I want to do is actually (in theory) quite simple.
Action result 1 returns a table which has paged data to a view as a Json result because that is what datatables require. This works fine.
Here are the controller actionresults for this:
public ActionResult AjaxVitalsHandler()
{
return PartialView();
}
[HttpPost]
[AccessRequired(AccessFeature.Vitals, AccessLevel.Read)]
public ActionResult AjaxVitalsHandler(JQueryDataTableParamModel param)
{
int start = param.iDisplayStart;
int perPage = Math.Min(param.iDisplayLength, 100);
int currentPage = (start / perPage) + 1;
int id = _accountSession.CurrentUserId;
//...Bunch of code here to do basic queries and get the data...All working fine.
return Json(new
{
sEcho = param.sEcho,
iTotalRecords = model.TotalItemCount,
iTotalDisplayRecords = model.TotalItemCount,
aaData = result
});
}
Here is the corresponding partial view (This is a partial view loaded into a main view with RenderAction, nothing special):
<table class="table table-striped table-bordered table-hover"
id="vitalsTable"
data-request-url="#Url.Action("AjaxVitalsHandler")">
<thead>
<tr>
<th class="hidden-480">Date</th>
<th class="hidden-480">Weight (kg)</th>
<th class="hidden-480">Height (cm)</th>
<th class="hidden-480">BMI</th>
<th class="hidden-480">B/P</th>
</tr>
</thead>
</table>
Finally the corresponding DataTables code (which for some odd reason gives errors when placed in the partial view but not in the main view, (not my question but anyway):
<script>
$(document).ready(function ()
{
var urlRequest = $('#vitalsTable').data("request-url");
$('#vitalsTable').dataTable({
"bSort": false,
"bServerSide": true,
"sAjaxSource": urlRequest,
"sServerMethod": "POST",
"bProcessing": true,
"bFilter": false,
"aLengthMenu": [[10], [10]],
"aoColumns": [
{ "mDataProp": "VitalsDate" },
{ "mDataProp": "weight" },
{ "mDataProp": "height" },
{ "mDataProp": "bmi" },
{ "mDataProp": "bp" },
]
});
});
</script>
Action result 2 returns a chart which has the same data to another view as a Json result because that is what flot charts require. This works fine but only for the first page of data.
Here are the controller actionresults for this:
public ActionResult LoadVitalsChartData2()
{
return PartialView();
}
//[HttpPost]
[AccessRequired(AccessFeature.Vitals, AccessLevel.Read)]
public JsonResult LoadVitalsChartData()
{
int id = _accountSession.CurrentUserId;
//Bunch of code here to retrieve the data...Problem here.
//There seems to be no way to sync the Ajax call back to that it refreshes
//the data here too.
//Ideally what I want is that when the page button is pressed, it reloads this
//chart too and passes to it the relevant columns of data for plotting.
//Presently I can only get it to do the first page.
//The queries work, but how do I pass the relevant page data from the above
//action result so I am not just getting the first page of data every time.
return Json(new
{
weightData = weightResult,
xLabels = xAxisResult,
heightData = heightResult
//There is security issues around AllowGet inside a post method.
//I would like to fix this but it is also not my question.
}, JsonRequestBehavior.AllowGet);
}
Here is the corresponding partial view (This is a partial view loaded into a main view with RenderAction, nothing special):
<div id="VitalsChart" class="chart"
data-request-url="#Url.Action("LoadVitalsChartData")">
</div>
Finally the corresponding chart code this was copied and pasted from the site and only slightly modified so I put it into a separate file, if you would like to see the full code go here there is a lot of reading but I don't see anywhere to do paging:
Charts.initCharts();
Obviously I want my chart to display the data that is currently displayed in the table. i.e. if my table has 100 items paged in sets of 10, then when my table is displaying items 20 to 30, my chart should show the data for those items. It only shows the data for the first items 1 to 10. The chart itself doesn't need to handle paging, it just needs the 10 items to display and to know when the table updates. This is all available from the data sent to the table and its paging event.
So how do get this out of my table and pass it to my chart.
I have tried extracting the data to an array of some sort so it can be shared. flot charts doesn't like this. I also tried extracting the data to a common action method and passing it to another actionresult but I cant figure out how to do it.
I have tried converting all the data from anonymous types to standard class types by defining the class and casting it. But I still get errors.
This has been done before. A paged table and a chart that corresponds to the data displayed in it. Why is this so difficult to do in C# MVC4.
If there was a way I could use the above with standard data instead of Json, I would be laughing cause I know the solution. In fact, I have the solution. When querying the data, wrap it in a Paging wrapper so only the data required is returned. Then do 2 queries one in each action result for the same dataset passing only the page number to the Paging wrapper. But alas, this is for standard C# and Razor with EF and Linq. My wonderful controls require Json. This should be a lot easier but I don't know what I am missing.
It is hard to post much more code on this, I have tried so many different ways that it would make the post very long. But any one in isolation or any part of one will serve only to confuse issues.
Finally: I got some clues how to do this and started making progress but the examples I am looking for on the datatables site are not good. Here is one of my many attempts so far which is not working right (it just gives me a standard datatables error, not a JavaScript error):
$('#vitalsTable').live('page', function ()
{
var tbl = $('#vitalsTable').dataTable(
{
"aoColumnDefs": [{
"aTargets": [0],
"mData": function (source, type, val)
{
return "Hello";
}
}]
});
alert("Data ==> " + tbl);
Charts.initCharts();
});
Effectively what I am looking to do, is get a column of data when the page changes and plot this data on my chart. The page contains 10 items. So I should have a column of data with 10 items in it that can be passed to my chart for plotting.
Another snippet of code suggests that I should detect the page change event like above and before it I should do this:
var my_chart = $.plot($("#vitalsTable"), [{}]);
Then inside it I should do something like this:
my_chart.setData([new_data_set]);
my_chart.draw();
But again, I don't know how to make it work.
Thanks for any help.
This is really surprising for me. SO is a great resource, and I love it. But this is the first time I came across a problem that SO users never answered. Anyway, Finally after 8 days I worked out the answer to my question. So I am going to pop it here for reference for myself in the future cause I always come back here. S/O is like my new coding bible.
Essentially the controller was not the problem. Even the view was not the problem but instead the way I was doing the jQuery in the view.
I was advised to do something like this on other forums which essentially adds a new event handler to my data tables.
$('#vitalsTable').live('page', function ()
{
var tbl = $('#vitalsTable').dataTable(
{
"aoColumnDefs": [{
"aTargets": [0],
"mData": function (source, type, val)
{
return "Hello";
}
}]
});
alert("Data ==> " + tbl);
Charts.initCharts();
});
Above this code in my datatables code I had something like this:
<script>
$(document).ready(function ()
{
var urlRequest = $('#vitalsTable').data("request-url");
$('#vitalsTable').dataTable({
"bSort": false,
"bServerSide": true,
"sAjaxSource": urlRequest,
"sServerMethod": "POST",
"bProcessing": true,
"bFilter": false,
"aLengthMenu": [[10], [10]],
"aoColumns": [
{ "mDataProp": "VitalsDate" },
{ "mDataProp": "weight" },
{ "mDataProp": "height" },
{ "mDataProp": "bmi" },
{ "mDataProp": "bp" },
]
});
});
</script>
In actuality, all I needed to change was the code directly above, to the code below:
<script>
$(document).ready(function ()
{
var urlRequest = $('#vitalsTable').data("request-url");
$('#vitalsTable').dataTable({
"bSort": false,
"bServerSide": true,
"sAjaxSource": urlRequest,
"sServerMethod": "POST",
"bProcessing": true,
"bFilter": false,
"aLengthMenu": [[10], [10]],
"fnDrawCallback": function (oSettings)
{
Charts.initCharts(oSettings);
},
"aoColumns": [
{ "mDataProp": "VitalsDate" },
{ "mDataProp": "weight" },
{ "mDataProp": "height" },
{ "mDataProp": "bmi" },
{ "mDataProp": "bp" },
]
});
});
</script>
Once I did that, I simply needed to make minor adjustments to the client side chart.js file which takes in the data through the Charts.initCharts function and all worked well.
Later I had problems with the anonymous types, so I tried replacing them with proper classes like what C# does behind the scenes. But I found that it was even easier than this to solve. My tip, watch carefully the capitalisation of your variables on the view, controller and client side chart.js. They have to correspond exactly. Took me 2 days of messing around to find that out. It's surprising how simple changes sometimes take the longest.
I hope others looking at this find it useful. If not, post a comment and I will try to answer it.

How to bind kendo mvc ui dropdownlist dynamically

I am working on asp.net mvc with Kendo UI mvc. I have two kendo dropdown lists. one for list of clinics and another of list of patients in selected clinic. But there is no direct relationship between clinic and patient to use the cascading dropdownlist. for that i have used ajax calls in dropdownlist change event and get list of patients. and this is my first dropdownlist for list clinics
#(
Html.Kendo().DropDownList()
.Name("ddlClinics")
.Events(e=>e.Change("ChangeClinic"))
.BindTo(new SelectList((List<Account.Entities.Clinic>)ViewBag.lstClinic,
"ClinicID", "ClinicName")))
and this is my second dropdownlist for listpatients
#(
Html.Kendo().DropDownList()
.Name("ddlPatients")
.BindTo(new SelectList((List<Patient>)ViewBag.Patients,
"PatId", "PatName"))))
I want to dynamically bind the list of patients to second dropdownlist when the first dropdownlist changes,
function ChangeClinic()
{
$.ajax({
url: '/Messages/GetPatient',
type: 'Post',
data: { email: '#User.Identity.Name' },
cache: false,
success: function (result) {
var ddlPatients = $('#ddlPatients').data('kendoDropDownList');
var main = [];
$.each(result, function (k, v) {
main.push({ "PatId": v.PatId, "PatName": v.PatName });
});
ddlPatients.dataTextField = "PatName";
ddlPatients.dataValueField = "PatId";
ddlPatients.dataSource.data(main);
ddlPatients.reload();
}
});
}
i am able to bind the list to dropdownlist but all items are shows as 'undefined'. so please guide me.
From what I can tell, there is a relationship between clinics and patients so you should be able to use the CascadeFrom("DropDownList1") provided in the wrappers. We use a cascading dropdownlist in a similar fashion for the relationship between school districts and schools:
#(Html.Kendo().DropDownList()
.Name("District")
.HtmlAttributes(new { style = "width:300px;" })
.BindTo(ViewBag.districts)
.DataTextField("DistrictName")
.DataValueField("DistrictID")
.OptionLabel("Select District")
)
#(Html.Kendo().DropDownList()
.Name("School")
.HtmlAttributes(new { style = "width:300px;" })
.CascadeFrom("District")
.BindTo(ViewBag.schools)
.DataTextField("SchoolName")
.DataValueField("SchoolID")
.OptionLabel("Select School")
)
If you want fill second DropDown on basis of first DropDown value.
Telerik Provided,
.CascadeTo("DropDownList2")
Please see following link for detailed information.
Cascading of Dropdown in Telerik dropdownlist
Instead of creating such array which is useless to the dataSource use:
success: function (result) {
var ddlPatients = $('#ddlPatients').data('kendoDropDownList');
var main = [];
$.each(result, function (k, v) {
main.push({ "text": v.PatId, "value": v.PatName });
});
ddlPatients.dataSource.data(main);
}
});
If you are not using
.DataSource(source =>
{
source.Read(read =>
{
read.Action ("FunctionName", "ControllerName").Data("filterDropdown1");
}).ServerFiltering(true);
})
.CascadeFrom("Dropdown1")
properties in the definition of second dropdown and you are using the definition mentioned in question above. i.e:-
#(
Html.Kendo().DropDownList()
.Name("ddlPatients")
.BindTo(new SelectList((List<Patient>)ViewBag.Patients,"PatId", "PatName"))
)
then you can bind the data to the 2nd dropdown directly in the success function of ajax post.
function ChangeClinic()
{
$.ajax({
url: '/Messages/GetPatient',
type: 'Post',
data: { email: '#User.Identity.Name' },
cache: false,
success: function (result) {
$('#ddlPatients').data('kendoDropDownList').dataSource.data(result);
//ddlPatients.reload();
}
});
}
#Note:- 1) The result value should contain the list of new patients with properties "PatId" and "PatName" based on the parameter email passed to the function "Messages" in GetPatient controller, and there will be no need for ddlpatients.reload(), infact .reload() is not supported, it will break the execution, so don't use .reload().

Jquery Autocomplete

iam using jquery autocomplete in asp.net project. it's not working. do you have any idea. the code is given below.
<script type="text/javascript">
$(function () {
$('#clientabbrev').val("");
$("#clientstate").autocomplete({
source: "clientstates.aspx",
select: function (event, ui) {
$('#clientstateid').val(ui.item.clientid);
$('#clientstateabbrev').val(ui.item.clientabbrev);
}
});
$("#clientstate_abbrev").autocomplete({
source: "clientstatesabbrev.aspx",
minLength: 2
});
});
</script>
problem is states.aspx returning the data but it is not showing in the jquery autocomplete control.
Your server needs to return a JSON serialized array of objects with properties id, label, and value. E.g. :
[ { "id": "1", "label": "Mike Smith", "value": "Mike Smith" }, { "id": "2", "label": "Bruce Wayne", "value": "Bruce Wayne" }]
Can you confirm with firebug or Fiddler that your server is returning the correct response?
If you're having trouble serializing your data in C#, you can try using JavaScriptSerializer like this:
var result = from u in users
select new {
id = u.Id,
value = u.Name,
label = u.Name
};
JavaScriptSerialier serializer = new JavaScriptSerializer();
var json = serializer.Serialize(result);
// now return json in your response

Resources