I am using Kendo UI chart in one of my projects today noticed a weird behaviour in different browser, I have date on x-axis, and it is auto transforming dates in different browsers with different time zone.
Like in UTC+5 it is showing date range from 3/1/2014 to 3/31/2014 while in UTC-6 it is showing date range from 2/28/2014 to 3/30/2014.
Basically this is happening due to the difference between the timezones of the client and the server and the form these dates are trasnfered and re-created on both sides into Date (JS) /DateTime (.NET) objects.
Basically the whole situtation is explained in details here. The dataSource that the Chart is using is the same as the one that the Grid uses so there is not difference.
Here is some example code from a project I have which you can use. Check the requestEnd handler
#(Html.Kendo().Grid<KendoMVCWrappers.Models.Person>().Name("persons")
.DataSource(dataSource => dataSource
.Ajax()
.Events(ev => ev.RequestEnd("convert"))
.Model(model => model.Id(m => m.PersonID))
.Read(read => read.Action("GetPersons", "Home"))
.Update(up => up.Action("UpdatePerson", "Home"))
)
.Filterable()
.Columns(columns =>
{
columns.Bound(c => c.PersonID);
columns.Bound(c => c.Name);
columns.Bound(c => c.BirthDate);
columns.Command(cmd => cmd.Edit());
})
.Pageable()
.Sortable()
)
<script type="text/javascript">
function convert(e) {
if (e.response.Data && e.response.Data.length) {
var offsetMiliseconds = new Date().getTimezoneOffset() * 60000;
var persons = e.response.Data;
for (var i = 0; i < persons.length; i++) {
persons[i].BirthDate = persons[i].BirthDate.replace(/\d+/,
function (n) { return parseInt(n) + offsetMiliseconds }
);
}
}
}
</script>
And the setter code of the ViewModel. Using a setter eases the whole situation since you have to do it in multiple places (before creating an object when it is fetched from the database and when it is created from the ModelBinder).
public class Person
{
public int PersonID { get; set; }
public string Name { get; set; }
private DateTime birthDate;
public DateTime BirthDate
{
get { return this.birthDate; }
set
{
this.birthDate = new DateTime(value.Ticks, DateTimeKind.Utc);
}
}
}
Good luck!
Got null response error when tried to parse date as mentioned in this post in onRequestEnd.
http://www.telerik.com/support/code-library/using-utc-time-on-both-client-and-server-sides
I resolve this by parsing in datasource parse method instead of requestEnd.
parse :function(data)
{
return ConvertToUTC(data);
}
function ConvertToUTC(data)
{
// iterate over all the data elements replacing the Date with a version
// that Kendo can work with.
$.each(data, function(index, item){
if(index == "data")
{
for(i =0 ;i< item.length; i++)
{
// Days is my date property in item collection
item[i].Days = item[i].Days.replace(/\d+/,
function (n) {
var time = parseInt(n);
return parseInt(time) + new Date(time).getTimezoneOffset() * 60000;
}
);
}
}
});
return data;
}
Related
My Kendo.Grid need to perform CRUD operations on the data on SAVE CHANGES click
This is the Grid's definition:
#(Html.Kendo().Grid(Model.TicketReportPropertyList)
.Name("TicketReportPropertyGrid")
.Columns(columns =>
{
columns.Bound(c => c.ID).Hidden();
columns.Bound(c => c.PropertyName).Title("Property Name").EditorTemplateName("_PropertyNameEditor").Width(900);
columns.Bound(c => c.Amount).Title("Amount").Format("{0:C}").Width(90);
columns.Command(c => c.Custom("Delete").Click("DeleteRecord"));
})
.Events(events => events.DataBound("Databound").SaveChanges("SaveGrid").Edit("Edit"))
.ToolBar(toolbar =>
{
toolbar.Create();
toolbar.Save();
})
.Editable(editable => editable.Mode(GridEditMode.InCell).DisplayDeleteConfirmation(false))
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.ServerOperation(false)
.Model(model =>
{
model.Id(c => c.ID);
model.Field(c => c.PropertyName);
model.Field(c => c.Amount);
})
.Events(events =>
{
events.RequestEnd("onRequestEnd");
})
.Read(read => read.Action("GetData", "TicketReportProperty", Model))
.Create(create => create.Action("AddTicketReportProperty", "TicketReportProperty"))
.Update(update => update.Action("UpdateTicketReportProperty", "TicketReportProperty"))
.Destroy(delete => delete.Action("DeleteTicketReportProperty", "TicketReportProperty"))
)
)
When doing the update, the following method of the controller is invoked:
[HttpPost]
public ActionResult UpdateTicketReportProperty([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<TicketReportPropertyEntity> ticketReportPropertyList)
{
TicketReportPropertyModel model = new TicketReportPropertyModel();
model = new TicketReportPropertyModel().UpdateTicketReportProperties(ticketReportPropertyList);
if (!model.Success)
{
ModelState.AddModelError("TicketReportProperty", model.ErrorDescription);
}
return Json(new[] { model.TicketReportPropertyList }.ToDataSourceResult(request, ModelState));
}
When Update is performed, all the data I need is populated inside of ticketReportPropertyList parameter and I can update the database
However, when performing a Create operation, the controller's method got hit, but ticketReportPropertyList is not populated with some of the parameters I need as happen during Update.
When adding new record the following method is invoked:
[HttpPost]
public ActionResult AddTicketReportProperty([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<TicketReportPropertyEntity> ticketReportPropertyList)
{
TicketReportPropertyModel model = new TicketReportPropertyModel();
model = new TicketReportPropertyModel().AddTicketReportProperty(ticketReportPropertyList);
if (!model.Success)
{
ModelState.AddModelError("TicketReportProperty", model.ErrorDescription);
}
return Json(new[] { model.TicketReportPropertyList }.ToDataSourceResult(request, ModelState));
}
This is TicketReportPropertyEntity:
public class TicketReportPropertyEntity
{
public int ID { get; set; }
public int TicketID { get; set; }
public decimal Amount { get; set; }
public string PropertyName { get; set; }
public int ReportPropertyID { get; set; }
}
What am I missing here?
I believe your issue can be fixed by simply replacing:
[Bind(Prefix = "models")]IEnumerable<TicketReportPropertyEntity> ticketReportPropertyList
with
TicketReportPropertyEntity ticketReportPropertyList
You are not sending a list when creating each row, you are simply sending one object.
OK, so for creates kendo initializes a new TicketReportPropertyList and then binds values from your columns. So your missing columns will be null. One mechanism to resolve that is to specify DefaultValue for those columns in your model definition. The value can come from a field in your model, a set value, value of a hidden, etc. So I typically have a model for the page the grid is displayed on with values for added items. Then I can do:
.Model(model =>
{
model.Id(c => c.ID);
model.Id(c => c.TicketID).DefaultValue(Model.TicketID);
model.Id(c => c.ReportPropertyID).DefaultValue(Model.ReportPropertyID);
model.Field(c => c.PropertyName);
model.Field(c => c.Amount);
})
Another way would be to handle the Grid's edit event, check for inserts and set the values:
if (e.model.isNew()) {
model.set("TicketID", $("#TicketID).val()); // hidden
model.set("ReportPropertyID", $("#ReportPropertyID).val()); // hidden
}
i using ajax for reading data from my controller(ShiftReports).and i also using view model for combine two tables.
i don't now how to binding or iterate json array to kendo column.
this is my kendo mvc grid
#(Html.Kendo().Grid<PLIMO.ViewModel.MainEqpViewModel>()
.Name("MainEQP")
.DataSource(ds => ds
.Ajax()
.Read(read => read.Action("Eqp_Read", "ShiftReports"))
)
.Pageable()
.Sortable()
)
this is my controller
public ActionResult Eqp_Read([DataSourceRequest]DataSourceRequest request)
{
try {
using (var db = new DBContext())
{
db.Configuration.ProxyCreationEnabled = false;
var eqp = new MainEqpViewModel()
{
Tags = db.Tags.ToList(),
};
DataSourceResult result = new List<MainEqpViewModel>() { eqp }.ToDataSourceResult(request);
return Json(result);
}
}
catch(Exception ex)
{
return Json(ex.Message);
}
}
}
You need a dynamic column. please refer the sample code https://dotnetlearningarray.blogspot.com/2015/06/telerik-mvc-grid-with-dynamic-columns.html
Consider this scenario:
app loads => fetches json from api => needs to modify json returned
In this case, I'm using moment to make some date modifications and do some grouping that I'll use in the UI. I looked on stack and found a similar question but didn't feel like it provided the clarity I am seeking.
Where should I use .map to create the new objects that contain the formatted & grouped dates? Should I manipulate the raw json in the api call or in the redux action before I dispatch? What is the best practice?
Is it OK to add properties and mutate the object as I am showing below,
service["mStartDate"] = mStartDate before I put the data into my store and treat it as immutable state?
First Approach - changing raw json in the api call
class TicketRepository extends BaseRepository {
getDataByID(postData) {
return this.post('api/lookup', postData)
.then(result => {
const groupedData = {}
return result.map(ticket => {
const mStartDate = moment(ticket.startDate)
const mEndDate = moment(ticket.endDate)
const serviceLength = mStartDate.diff(mEndDate,'hours')
const duration = moment.duration(serviceLength,"hours").humanize()
const weekOfYear = mStartDate.format('WW')
const dayOfWeek = mStartDate.format("d")
if(!groupedData.hasOwnProperty(weekOfYear)){
groupedData[weekOfYear] = {}
}
if (!groupedData[weekOfYear].hasOwnProperty(dayOfWeek)) {
groupedData[weekOfYear][dayOfWeek] = []
}
service["mStartDate"] = mStartDate
service["mEndDate"] = mEndDate
service["serviceLength"] = serviceLength
service["duration"] = duration
groupedData[weekOfYear][dayOfWeek].push(service)
})
})
}
}
2nd Approach, make a simple api call
class TicketRepository extends BaseRepository {
getDataByID(postData) {
return this.post('api/lookup', postData)
}
}
Change the json in the action before dispatching
export function getDataByID() {
return (dispatch, getState) => {
dispatch(dataLookupRequest())
const state = getState()
const groupedData = {}
return TicketRepository.getDataByID(userData)
.then(result => {
const groupedData = {}
return result.map(ticket => {
const mStartDate = moment(ticket.startDate)
const mEndDate = moment(ticket.endDate)
const serviceLength = mStartDate.diff(mEndDate,'hours')
const duration = moment.duration(serviceLength,"hours").humanize()
const weekOfYear = mStartDate.format('WW')
const dayOfWeek = mStartDate.format("d")
if(!groupedData.hasOwnProperty(weekOfYear)){
groupedData[weekOfYear] = {}
}
if (!groupedData[weekOfYear].hasOwnProperty(dayOfWeek)) {
groupedData[weekOfYear][dayOfWeek] = []
}
service["mStartDate"] = mStartDate
service["mEndDate"] = mEndDate
service["serviceLength"] = serviceLength
service["duration"] = duration
groupedData[weekOfYear][dayOfWeek].push(service)
})
return groupedData
})
.then(groupedData => {
dispatch(lookupSuccess(groupedData))
})
.catch(err => dispatch(dataLookupFailure(err.code, err.message)))
}
}
All data manipulation should be handled by your reducer. That is, the returned response data should be passed on to a reducer. This practice is common, because this way if there's a problem with your data, you will always know where to look - reducer. So neither of your approaches is "correct". Actions should just take some input and dispatch an object (no data manipulation).
When you want to manipulate data for 'view' purposes only, consider using reselect library, which makes it easier to handle "data views" that are composed of the existing data.
I have tried everything I know to do but I cannot seem to get this to work...
My goal is to show tickets submitted in the past two weeks. I have already done all the logic on the back side of my MVC project but I cannot seem to display it properly. I just get a blank line graph with the legend to the right. I have provided my Razor code and the JSON return data. Please help. Thanks.
#(Html.Kendo().Chart<NewTicketsTwoWeekGraph>()
.Name("TwoWeekTickets")
.DataSource(dataSource => dataSource
.Read(read => read.Action("NewTicketsData_Read", "Home"))
)
.Series(series =>
{
series.Line(d => d.TicketCount).Name("Ticket Count");
})
.CategoryAxis(axis => axis
.Categories(t => t.TicketDate).Date().BaseUnit(ChartAxisBaseUnit.Days)
.Labels(labels => labels.Rotation(-90))
.Crosshair(c => c.Visible(true))
)
.ValueAxis(axis => axis.Numeric()
.Labels(labels => labels.Format("{0:N0}"))
.MajorUnit(10)
)
)
JSON Return:
{"Data":[{"TicketCount":1,"TicketDate":"\/Date(1426651200000)\/","TicketDateString":"2015-03-18"},
{"TicketCount":2,"TicketDate":"\/Date(1426564800000)\/","TicketDateString":"2015-03-17"}],"Total":2,"AggregateResults":null,"Errors":null}
The problem was with the JSON data being sent back. It doesnt like be wrapped in the "Data" array.
So I changed up my ActionResult to fix this...
public ActionResult _NewTicketCtOverTwoWeeks_Read([DataSourceRequest]DataSourceRequest request, string username)
{
using (var ctx = new GuardianContext())
{
var startDate = DateTime.Now.AddDays(-14);
var graphData = from ticket in ctx.TICKETS
where ticket.CREATED > startDate
group ticket by DbFunctions.TruncateTime(ticket.CREATED)
into a
orderby a.Key
select new TicketCount() { TicketCt = a.Count(), TicketDate = (DateTime)a.Key, TicketDateString = a.Key.ToString().Substring(0, 10) };
return Json(graphData.ToList());
}
}
So now my JSON request returns the following...
[{"TicketCt":2,"TicketDate":"\/Date(1426564800000)\/","TicketDateString":"2015-03-17"},{"TicketCt":11,"TicketDate":"\/Date(1426651200000)\/","TicketDateString":"2015-03-18"},{"TicketCt":20,"TicketDate":"\/Date(1426737600000)\/","TicketDateString":"2015-03-19"}]
I have a two kendo multiselect and i want my roles multiselect to sort of cascade from the other in a way that on the roles multiselect read i want to post a list of the values selected in my systems multiselect. This is what my roles multiselect looks like:
#(Html.Kendo().MultiSelect()
.Name("Roles")
.DataTextField("Name")
.DataValueField("Id")
.Placeholder("Select roles")
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetRoles", "UserAdmin").Data("additionalItemsGetRoles");
})
.ServerFiltering(true);
})
)
<script>
function additionalItemsGetRoles() {
var multiselect = $("#Systems").data("kendoMultiSelect").dataItems();
var length = multiselect.length;
var systems = [];
for (var i = 0; i < length; i++) {
systems.push({
Name: multiselect[i].Name,
SystemId: multiselect[i].SystemId,
Description: multiselect[i].Description
});
}
var json = JSON.stringify(systems);
console.log(json);
return json;
}
</script>
and here is what my action method looks like:
public ActionResult GetRoles([DataSourceRequest] DataSourceRequest request, IList<SystemViewModel> systems)
{
And here is what my console.log(json) shows in the console.
And here is my viewmodel:
public string SystemId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
I tried to set the action method as [httpPost] but then it can't find the method at all.
Everytime it posts to the controller it get null. What am i doing wrong here?
OK, if I understand correctly, what you want to do is to filter one multiselect list, based on the selected items in another multiselect list. Yes?
I am actually doing something similar, and here is what I did:
First, setup the two MultiSelect "widgets"
Set the Change event on the first MultiSelect ("regionChange")
Set the .Data parameter of DataSource to a js function ("regionfilter")
#(Html.Kendo().MultiSelect()
.Name("region")
.DataTextField("Name")
.DataValueField("Id")
.AutoBind(false)
.Events(e => e.Change("regionChange"))
.DataSource(ds => ds
.Read(read => read.Action("GetRegionsForFilter", "Authorization")
)
)
#(Html.Kendo().MultiSelect()
.Name("branch")
.DataTextField("Name")
.DataValueField("Id")
.AutoBind(false)
.DataSource(ds => ds
.Read(read => read.Action("GetBranchesForFilter", "Authorization")
.Data("regionfilter"))
.ServerFiltering(true)
)
)
Define js functions (I have an additional function to get the MultiSelect values, because I am using this on a couple of other MultiSelect "widgets" no the page, AND I am actually doing reverse filtering for some (such as Branch/Region though I snipped out the filtering being done on region)
function regionChange() {
var value = this.value(),
grid = $("#grid").data("kendoGrid");
<-- snipped logic related to filtering grid -->
$('#branch').data('kendoMultiSelect').dataSource.read();
}
function regionfilter() {
var values = getMultiSelectValues("region");
return { regions: values.toString() };
}
function getMultiSelectValues(multiSelectControl) {
var multiSelect = $("#" + multiSelectControl).data("kendoMultiSelect");
var values = multiSelect.value($("#value").val());
return values;
}
Finally, in my controller I am just returning a JsonResult (get request) that accepts a string argument (comma separated list of strings)
public JsonResult GetBranchesForFilter(string regions)
{
var list = _repository.Branches().GetAll().Select(x => new { x.Id, x.Name, x.RegionId });
if (!String.IsNullOrWhiteSpace(regions))
list = list.Where(x => regions.Contains(x.RegionId.ToString()));
return Json(list.OrderBy(o => o.Name), JsonRequestBehavior.AllowGet);
}