Kendo Grid doesn't display JSON Dates - asp.net-mvc

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.

Related

Sort Kendo Multiselects selected Items by DataTextField

We are using kendo multiselect with mvc wrappers. Everything on the setup works fine, but the selected items are sorted by the data value field. I simply want them to be sorted by the data text field, but nothing worked so far.
#(Html.Kendo().MultiSelectFor(m => m.SelectedPersonIds)
.HtmlAttributes(new { style = "width: 400px" })
.DataTextField("Name")
.DataValueField("PersonID")
.Filter("contains")
.Height(400)
.DataSource(ds =>
{
ds.Read(read =>
{
read.Action("GetPersons", "Person", new { area = "" });
});
})
.ItemTemplateId("detailTemplate")
.TagTemplateId("valueTemplate")
)
This is the working version. I tried adding
ds.Custom().Sort(s => s.Add("Name").Ascending());
and other approaches, but still no luck. The initial data, coming from the server, is sorted and therefore the list you select from is sorted perfectly (by Name).
How can i achieve that the selected items are also sorted by Name instead of by ID?
Thanks in advance.
Do not manually rearrange the DOM elements. Doing this will break the mapping between the displayed items and the internal data. If you were to manually rearrange the DOM elements, then deselecting an item will cause a different data item to be deselected in the underlying data. Binding back to the server will yield incorrect results.
Here is a correct way to order it, given that the value field is PersonID but you want it sorted by Name
function orderMultiSelect(multi) {
const dataItems = multi.dataItems();
dataItems.sort(
(a, b) => {
const textA = a.Name;
const textB = b.Name;
return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;
}
);
const values = dataItems.map(di => di.PersonID);
multi.value(values);
}
function onMultiselectChange(e) {
orderMultiselect(e.sender);
}
You would then bind this handler function to the to the DataBound and Change events:
#(Html.Kendo().MultiSelectFor(m => m.SelectedPersonIds)
.HtmlAttributes(new { style = "width: 400px" })
.DataTextField("Name")
.DataValueField("PersonID")
.Filter("contains")
.Height(400)
.DataSource(
ds =>
{
ds.Read(
read =>
read.Action("GetPersons", "Person", new { area = "" })
);
}
).ItemTemplateId("detailTemplate")
.TagTemplateId("valueTemplate")
.Event(
e =>
e.DataBound("onMultiselectChange")
.Change("onMultiselectChange")
)
)
http://plnkr.co/edit/0gS9SDeccgOmfsGp?preview
I don't know is there is ASP.NET solution but i can give you JavaScript to solve it:
function onMultiselectChange(e) {
e.sender.tagList.find('> li').sort(function (a, b) {
return $(a).text() > $(b).text();
}).appendTo(e.sender.tagList);
}
});
You can apply it to your ASP.NET multiselect like this:
.Events(e =>
{
e.Change("onMultiselectChange")
})

Kendo Grid and one row of JSON data and a single row of data

I feed JSON results to My Kendo Grid; seems like when I have one row of data, the grid can't function?
Following works:
<DocumentElement>
<ResultXml>
<NoData>1</NoData>
</ResultXml>
<ResultXml>
<NoData>2</NoData>
</ResultXml>
</DocumentElement>";
and this doesn't work:
<DocumentElement>
<ResultXml>
<NoData>1</NoData>
</ResultXml>
</DocumentElement>";
EDIT to clear out above xml is converted to json before being fed to the grid (I included the xml version of the json for readability purposes)
Obviously above is xml; after using Newtonsoft.Json.JsonConvert, I get the following JSON results to feed to my grid (none of which works):
{"DocumentElement":{"ResultXml":"Nothing to display"}}
{"DocumentElement":{"ResultXml":{"field":"Nothing to display"}}}
Following works, but I want to avoid sending an empty field if I don't have to
{"DocumentElement":{"ResultXml":[{"NoData":null},{"NoData":"Nothing to display"}]}}
End of Edit
here's my Kendo Grid:
$("#grid").kendoGrid({
sortable: true,
groupable: true,
scrollable: true,
height: "600px",
pageable: { pageSizes: 9 },
dataSource:
{
transport:
{
read: function (options) {
$.ajax("/Controller/Action?param=" + paramVal,
success: function (result) {
var jResult = $.parseJSON(result);
options.success(jResult.DocumentElement.ResultXml);
});
}
}
},
});
I guess I can hardcode the json string when sending it back to client; I would've liked to have the xml version of saying I have empty dataset; but I guess this'll do, unless someone can suggest something better;
if (string.IsNullOrEmpty(xmlResult))
{
//No data:
jsonData = "{\"DocumentElement\":{\"ResultXml\":[{\"NoData\":\"Nothing to display\"}]}}";
}
else
{
//Turn xml data to Json:
var doc = new XmlDocument();
doc.LoadXml(xmlResult);
jsonData = JsonConvert.SerializeXmlNode(doc);
}
I believe the field name does not need double quotes. Ex/ name\value pair as in {name: "Bob Mazzo"}
Are you sure that there are 2 definition (one for View,the other for retrieving data) in your controller and the View calls the other one as below?
Controller:
//!!! This is for calling "CustomAjaxBinding" view
public ActionResult CustomAjaxBinding()
{
return View();
}
//!!! This is for retrieving data called from "CustomAjaxBinding" View
public ActionResult CustomAjaxBinding_Read([DataSourceRequest] DataSourceRequest request)
{
var dataContext = new SampleEntities();
//Convert to view model to avoid JSON serialization problems due to circular references.
IQueryable<OrderViewModel> result = dataContext.Orders.Select(o => new OrderViewModel
{
OrderID = o.OrderID,
ShipCity = o.ShipCity,
ShipCountry = o.ShipCountry,
ShipName = o.ShipName
});
return Json(result, JsonRequestBehavior.AllowGet);
}
View (name is "CustomAjaxBinding")
#(Html.Kendo().Grid<Kendo.Mvc.Examples.Models.Order>()
.Name("Grid")
.Columns(columns => {
columns.Bound(o => o.OrderID).Groupable(false);
columns.Bound(o => o.ShipCity);
columns.Bound(o => o.ShipCountry);
columns.Bound(o => o.ShipName);
})
.Pageable()
.Sortable()
.Filterable()
.Scrollable()
.Groupable()
.DataSource(dataSource => dataSource
.Ajax()
//!!! Call "CustomAjaxBinding_Read" not CustomAjaxBinding
.Read(read => read.Action("CustomAjaxBinding_Read", "Grid"))
)
)
Regards...

Should I query the database as user types or left the job to Bloodhund?

I'm using Typeahead.js to search products in our webapp.
We are currently using Bloodhound to do a remote fetch of suggestions.
In the server, I'm doing a database query using the string entered by the user.
But I suspect, that the idea of using Bloodhound is not to do that in the server...
My JS code is as follow:
var products = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
limit: 205,
prefetch: 'Product/GetData?q=&ft=true',
remote: 'Product/GetData?q=%QUERY'
});
products.initialize();
$('#the-basics .typeahead').typeahead(null, {
name: 'products',
displayKey: 'name',
source: products.ttAdapter(),
minLength: 3,
templates: {
empty: [
'<div class="empty-message">',
'No products for current query',
'</div>'
].join('\n'),
suggestion: Handlebars.compile('<p><strong>{{name}}</strong></p>')
}
});
I have a Action in my Product controller to retrieve a list of products... so that on Prefetch I'm only returning the ACTIVE products... but then is you write something in the text box the remote option is triggered and returns all that matches the query.
My GetData action code:
public virtual JsonResult GetData(string q, bool ft = false)
{
if (ft == true)
{
var fetchTag = db.Products.AsNoTracking()
.Where(x => x.Active == true && x.idTenant == CurrentTenantID)
.OrderByDescending(x => x.Active).ThenBy(x => x.Nombre)
.Take(20)
.Select(x => new TagSys() { tag = x.idCaballo, name = x.Nombre }).ToList();
return Json(fetchTag, JsonRequestBehavior.AllowGet);
}
else
{
var fetchTag = db.Products.AsNoTracking()
.Where(x => x.idTenant == CurrentTenantID && x.Nombre.StartsWith(q.ToLower()))
.OrderByDescending(x => x.Active).ThenBy(x => x.Nombre)
.Take(20)
.Select(x => new TagSys() { tag = x.idCaballo, name = x.Nombre }).ToList();
return Json(fetchTag, JsonRequestBehavior.AllowGet);
}
}
First issue:[Solved] despite the fact that the query entered matched values in the Prefetch data, it returns values from the remote data. Why is this? ANSWER: The bloodhound tokenizer need a datum field/property as parameter. "value" was not a property in my datum... changing to "name" in my caso solved the issue.
Second: Should I query the database using the query value entered by user in the remote method, or should I return a big list and let Bloodhound do the work?
Third:[solved] I used the minLength: 3 option, hoping that it will start suggesting/searching just after 3 chars in the input, but I'm seeing that it is triggered after the first key pressed. ANSWER: this option is top level, not dataset related, so in my code, I replaced the null parameter in typeahead init with { minLength: 3} and it worked.

How do I update a KendoUI chart via javascript JSON?

So I have a chart which is configured using the MVC style configuration...
#(Html.Kendo().Chart<DIMVC.ViewModel.CompanyProduction>(Model.CompanyProduction)
.Name("Chart")
.Title("Files sent")
.Legend(legend => legend
.Position(ChartLegendPosition.Bottom)
)
.ChartArea(chartArea => chartArea
.Background("transparent")
)
.SeriesDefaults(seriesDefaults =>
seriesDefaults.Line().Style(ChartLineStyle.Smooth)
)
.Series(series => {
series.Line(model => model.SentFiles).Name("Sent Files");
... { lots more series added here }
}
.CategoryAxis(axis => axis
.Categories(model => model.MonthDisplay)
.Labels(labels => labels.Rotation(-90))
)
.ValueAxis(axis => axis.Numeric()
.Labels(labels => labels.Format("{0:N0}"))
.MajorUnit(10000)
)
.Tooltip(tooltip => tooltip
.Visible(true)
.Format("{0:N0}")
)
.Events(e => e
.SeriesClick("onSeriesClick")
)
)
I also have a slider on the page. When the slider value is changed I handle this event.
#(Html.Kendo().RangeSlider()
.Name("yearRange")
.Min(2000)
.Max(DateTime.Today.Year)
.SmallStep(1)
.LargeStep(5)
.Values(Model.MinYear, Model.MaxYear)
.Events(e => e.Change("yearRangeChange"))
)
javascript method
function yearRangeChange(e)
{
var url = "/FetchData/";
$.ajax({
type: "GET",
url: url,
data: { startYear: e.values[0], endYear: e.values[1] },
dataType: "json",
success: function (json) {
$("#DINETChart").kendoChart({
dataSource: {
data: json
}
});
var chart = $("#DINETChart").data("kendoChart");
chart.refresh();
}
});
}
now when the chart is updated the grid is just blank.
The json request is successfully called and the data is retrieved.
but after the chart is populated the chart is blank.
has anyone got any suggestions?
* EDIT *
adding a sample of the JSON returned
"[{\"CompanyID\":1,\"Year\":2011,\"Month\":8,\"SentFiles\":1666,\"ReceivedFiles\":1632,\"SentData\":12803.674593292486,\"ReceivedData\":11908.047586546765,\"Note\":null,\"MonthDisplay\":\"Aug\",\"CompanyDisplay\":null},{\"CompanyID\":1,\"Year\":2013,\"Month\":10,\"SentFiles\":21004,\"ReceivedFiles\":20387,\"SentData\":157376.825542573,\"ReceivedData\":152878.87845794103,\"Note\":null,\"MonthDisplay\":\"Oct\",\"CompanyDisplay\":null},{\"CompanyID\":1,\"Year\":2013,\"Month\":4,\"SentFiles\":9989,\"ReceivedFiles\":9880,\"SentData\":74913.53277995327,\"ReceivedData\":75145.16331588416,\"Note\":null,\"MonthDisplay\":\"Apr\",\"CompanyDisplay\":null},{\"CompanyID\":1,\"Year\":2013,\"Month\":11,\"SentFiles\":25956,\"ReceivedFiles\":25249,\"SentData\":196155.8977337967,\"ReceivedData\":189320.44546897494,\"Note\":null,\"MonthDisplay\":\"Nov\",\"CompanyDisplay\":null}]"
I would also like to point out that if I add this
.DataSource(ds => ds.Read(read =>
read.Action("FetchData", "Home", new { startYear = 2012, endYear = 2013 })
))
to my chart configuration, this will populate the chart fine, without using the page Model.
i.e. The data is correct for the chart.
The data source expects an array as value for its data, but it looks like the json variable you're assigning contains a JSON string (at least if the string you added to the question is what you're seeing on the client in the json var). jQuery should normally parse that for you if you set dataType to "json" (not sure why that is not happening for you - you should double check that the dataType param is set correctly).
You can try parsing it yourself; apart from that, you should also use chart.setDataSource() instead of creating a new chart. They way you're doing it now, you're simply replacing your original chart with its configuration with a chart that has no configuration. Try something like this in your callback:
var data = JSON.parse(json);
var chart = $("#DINETChart").data("kendoChart");
var dataSource = new kendo.data.DataSource({
data: data
});
chart.setDataSource(dataSource);
Note that in your yearRangeChange function, you're trying to call refresh on the chart outside of your ajax success call. You don't need to refresh when you use setDataSource, but even if you needed to do that, it would have to be in the callback. Otherwise it happens before the ajax call completes.

JQuery Datatables and ASP.NET MVC Parameter to ActionResult

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.

Resources