kendo ui grid repeat the destroy action on error - asp.net-mvc

I have a grid similar to the code below.
if an errors occurs on destroy action they accumulate.
for example the on the first error 1 call to destroy action, the second 2 calls, the third error 3 calls .......
after some investigation I found out that the items to be sleeted are stored in an array (_destroyed) in the dataSource,
so every time the destroy button is clicked the destroy action is called for each of these items.
I tried assigning null to the _destroyed array but this gave js error when I called destroy action again.
#(Html.Kendo().Grid<someType>()
.Name("grid")
.Columns(columns =>
{
columns.Bound(p => p.Name);
columns.Command(command =>
{
command.Edit();
command.Destroy();
}).Width(250);
})
.Editable(editable => { editable.Mode(GridEditMode.PopUp); editable.TemplateName("myTemplate"); })
.Pageable()
.Sortable()
.Filterable()
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(50)
.Events(events => events.Error("error_handler"))
.Model(model => model.Id(p => p.Id))
.Create(update => update.Action("EditingPopup_Create", "myController"))
.Read(read => read.Action("EditingPopup_Read", "myController"))
.Update(update => update.Action("EditingPopup_Update", "myController"))
.Destroy(update => update.Action("EditingPopup_Destroy", "myController"))
)
<script type="text/javascript">
function error_handler(e) {
if (e.errors) {
var message = "Errors:\n";
$.each(e.errors, function (key, value) {
if ('errors' in value) {
$.each(value.errors, function () {
message += this + "\n";
});
}
});
alert(message);
$(".k-grid").each(function () {
var grid = $(this).data("kendoGrid");
if (grid !== null && grid.dataSource == e.sender) {
grid.one('dataBinding', function (e) {
e.preventDefault();
});
grid.dataSource.read();
grid.refresh();
}
});
}
}
</script>
a

You can call the cancelChanges method of the data source and then read. There is no need to call grid.refresh() as it will be called automatically when dataSource.read() is done.

Ok I'm not sure If that's a legitimate answer but this fixed my problem
grid.dataSource._destroyed = [];
grid.dataSource.read();
grid.refresh();
I hope it saves some's time.

Related

Kendo.Grid Delete Acton does not invoke the Controller's method even though I'm doing the same as in telerik example

I have the following Kendo().Grid():
#(Html.Kendo().Grid<TicketReportPropertyEntity>()
.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(command => command.Destroy()).Width(150);
})
.Events(events => events.DataBound("Databound").SaveChanges("SaveGrid").Edit("Edit"))
.ToolBar(toolbar =>
{
toolbar.Create();
toolbar.Save();
})
.Editable(editable => editable.Mode(GridEditMode.InCell))
.Navigatable()
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.ServerOperation(false)
//.Events(events => events.Error("error_handler"))
.Model(model =>
{
model.Id(c => c.ID);
model.Field(c => c.PropertyName);
model.Field(c => c.Amount);
})
.Create(create => create.Action("AddTicketReportProperty", "TicketReportProperty").Data("GetData"))
.Read(read => read.Action("GetData", "TicketReportProperty", Model))
.Update(update => update.Action("UpdateTicketReportProperty", "TicketReportProperty"))
.Destroy(delete => delete.Action("DeleteTicketReportProperty", "TicketReportProperty"))
)
)
And my controller's method:
[HttpPost]
public ActionResult DeleteTicketReportProperty([DataSourceRequest] DataSourceRequest request, TicketReportPropertyModel model)
{
var result = new TicketReportPropertyModel().DeleteTicketReportProperty(model.ID);
return Json(new[] { model }.ToDataSourceResult(request, ModelState));
}
Here is a "SaveGrid" function:
function SaveGrid(e) {
console.log("save")
var rowsCount = e.sender.dataSource.data().length;
var totalSum = 0;
if (rowsCount > 0) {
for (var i = 0; i < rowsCount; i++) {
totalSum += e.sender.dataSource.data()[i].Amount;
}
}
var ticketAmount = $('#Ticket_Amount').val();
console.log(ticketAmount);
if (totalSum != ticketAmount) {
console.log("failed");
//show the popup
e.preventDefault();
}
}
It should add, update and delete records. Now, I'm working on deleting the record. But the event does not call the controller.
I'm following telerik example here
What am I missing?
I believe you are not following exactly the example from telerik. When you create the Grid you're using an Entity TicketReportPropertyEntity, but in the Controller you are receiving a Model TicketReportPropertyModel.
Make sure you create your Grid using the Model, not the Entity, as the example you're following.
#(Html.Kendo().Grid<TicketReportPropertyModel>()

Kendo grid delete button passing the details of the row

I have a kendo grid in which I want to form delete button to pass the details of the row to the controller (as I am using mvc). Then I will use these details to delete the row from the database.
I searched in kendo website and I found the part for the delete or destroy call but I don't know how to pass these details of the row to the controller.
#(Html.Kendo().Grid<Kendo.Mvc.Examples.Models.ProductViewModel>()
.Name("Grid")
.Columns(columns => {
columns.Bound(p => p.ProductName);
columns.Bound(p => p.UnitPrice).Width(140);
columns.Bound(p => p.UnitsInStock).Width(140);
columns.Bound(p => p.Discontinued).Width(100);
columns.Command(command => command.Destroy()).Width(110);
})
.ToolBar(toolbar => {
toolbar.Create();
toolbar.Save();
})
.Editable(editable => editable.Mode(GridEditMode.InCell))
.Pageable()
.Navigatable()
.Sortable()
.Scrollable()
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.PageSize(20)
.ServerOperation(false)
.Events(events => events.Error("error_handler"))
.Model(model => model.Id(p => p.ProductID))
.Create("Editing_Create", "Grid")
.Read("Editing_Read", "Grid")
.Update("Editing_Update", "Grid")
.Destroy("Editing_Destroy", "Grid")
)
)
My grid:
<div id="grid">
#(Html.Kendo().Grid<dynamic>()
.Name("BrowseGrid")
.Columns(columns =>
{
foreach (System.Data.DataColumn c in Model.Grid.Columns)
{
columns.Bound(c.ColumnName).EditorTemplateName("String");
}
columns.Command(command =>
{
command.Destroy();
command.Custom("").Text("editteam").HtmlAttributes(new { id = "editteam", onClick = "editRow()" });
});
})
.Scrollable()
.DataSource(dataSource => dataSource
.Ajax()
.Events(events => events.Error("error_handler"))
.Model(model =>
{
//Define the model
foreach (System.Data.DataColumn column in Model.Grid.Columns)
{
model.Field(column.ColumnName, column.DataType);
model.Id("Id");
}
})
.Read(read =>
read.Action("BrowseGrid", "Configuration")
)
.Destroy( update => update.Action("Delete", "Configuration"))
//Data("{nodeID:"+#Model.nodeID+"}"
)
.Pageable(pageable => pageable
.Refresh(true)
.PageSizes(new int[] { 10, 100, 1000, 10000, 100000, 1000000 })
.ButtonCount(10)
)
)
Please change to:
.Destroy(update => update.Action("Process_Destroy", "controller name"))
and in controller,
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Process_Destroy([DataSourceRequest] DataSourceRequest request, ProductViewModel product)
{
if (product != null)
{
//write your code for delete action;
}
return Json(ModelState.ToDataSourceResult());
}
This will work.
I am assuming you only need the id of the row to delete it, but I think you can send the full model of the selected row. I am sending both.
Like this:
.Destroy(d => d.Action("controllerMethod", "controllerName").Data("jsFunction"))
And in javascript:
function jsFunction() {
var grid = $("#BrowseGrid").data("kendoGrid");
var id = grid.dataItem(grid.select()).Id;
return {
id: id
};
}
Your controller method should receive the parameter:
public ActionResult controllerMethod(string id)

Kendo UI ASP.NET MVC Kendo Grid Edit Popup Globalization Validation Issue

i have a Kendo grid that uses an edit Popup. The culture in the client is de-DE, when an user tries to edit a decimal number i.e. 8,5 Kendo sends a null to the server for that value and returns validation message "The value 8.5 is not valid for 'PropertyName'". Any help will be greatly appreciated.
#{
var culture = System.Globalization.CultureInfo.CurrentCulture.ToString();
}
<script src="#Url.Content("~/Scripts/Kendo/cultures/kendo.culture." + culture + ".min.js")"></script>
jQuery.extend(jQuery.validator.methods, {
date: function (value, element) {
return this.optional(element) || kendo.parseDate(value) != null;
},
number: function (value, element) {
return this.optional(element) || kendo.parseFloat(value) != null;
}
});
<script type="text/javascript">
kendo.culture("#culture");
</script>
#(Html.Kendo().Grid<SalesToolkit.ViewModels.AdminEquipDimViewModel.Equipment>()
.Name("grdEquipDim")
.Columns(columns =>
{
columns.Bound(dim => dim.EquipmentID).Width(50).Title("ID").Hidden(true);
columns.Bound(dim => dim.EquipmentName).Width(140).Title("Equipment Name");
columns.ForeignKey(dim => dim.EquipmentTypeID, (System.Collections.IEnumerable)ViewData["EquipTypes"], "EquipmentTypeID", "EquipmentName").Width(60).Title("Type");
columns.Bound(dim => dim.Metric).Width(55);
columns.Bound(dim => dim.ModelVerified).Width(65);
columns.Bound(dim => dim.LastModifiedBy).Width(130);
columns.Bound(dim => dim.TimeStamp).Format("{0:MM/dd/yyyy hh:mm:ss}").Width(120);
columns.Command(command => { command.Edit(); command.Destroy(); }).Width(160);
})
.ToolBar(toolbar => toolbar.Create())
.Editable(editable => editable.Mode(GridEditMode.PopUp))
.Scrollable()
.HtmlAttributes(new { #class = "dimGrid" })
.Sortable()
.Pageable()
.DataSource(dataSource => dataSource
.Ajax()
.Model(model =>
{
model.Field(dim => dim.EquipmentID).Editable(false);
model.Field(dim => dim.TimeStamp).Editable(false);
model.Field(dim => dim.LastModifiedBy).Editable(false);
})
.Events(events => events.Error("error"))
.Model(model => model.Id(dim => dim.EquipmentID))
.Create(update => update.Action("EquipDim_Create", "AdminEquipDim", new { mID = Model.ManufacturerID }))
.Read(read => read.Action("EquipDim_Read", "AdminEquipDim", new { mID = Model.ManufacturerID }))
.Update(update => update.Action("EquipDim_Update", "AdminEquipDim"))
.Destroy(update => update.Action("EquipDim_Delete", "AdminEquipDim"))
)
)
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EquipDim_Update([DataSourceRequest] DataSourceRequest request, AdminEquipDimViewModel.Equipment equipMfg)
{
if (equipMfg != null && ModelState.IsValid)
{
AdminEquipDim oEquipMfg = new AdminEquipDim();
oEquipMfg.UpdateEquipDim(equipMfg);
}
return Json(ModelState.ToDataSourceResult());
}

How can I store value inside Telerik Grid in ASP.Net MVC?

I am using Telerik grid in ASP.Net MVC, I want to store "Key" in ViewBag/Session and pass it to controller, "Key" is bound with column of telerik grid.
How can I achieve it?
#(Html.Telerik().Grid()
.Name("Grid")
.Columns(columns =>
{
columns.Bound(e => e.Key).Width(140);
})
.ClientEvents(events => events.OnRowDataBound("OnRowDataBound"))
.DetailView(details => details.ClientTemplate(
Html.Telerik().TabStrip()
.Name("TabStrip_<#= Key #>")
.SelectedIndex(0)
.Items(items =>
{
items.Add().Text("Resolution").Content(
Html.Telerik().Grid<AnotherModel>()
.Name("Resolutions_<#= Key #>")
.Columns(columns =>
{
columns.Bound(f => f.CreatedDate).Width(140).Title("Date");
columns.Bound(f => f.Name).Title("Name");
})
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("MyControllerAction2", "MyController",PASS_KEY_HERE_EACH_TIME))
//Here instead of PASS_KEY_HERE_EACH_TIME, I need 'Key' to pass here of FirstModel
.Pageable()
.Sortable()
.Filterable()
.Scrollable()
.ToHtmlString()
);
})
.ToHtmlString()
))
.DataBinding(dataBinding => dataBinding.Ajax().Select("MyControllerAction1", "MyController"))
.Pageable()
.Scrollable(scrolling => scrolling.Height(500))
.Sortable()
)
controller I want to call each time at binding
public ActionResult MyControllerAction2(string myKey)
{
var test = new List<Data>();
ProjectDetail projectDetail = new ProjectDetail();
test = projectDetail.GetData("KEY-123");
// Actually I want to use Key here which is passed in View at DataBinding(),
so that I can get all data I need
return View(new GridModel(test));
}
You can get this value in your OnRowDataBound(e) javascript function:
function onRowDatabound(e) {
var currentKey = e.dataItem.Key;
//send currentKey to controller
}
Update:
If you have master:detail, you can use next:
.DetailView(details => details.ClientTemplate("<div style='float:left' class='e-function-grid-detail' data-Key='<#= Key #>'>" +
Html.Telerik().TabStrip()
.Name("TabStrip_<#= Key #>")
.SelectedIndex(0)
.Items(items =>
{
items.Add().Text("Resolution").Content(
Html.Telerik().Grid<AnotherModel>()
.Name("Resolutions_<#= Key #>")
.Columns(columns =>
{
columns.Bound(f => f.CreatedDate).Width(140).Title("Date");
columns.Bound(f => f.Name).Title("Name");
})
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("MyControllerAction2", "MyController"))
.Pageable()
.Sortable()
.Filterable()
.Scrollable()
.ClientEvents(ce => ce.OnDataBinding("function(e){ addKeyToRequest(this, e);}")
.ToHtmlString()
);
})
.ToHtmlString()
)) + "</div>"
and write js function
function addKeyToRequest(sender, e) {
var key= $(sender).closest('div.e-function-grid-detail').data("Key");
if (e.data == null || e.data == '') {
e.data = new Object();
}
e.data.Key = key;
}
This function is called before post for data to controller and add your key value to post.

Kendo UI Grid Only Expand One Row at a Time

I have a Kendo Grid which I would like to only be able to expand one row at a time for detail editing. What is the simplest way to do this?
#(Html.Kendo().Grid<MyModel>()
.Name("MyGrid")
.ClientDetailTemplateId("MyTemplate")
.Columns(columns =>
{
columns.Bound(b => b.Code);
columns.Bound(b => b.Name);
columns.Bound(b => b.Description);
...
columns.Command(cmd => { cmd.Edit(); cmd.Destroy(); });
})
.ToolBar(toolbar => toolbar.Create())
.Editable(editable => editable.Mode(GridEditMode.InLine))
.DataSource(dataSource => dataSource
.Ajax()
.Model(model => model.Id(a => a.Id))
.Create(create => create.Action("Create", "SysMaint", new { id = Model.ProjectId }))
.Read(read => read.Action("Read", "SysMaint", new { projectId = Model.ProjectId }))
.Update(update => update.Action("Update", "SysMaint"))
.Destroy(destroy => destroy.Action("Destroy", "SysMaint"))
)
)
<script id="MyTemplate" type="text/kendo-tmpl">
#(Html.Kendo().TabStrip()
.Name("TabStrip_#=Id#")
.SelectedIndex(0)
.Items(items =>
{
items.Add().Text("A").LoadContentFrom("MyPartialA", "SysMaint", new { id = "#=Id#" });
items.Add().Text("B").LoadContentFrom("MyPartialB", "SysMaint", new { id = "#=Id#" });
})
.ToClientTemplate()
)
</script>
Ends up this is really simple. Just add these few lines.
...
.Update(update => update.Action("Update", "SysMaint"))
.Destroy(destroy => destroy.Action("Destroy", "SysMaint"))
)
.Events(events => events.DetailExpand("detailExpand"))
)
<script type="text/javascript">
var expandedRow;
function detailExpand(e) {
// Only one open at a time
if (expandedRow != null && expandedRow[0] != e.masterRow[0]) {
var grid = $('#MyGrid').data('kendoGrid');
grid.collapseRow(expandedRow);
}
expandedRow = e.masterRow;
}
</script>
I hope this helps somebody.
That works except it does not remove the old detail row. Add the bit marked NEW to remove each previously opened detail row.
if (expandedRow != null && expandedRow != e.masterRow[0]) {
var grid = $('#RequestsGrid').data('kendoGrid');
grid.collapseRow(expandedRow);
expandedRow[0].nextElementSibling.remove(); //NEW
}
expandedRow = e.masterRow;
Building on Trey's answer, this version will work generically for any grid (using #vikasde's suggestion), and will also work when you have nested grids, so that the child grid when invoking the detailExpand, won't collapse its parent grid row as a side effect.
<script type="text/javascript">
function detailExpand(ev) {
var expandedRow = $(ev.sender.wrapper).data('expandedRow');
// Only one open at a time
if (expandedRow && expandedRow[0] != ev.masterRow[0]) {
var grid = $(ev.sender.wrapper).data('kendoGrid');
grid.collapseRow(expandedRow);
}
$(ev.sender.wrapper).data('expandedRow', ev.masterRow);
}
</script>
Further to the answers already here, I found that by combining hatchet's and Danny Blue's answers and using the DetailCollapse event works nicely, and will remove the underlying detail content if a row is collapsed manually.
MVC Grid configuration:
.Events(ev =>
{
ev.DetailExpand("detailExpand");
ev.DetailCollapse("detailCollapse");
})
Page scripts:
function detailExpand(ev) { // Credit hatchet
var expandedRow = $(ev.sender.wrapper).data('expandedRow');
// Only one open at a time
if (expandedRow && expandedRow[0] !== ev.masterRow[0]) {
var $grid = $(ev.sender.wrapper).data('kendoGrid');
$grid.collapseRow(expandedRow);
}
$(ev.sender.wrapper).data('expandedRow', ev.masterRow);
}
function detailCollapse(ev) {
var expandedRow = $(ev.sender.wrapper).data('expandedRow');
expandedRow.next().remove(); // Credit Danny Blue
}

Resources