Monthpicker in Kendo UI for MVC works but sets current month when changing focus from grid cell - kendo-grid

I have set up a "monthpicker" in a cell of a Kendo grid. The picker works fine and the column shows MMMM yyyy (e.g. April 2019)
However, when I move focus from the cell it doesn't set the cell as dirty and reverts back to current month and year.
Editor template (called Month.cshtml)
#model DateTime?
#{string[] formats = { "MMMM yyyy" }; }
#(Html.Kendo().DatePickerFor(m => m)
.Name("monthpicker")
.Start(CalendarView.Year)
.Depth(CalendarView.Year)
.Format("MMMM yyyy")
.DateInput()
.Culture("en-US")
.ParseFormats(formats)
.HtmlAttributes(new { style = "width: 100%", title = "monthpicker" })
)
Model:
[Display(Name = "Month", ResourceType = typeof(Resources.Resources))]
[UIHint("Month")]
public DateTime Month { get; set; }
View
#(Html.Kendo().Grid<GrindrodDataCapture.Models.MonthlyOceanPlan>()
.Name("grid")
.Columns(columns =>
{
columns.Bound(c => c.Month).Format("{0:MMMM yyyy}");
//etc
})
.ToolBar(toolbar =>
{
toolbar.Create();
toolbar.Save();
})
.Editable(editable => editable.Mode(GridEditMode.InCell))
.Pageable()
.Sortable(sortable =>
{
sortable.SortMode(GridSortMode.SingleColumn);
})
.Filterable()
.DataSource(dataSource => dataSource
.Ajax()
.Events(events => events.Error("error_handler"))
.Sort(p => { p.Add("Month").Descending(); })
.Model(model => model.Id(p => p.ID))
.Read(read => read.Action("MonthlyOceanPlans_Read", "MonthlyOceanPlanGrid"))
.Create(create => create.Action("MonthlyOceanPlans_Create", "MonthlyOceanPlanGrid"))
.Update(update => update.Action("MonthlyOceanPlans_Update", "MonthlyOceanPlanGrid"))
.Destroy(destroy => destroy.Action("MonthlyOceanPlans_Destroy", "MonthlyOceanPlanGrid"))
)

I got a support response from Telerik which fixed it :)
"Hi Evan,
I noticed that the name of the date picker editor does not match the name of the field it edits. The editor is bound to its corresponding field of the model using the name setting. Currently the binder will try to bind the editor to the monthpicker field of the model. However, the actual field in the model is called Month.
Furthermore, when using a WidgetFor helper, you can omit the name configuration as the name is automatically set to the name of the field.
Could you pleas remove the Name configuration of the editor and let me know if the editor binds as expected?"

Related

Kendo.Grid does not send some data to the controller on "Create" operation

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
}

Persist State In Kendo MVC Grid With Custom Command Columns?

Having issues persisting state in mvc grid when using custom command columns. Here is the grid's wrapper
#(Html.Kendo().Grid < Weighmaster_Web.Data.Entity.Destination > ()
.Name("grid")
.Columns(columns => {
columns.Bound(c => c.Description);
columns.Bound(c => c.CODE);
columns.Command(c => {
if (bUpdate) c.Custom("Edit").Click("editItem");
if (bDelete) c.Custom("Delete").Click("deleteItem");
}).Width(175);
})
.Scrollable()
.Groupable()
.Sortable()
.ToolBar(toolbar => {
if (bCreate) {
toolbar.Create().HtmlAttributes(new {
id = "addDestination"
}).Text("Add Destination");
}
})
.ToolBar(t => t.Excel())
.Excel(excel => excel
.FileName("Destinations.xlsx")
.Filterable(true)
.AllPages(true)
.ProxyURL(Url.Action("Excel_Export_Save", "MaterialTransaction"))
)
.Filterable(filterable => filterable.Extra(false))
.Resizable(resize => resize.Columns(true))
.Reorderable(reorder => reorder.Columns(true))
.Pageable(pageable => pageable
.Refresh(true)
.PageSizes(true)
.ButtonCount(5))
.DataSource(dataSource => dataSource
.Ajax()
.Events(events => events.Error("error_handler"))
.Read(read => read.Action("DestinationIndex", "Destination").Type(HttpVerbs.Post))
.Model(model => model.Id(p => p.Id))
.PageSize(20)
.Create(update => update.Action("DestinationSave", "Destination").Type(HttpVerbs.Post)))
)
Here i define a click event handler in the wrapper for both edit and delete buttons. i am using custom commands so that i may define custom edit template.
When you look at the actual jquery for this wrapper , i can see the event handler defined.
Then when you leave the page , this code is ran to save the grid's state in a cookie :
$(window).unload(function () {
var grid = $("#grid").data("kendoGrid");
var dataSource = grid.dataSource;
var state = {
columns: grid.columns,
page: dataSource.page(),
pageSize: dataSource.pageSize(),
sort: dataSource.sort(),
filter: dataSource.filter(),
group: dataSource.group()
};
$.cookie(username + "DestinationGridState", JSON.stringify(state), { expires: 365 });
})
The grid's state is read from cookie in $(document).ready like this :
$(document).ready(function () {
var grid = $("#grid").data("kendoGrid");
var toolbar = $("#grid").find(".k-grid-toolbar").html();
var state = $.cookie(username + "DestinationGridState");
if (state) {
state = JSON.parse(state);
var options = grid.options;
options.columns = state.columns;
options.dataSource.page = state.page;
options.dataSource.pageSize = state.pageSize;
options.dataSource.sort = state.sort;
options.dataSource.filter = state.filter;
options.dataSource.group = state.group;
if (grid) {
grid.destroy();
//grid.wrapper.html("");
}
$("#grid").empty().kendoGrid(options).find(".k-grid-toolbar").html(toolbar);
}
});
After the grid's state is read from the cookie, no click event handler is defined for the custom edit command button. So , i guess my question is; How do i correctly save the state of the grid so that my custom command buttons will retain their event handlers?
As mentioned in the kendo documentation:
JSON.stringify() cannot serialize function references (e.g. event
handlers), so if stringification is used for the retrieved Grid state,
all configuration fields, which represent function references, will be
lost.
I once had the same issue, when I was trying to save the filters values in session. I was doing just like you, but then I realized that I didn't need to restore the columns state. If you remove the row options.columns = state.columns;
the custom command will work just as expected.
Hope it helps.
I had a similar problem. After loading the settings my custom delete button stopped working. This was the solution that I came up with:
Save the original grid options. After parsing the saved settings, restore the original values, in this case the column where my delete buttons was placed.
Hope this helps.
$("#Grid").on("click",
".loadsetting",
function(e) {
var grid = $("#Grid").data("kendoGrid");
var originalOptions = grid.options; // Save original options
e.preventDefault();
var options = localStorage["msettings"];
if (options) {
var parsedOptions = JSON.parse(options);
parsedOptions.toolbar = [
{
template: $("#toolbarTemplate").html()
}
];
// Restore values
parsedOptions.columns[30] = originalOptions.columns[30];
grid.setOptions(parsedOptions);
}
});

Kendo Grid: issue with DataType based Filter

I have one data table and it contains columns of different datatype(e.g String, Date Time, Boolean and etc..), Following is code snippet that I have written in .cshtml file
#(Html.Kendo().Grid(Model.ViewResultTable)
.Name("Grid").Columns(columns =>
{
foreach (DataColumn column in Model.ViewResultTable.Columns.Cast<DataColumn>().Where(column => column.ColumnName != "XYZ"))
{
columns.Bound(typeof(DateTime) ,column.ColumnName).Title(column.ColumnName).Filterable(true).Width(120);
}
})
.Pageable(pageable => pageable
.PageSizes(new[] { 15, 20, 50, 100, 200, 500 })
.Messages(a => a.ItemsPerPage(""))
.PreviousNext(false)
.ButtonCount(5)
)
.Filterable(f => f.Extra(false))
.Groupable()
.ClientDetailTemplateId("template123")
.DataSource(
dataSource => dataSource
.Ajax()
.PageSize(15)
.ServerOperation(false)
)
.TableHtmlAttributes(new { #class= "grid" })
)
It takes all the type as string even defined type as "Date" or "Number". Does anyone having idea how to fix?

how to get data from JSON without using script?

i want to get data from json file without using script code!
iam using MVC4 and want to put the code in the .cshtml file, how can i do this?
( Iam using kendo function)
example:
#{
ViewBag.Title = "Home Page";
}
<div class="chart-wrapper">
#(Html.Kendo().Chart()
.Name("chart")
.Title(title => title
.Text("Share of Internet Population Growth, 2007 - 2012")
.Position(ChartTitlePosition.Bottom))
.Legend(legend => legend
.Visible(false)
)
.DataSource(dataSource=>dataSource.Read(read=>read.Url("~/")))
.Events(e => e.SeriesClick("onSeriesHover"))
.Series(series => {
series.Pie(new dynamic[] {
new {category="Asia",value=53.8,color="#9de219"},
new {category="Europe",value=16.1,color="#90cc38"},
new {category="LatinAmerica",value=11.3,color="#068c35"},
new {category="Africa",value=9.6,color="#006634"},
new {category="MiddleEast",value=5.2,color="#004d38"},
new {category="NorthAmerica",value=3.6,color="#033939"}
})
.Labels(labels => labels
.Template("#= category #: #= value#%")
.Background("transparent")
.Visible(true)
.Color("Red")
)
.StartAngle(150);
})
.Tooltip(tooltip => tooltip
.Visible(true)
.Format("{0}%")
)
)
<script>
function onSeriesClick(e) {
alert(kendo.format("Series click :: {0} ({1}): {2}",
e.series.name, e.category, e.value));
}
</script>
</div>
i have use
.DataSource(dataSource=>dataSource.Read(read=>read.Url("~/")))
but not working
Try like this,
Example
View
#(Html.Kendo().Chart<Model.DashboardPieChartModel>()
.Name("PieChartPopup")
.Events(events => events.DataBound("onDataBound"))
.Legend(legend => legend
.Visible(false)
)
.DataSource(ds =>
{
ds.Read(read => read.Action("Read_PieChart", "Dashboard"));
}
)
.Series(series =>
{
series.Pie(
model => model.Percentage,
model => model.Service, null, null
).Labels(labels => labels
.Visible(true)
.Template("${ category } - ${ value }%")
).Overlay(ChartPieSeriesOverlay.None);
})
.Tooltip(tooltip => tooltip
.Visible(true)
.Template("${ category } - ${ value }%")
)
)
Controller
public JsonResult Read_PieChart()
{
//Whatever you do here
return Json(return your data);
}
Read this link: http://demos.kendoui.com/dataviz/pie-charts/remote-data.html

Editable Detail Template Kendo UI Grid

Update
I have managed to get the form rendering (see image) with values and editor templates.
Sorry for blacked out labels. NDA requirements...
This now works across multiple rows because I am using the uid of the row to give the detail template a unique name doing this:
#(Html.Kendo().TabStrip()
.Name("Details_#=uid#")
I've updated the source below to the latest version and have included a list of issues that I'd still love help with.
Outstanding issues:
Serializing the detail template along with the grid when I save changes
Assign labels and validation rules using Data Annotations on the View Model (This doesn't seem to work since I can't seem to get Html helpers working. Advice would be great!
Orignal Post text (Source code has been updated)
I am trying to create a Batch Grid where each item contains a detail template.
Each detail template contains a tabstrip that I want to hold extra form data within.
Right now, I have the standard batch grid working, but I can't seem to get the information on screen for each item AND have it be editable. Also, the detail template breaks when more than one row is present. The template still renders, but buttons are non-functional because both templates have the same id understandably breaking the user's ability to interact, but I'm unsure of how to ensure unique identifiers for each row template (maybe use the parent uid somehow?)
I am also unsure of how to serialize this form data once I have completed this first step, but I can make a separate question for that should it prove necessary.
Grid
#(Html.Kendo().Grid(Model.ItemModelList)
.Name("ItemGrid")
.Columns(columns =>
{
//Other columns omitted for brevity
columns.Bound(i => i.Description).Width(100);
columns.Command(command =>
{
command.Destroy();
}).Width(60);
})
.ClientDetailTemplateId("ItemDetails")
.ToolBar(toolbar =>
{
toolbar.Create();
toolbar.Save();
})
.Editable(editable => editable.Mode(GridEditMode.InCell).CreateAt(GridInsertRowPosition.Bottom))
.Pageable()
.Sortable()
.Scrollable()
.Resizable(resize => resize.Columns(true))
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.ServerOperation(false)
.Events(events =>
{
events.Error("ItemGrid_ErrorHandler");
})
.Model(model =>
{
model.Id(i => i.ItemModelID);
model.Field(i => i.DimensionUOM).DefaultValue("in");
model.Field(i => i.WeightUOM).DefaultValue("lbs");
})
.Create(create => create.Action("CreateProducts", "ItemGrid"))
.Read(read => read.Action("GetProducts", "ItemGrid"))
.Update(update => update.Action("UpdateProducts", "ItemGrid"))
.Destroy(destroy => destroy.Action("DeleteProducts", "ItemGrid"))
)
)
Detail Template
<script id="ItemDetails" type="text/kendo-tmpl">
#(Html.Kendo().TabStrip()
.Name("Details_#=uid#")
.SelectedIndex(0)
.Items(items =>
{
items.Add().Text("test").Content(#<div>
<table id="testForm">
<tr>
<td>TEST</td>
</tr>
<tr>
</tr>
</table>
</div>);
items.Add().Text("test2").Content(#<div>
<table id="test2Form">
<tr>
<td><label>A</label></td>
<td><label>B</label></td>
<td><label>C</label></td>
<td><label>D</label></td>
<td><label>E</label></td>
</tr>
<tr>
<td>
<input class="k-textbox" value="#=objectA#">
</td>
<td>
#(Html.Kendo().DropDownList()
.Name("objectB")
.Value("#=objectB#")
.DataTextField("Text")
.DataValueField("Value")
.BindTo(new SelectList((System.Collections.IEnumerable)ViewBag.objectBListing, "Value", "Value"))
.ToClientTemplate()
)
</td>
<td>
#(Html.Kendo().DropDownList()
.Name("objectC")
.Value("#=objectC#")
.DataTextField("Text")
.DataValueField("Value")
.BindTo(new SelectList((System.Collections.IEnumerable)ViewBag.objectCListing, "Value", "Value"))
.ToClientTemplate()
)
</td>
<td><input class="k-textbox" value="#=objectD#"></td>
<td><input class="k-textbox" value="#=objectE#"></td>
</tr>
</table>
</div>);
})
.ToClientTemplate()
)
You should create a model for your extra information and add it as a property of your ItemModelList like this:
public class BaseMode
{
public string UID { get; set; } // Create your own UID, distinguished from Telerik UID by casing.
}
public class ExtraInfoModel : BaseModel
{
[DisplayName("Object A")]
[Required] // For example
public string ObjectA { get; set; }
[DisplayName("Object B")]
[UIHint("DropDownList")]
public int? ObjectB { get; set; }
[DisplayName("Object C")]
public int? ObjectC { get; set; }
public ExtraInfoModel(string uid)
{
this.UID = uid;
}
}
public class ItemModelList : BaseModel
{
public ExtraInfoModel ExtraInfo { get; set; }
public ItemModelList()
{
this.UID = Guid.NewGuid().ToString(); // Not sure about syntax, please review.
this.ExtraInfo = new ExtraInfoModel(this.UID); // Guarantee ExtraInfo.UID is the same as parent UID when you get model from DB.
}
}
Create a partial view for your detail extra info, using a kendo grid in your second tab:
#model ExtraInfoModel
#(Html.Kendo().TabStrip()
.Name("Details_#=UID#")
.SelectedIndex(0)
.Items(items =>
{
items.Add().Text("test").Content(#<text>
<div>
<table id="testForm">
<tr>
<td>TEST</td>
</tr>
<tr></tr>
</table>
</div>
</text>);
items.Add().Text("test2").Content(#<text>
#(Html.Kendo().Grid<ExtraInfoModel>()
.Name("gvDetail_#=UID#")
.Columns(c =>
{
c.Bound(m => m.ObjectA).ClientTemplate(Html.Kendo().TextBox()
.Name("ObjectA")
.HtmlAttributes(new { id = "ObjectA_#=UID#" })
.Value(Model.AgencyCode)
.ToClientTemplate()
.ToHtmlString());
c.Bound(m => m.ObjectB).ClientTemplate(Html.Kendo().DropDownList()
.Name("ObjectB")
.HtmlAttributes(new { id = "ObjectB_#=UID#" })
.Value((Model != null && Model.ObjectB.HasValue) ? Model.ObjectB.ToString() : string.Empty)
.OptionLabel("Select B...")
.BindTo(ViewBag.Data)
.ToClientTemplate()
.ToHtmlString());
// Config ObjectC same as ObjectB.
})
.BindTo(new ExtraInfoModel[] { Model }) // Your detail grid has only one row.
)
</text>);
})
)
In your main view page, using server detail template instead of client detail template. I suggest to use server template because I'm using server binding in step 2. Of course, you can change it to ajax binding or local binding (by defining event OnDetailExpand)
#Html.Kendo().Grid(Model.ItemModelList)
...
.DetailTemplate(#<text>
#Html.Partial("YourPartialName", item.ExtraInfo) // item stands for ItemModelList, which is the binding data item.
</text>)
And the last one, for your first issue, serializing extra info data on save, we should handle change event of each extra info property to set value and dirty flag to master data item. Remember that kendo grid in batch editing only submits dirty data items. Back to step 2:
c.Bound(m => m.ObjectB).ClientTemplate(Html.Kendo().DropDownList()
.Name("ObjectB")
.HtmlAttributes(new { id = "ObjectB_#=UID#" })
.Value((Model != null && Model.ObjectB.HasValue) ? Model.ObjectB.ToString() : string.Empty)
.OptionLabel("Select B...")
.BindTo(ViewBag.Data)
.Events(e => e.Change("onChangeObjectB")) // Added this line
.ToClientTemplate()
.ToHtmlString());
<script type="text/javascript">
function onChangeObjectB(e) {
var selectedValue = this.value();
var sender = e.sender;
if (sender != undefined && sender.length > 0) {
var detailRow = sender.closest(".k-detail-row");
var masterRow = detailRow.prev();
var mainGrid = $("#ItemGrid").data("kendoGrid");
if (mainGrid != null) {
var masterRowIndex = masterRow.parent().find(".k-master-row").index(masterRow);
var dataItem = mainGrid.dataSource._data[masterRowIndex];
dataItem.ObjectB = selectedValue;
dataItem._dirty = true;
}
}
}
</script>
And the save action will work as normal.