Updating Kendo grid row on column value change - asp.net-mvc-4

I am working on an MVC .Net Framework v4.5 site using Kendo MVC controls. One of the pages uses a Kendo grid which is bound to the view model. When I edit a row, there is a field which uses a dropdownlist control, and when the selection is changed, the remaining fields in the row should be updated to reflect that newly selected item..I am doing this with a JSON call which then returns the new view model to use for that row. However, I cannot figure out how to tell the grid to use the data that was returned from that JSON call. Any ideas?
EDIT:
I have attached some code. The issue now is that I can get the grid's datasource to 'refresh', but in the UpdateGridSource jscript function, at the grid.dataSource.read() line, the grid is now out of edit mode, and if I go to edit the row, the data is reverted back to how it originally was
(VIEW)
#(Html.Kendo().Grid<RedFile.Models.QuickQuoteItemView>()
.Name("QuoteItems")
.Columns(c =>
{
c.Bound(p => p.Id).Hidden();
c.Bound(p => p.Description).EditorTemplateName("Combobox");
c.Bound(p => p.ItemQty);
c.Bound(p => p.ItemPrice).Format("{0:c}");
c.Bound(p => p.Total).ClientTemplate("#= kendo.format('{0:c}', Total) #").FooterHtmlAttributes(new { style = "text-align:right;" }).ClientFooterTemplate("#= kendo.format('{0:c}', sum)#").HtmlAttributes(new { style = "text-align:right;" }).Width(100);
c.Command(cmd => { cmd.Edit(); cmd.Destroy(); }).Title("Commands").Width(200);
})
.ToolBar(tb => tb.Create())
.ClientDetailTemplateId("subitems")
.Editable(eb => eb.Mode(GridEditMode.InLine).CreateAt(GridInsertRowPosition.Bottom))
.Events(e=>e.Edit("editGrid"))
.Scrollable()
.DataSource(ds => ds
.Ajax()
.ServerOperation(false)
.Aggregates(a =>
{
a.Add(p => p.LineTotal).Sum();
a.Add(p => p.Total).Sum();
})
.Model(model =>
{
model.Id(p => p.Id);
model.Field(p => p.LineTotal).Editable(false);
model.Field(p => p.ItemPrice).Editable(true);
})
.Read(read => read.Action("GridSelect", "QuickQuote", new { qqid = Model.Id })).ServerOperation(false)
.Create(create => create.Action("GridCreate", "QuickQuote", new { qqid = Model.Id }))
.Update(update => update.Action("GridUpdate", "QuickQuote"))
.Destroy(destroy => destroy.Action("GridDelete", "QuickQuote"))
)
)
<script type="text/javascript">
function dropDownSelectionChanged(e) {
var dataItem = this.dataItem(e.item.index());
UpdateGridSource(dataItem.Value);
}
function UpdateGridSource(DropDownValue) {
var grid = $("#theGrid").data("kendoGrid");
grid.dataSource.transport.options.read.url = '/Controller/refreshGridDataSource?selection=' + DropDownValue;
grid.dataSource.read();
}
</script>
(CONTROLLER)
public JsonResult refreshQuickQuoteTest2([DataSourceRequest] DataSourceRequest request, string selection)
{
QuickQuoteItem qqi = db.QuickQuoteItems.FirstOrDefault(p => p.ItemID == selection);
QuickQuote quickQuoteOriginal = qqi.QuickQuote;
QuickQuoteItemView qqiv = new QuickQuoteItemView();
IEnumerable<QuickQuoteItemView> return2;
qqiv.Id = qqi.Id;
qqiv.ItemID = qqi.ItemID;
qqiv.ItemPrice = qqi.ItemPrice;
if (Session["quickQuote"] != null)
{
QuickQuote qq = (QuickQuote)(Session["quickQuote"]);
if (Session["editingQuickQuoteId"] != null)
{
int row = Convert.ToInt32(Session["editingQuickQuoteId"]);
foreach (var item in qq.QuickQuoteItems)
{
if (item.Id == row)
{
QuickQuoteItem test = new QuickQuoteItem();
StandardItem stdItem = new StandardItem();
stdItem = db.StandardItems.Find(selection);
item.ItemPrice = stdItem.Price;
item.Description = stdItem.Description;
item.ItemID = stdItem.ItemId;
}
}
}
return2 = QuickQuoteItemViewItems(qq);
}
else
{
return2 = QuickQuoteItemViewItems(quickQuoteOriginal);
}
return Json(return2.ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}

You can use the grids Ajax binding to call your controller Update Action method
.DataSource(dataSource => dataSource
.Ajax()
.ServerOperation(false)
.Model(model => model.Id(p => p.SampleId))
.Update(read => read.Action("SampleGrid_Update", "Controller")))
Then you need to decorate your methods datasource parameter with the DataSourceRequest attribute and return the ModelState as a DataSourceResult
[HttpPost]
public ActionResult SampleGrid_Update([DataSourceRequest] DataSourceRequest dsRequest, IEnumerable<SampleViewModel> viewModel)
{
if (viewModel != null && ModelState.IsValid)
{
// other code goes here...
}
return Json(ModelState.ToDataSourceResult());
}
You can view the samples and documentation on the Kendo website
Edit
You can bind your ajax call to the change event of your dropdownlist and when this returns you can refresh the grid data from the server using
$("#gridId").data("kendoGrid").dataSource.read();

Related

Kendo MVC Group Paging

I have a kendo grid that shows around more than a million records and in its initial load, it takes more than a minute to display.
how can I use the kendo Grouppaging feature to minimize the load time so the page loads the group items on demand? Loading of the group items happens when a group is expanded. Any other options to speed up are also appreciated.
what it basically does is that based on the dropdown value it draws the kendo grid
<div>
#(Html.Kendo().DropDownList()
.Name("samplegrid")
.DataTextField("samplegrid")
.DataValueField("samplegrid")
.HtmlAttributes(new { style = "width:90%" })
.DataSource(source => source
.Custom()
.Transport(transport => transport
.Read(read =>
{
read.Action("infoDropdown_Read",
"sample").Type(HttpVerbs.Get);
})))
.Events(e =>
{
e.Select("onCheckSelectForDetail");
})
)
</div>
Here is the code for the Grid.
#(Html.Kendo().Grid<Portal.Data.Models.InfoModel>()
.Name("gridSummary")
.DataSource(dataSource => dataSource
.Ajax()
.Aggregates(aggregates =>
{
aggregates.Add(p => p.OutstandingCount).Sum();
})
.Group(groups =>
{
groups.Add(p => p.OfficeNumber);
})
.Batch(true)
.ServerOperation(false)
.Sort(sort => sort.Add(column => column.Date))
)
.Columns(columns =>
{
columns.Bound(c => c.OfficeState).Width(70)
.Title("xxxx");
columns.Bound(c => c.OfficeNumber).Width(70)
.Title("zzzz");
columns.Bound(c => c.OutstandingCount).Width(60)
.Title("yyyy");
})
)
and the controller looks like
public async Task<IActionResult> infoDropdown_Read(){
try{
var infoListSR = await _eService.GetInfoListAsync();
if (infoListSR.Failed){
Log.Error($"Unexpected error occured due to
{infoListSR.ErrorData.ApplicationErrorMessage}");
throw new DataException($"{infoListSR.ErrorData.ApplicationErrorMessage}");
}
return Json(infoListSR.ResultData, new JsonSerializerOptions() {
PropertyNameCaseInsensitive = false});
}
catch (Exception ex){
Log.Error($"Username: {User.Identity.Name} - Dropdown list Failed with
Exception: {ex}");
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
An option to boost the performance, you could use the server grouping along with virtualization:
Demo

kendodropdownlist not binding value to model after post

I'm using kendo UI for my project. I have a kendo dropdownlist that I'm populating with json. I get the values in my dropdownlist but on post, the model doesn't get the selected value of the dropdownlist. I have been stuck on it for a day with no result. I'm not sure where I'm going. Please review the code
View:
#model IEnumerable<EntityFrameworkClasses.StaggingException>
#foreach (var item in Model)
{
#(Html.Kendo().DropDownListFor(modelItem => item.Level2)
.Name("Level2")
.HtmlAttributes(new { style = "width:10%" })
.OptionLabel("Select level 2...")
.DataTextField("Text")
.DataValueField("Value")
.BindTo((System.Collections.IEnumerable)ViewBag.Level2)
)
}
#(Html.Kendo().Grid(Model)
.Name("CashExceptionsGridTest")
.Columns(columns =>
{
columns.Bound(p => p.Category).Title("Category").Width(130);
columns.Bound(p => p.EnterText1).Title("Comments").Width(130);
columns.Bound(p => p.Dateoftransaction).Title("Date").Width(130);
columns.Bound(p => p.InternalLocalAmount).Title("InternalAmt").Width(130);
columns.Bound(p => p.ExternalLocalAmount).Title("ExternalAmt").Width(130);
})
.ToolBar(toolbar =>
{
//toolbar.Template("<a class='k-button k-button-icontext' onclick='customCommand()' href='#'></span>Cutom Command</a>");
toolbar.Create(); // The "create" command adds new data items.
toolbar.Save();// The "save" command saves the changed data items.
})
.Editable(editable => editable.Mode(GridEditMode.InCell)) // Use in-cell editing mode.
.HtmlAttributes(new { style = "height: 550px;" })
.HtmlAttributes(new { style = "height: 350px;" })
.Pageable(pageable => pageable
.Input(true)
.Numeric(false)
)
.Reorderable(r => r.Columns(true))
.Sortable()
.ColumnMenu()
.Scrollable(scr => scr.Height(430))
.Filterable()
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(20)
.ServerOperation(false)
.Batch(true) // Enable batch updates.
.Model(model =>
{
model.Id(p => p.RowID); // Specify the property which is the unique identifier of the model.
model.Field(p => p.RowID).Editable(false); // Make the ProductID property not editable.
})
.Update("Editing_Update", "MultiTab")
.Create("Editing_Create", "MultiTab")
)
)
}
I have a kendo grid below which im not including for code brevity.
Controller:
public ActionResult GetLevel()
{
IEnumerable<SelectListItem> Level2 = db2.StaggingInternalCashExceptions.Where(x=>x.LoadID==loadid).Select(c => new SelectListItem
{
Value = c.Level2.ToString(),
Text = c.Level2
}).Distinct();
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Editing_Create([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<StaggingException> results)
if (results != null && ModelState.IsValid)
{
foreach (var result in results)
var entity = new StaggingException();
entity.RowID = result.RowID;
entity.Category = result.Category; //this is a textbox in the view for which i get the value
entity.Level1 = result.Level1; //gives null
//I'm adding those values to the db. Didn't include all that for the sake of keeping it short.
}
}
The grid has batch editing, once i hit on save changes, the grid's data is posted to the controller where i can see it in results. I cannot get the dropdown value though.
Any ideas or leads please.
Thank you.
Late to this post but I have had a similar issue, for those with the same problem of the value not being posted in the model, if your model value is a nullable int? (can't see from the above post...) then you need to configure the DropDownListFor as below to avoid the default value binding behavior to update the field with the selected item when the initial value is null. Hope this helps someone.
Html.Kendo().DropDownListFor(m => m)
.HtmlAttributes(new { data_value_primitive = "true" })

Kendo MVC treeview Passing selected node to grid

I'm new to Kendo, I just trying to pass the selected node of treeview to the grid, this is my code:
<div>
#(
Html.Kendo().TreeView()
.Name("treeview")
.BindTo((IEnumerable<TreeViewItemModel>)ViewBag.TreeData)
.Events(events => events
.Select("onSelect")).ExpandAll(true)
)
</div>
#(Html.Kendo().Grid<DocunetViewer.Models.MyModel>()
.Name("grid")
.Columns(columns =>
{
columns.Bound(c => c.ID);
columns.Bound(c => c.Name);
})
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(20)
.Read(read => read.Action("GetData", "Home").Data("getParameter")))
and this is the script:
<script>
function onSelect(e) {
BindGrid();
}
function BindGrid() {
$("#grid").data("kendoGrid").dataSource.read();
$("#grid").data("kendoGrid").refresh();
}
function getParameter() {
var treeview = $("#treeview").data("kendoTreeView");
var selectedNode = treeview.select();
var id = -1;
if (selectedNode.length != 0) {
var item = treeview.dataItem(selectedNode);
id = item.id;
}
return {
myId: id
};
}
my problem is that I always get the previous selected node not the current selected node, so what is the problem? and how can I solve it?
Thanks in advance.
In your onSelect method, you can get at the node with some JavaScript like this.
var node = ($(e.node));
and here I'm getting the id attribute of the node (your node may have different things on it).
var id = ($(e.node).data("id"));

Kendo UI Grid CRUD firing multiple times

I have a kendo UI grid in my page. Below is the code of kendo UI grid with CRUD datasource actions.
#(Html.Kendo().Grid<Gts.GlaspacLX.Web.ViewModel.ProductViewModel>()
.Name("xyzGrid")
.Columns(columns =>
{
columns.Bound(p => p.SelectedProductCategory).EditorTemplateName("_ProductDropDownList").Title("Product Category").HtmlAttributes(new { #tabindex = "8" });
columns.Bound(p => p.Name).Width(130).Title("% Off").HtmlAttributes(new { #tabindex ="9" });
columns.Bound(p => p.Rate).Width(130).HtmlAttributes(new { #class = "prodDiscRtAlign",#tabindex= "10" });
columns.Bound(p => p.Hours).Width(130).HtmlAttributes(new { #class = "prodDiscRtAlign",#tabindex= "11" });
if (SiteContext.CurrentUser.HasPrivilege(PrivilegeNames.Maintenance, PermissionNames.DELETE))
{
columns.Command(command => { command.Destroy(); }).Width(110).Title("Delete").HtmlAttributes(new { #tabindex = "12" });
}
})
.ToolBar(commands =>
{
commands.Create();
commands.Save();
})
.Editable(editable => editable.Mode(GridEditMode.InCell).CreateAt(GridInsertRowPosition.Bottom))
.Sortable()
.Navigatable()
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.ServerOperation(false)
.Model(model =>
{
model.Id(p => p.ProductID);
model.Field(p => p.SelectedProductCategory).DefaultValue(ViewBag.DefaultProductCategory);
})
.Read(read => read.Action("Product_Read", "ProductController"))
.Update(update => update.Action("Product_Update", " ProductController "))
.Create(create => create.Action("Product_Create", " ProductController "))
.Destroy(update => update.Action("Product_Destroy", " ProductController ")
))
.Events(e => e.Edit("proField").DataBound("boundProductChange"))
)
Below is the screen shot of "Save" button just after the kendo grid. It's responsible for any create/update operation of the page.
My problem is once I clicked on Save button for any create or update operation its posting the action method twice. You can see the console of above screen shot.
Below is the piece of the code of my controller's action method:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Product_Create([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<ProductViewModel> product){
return Json(results.ToDataSourceResult(request, ModelState));
}
Below is proField function code :-
function proField(e) {
var defaultproduct = $("#DefaultProductCategory").val();
defaultproduct = "\n" + defaultproduct + "select ";
if (e.model.SelectedProductCategory == "Default" && (e.sender._editContainer[0].textContent == defaultproduct || e.sender._editContainer[0].textContent == "\n select ")) {
e.sender._editContainer[0].disabled = true;
e.sender._editContainer[0].children[0].textContent = "Default";
e.sender.table[0].rows[1].cells[1].click();
e.sender.table[0].rows[1].cells[4].disabled = true;
}
}
after insert any record you should return primary(Id) key to view.
see kendo demo.

Kendo MVC Grid not passing parent ID to ClientID Template, when creating Child record

I'm having a problem adding a child record in my hierarchical grid. It won't pass over the HeaderId from the parent in the grid.
Can anyone spot an issue, or am I trying to do something that isn't possible?
Thanks.
Here's the controller action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult BillDetail_Create(BillDetail billDetail, int billHeaderId)
{
if (billHeaderId == 0)
{
ModelState.AddModelError("billHeaderID", "add bill header first");
}
if (billDetail != null && ModelState.IsValid)
{
var target = new BillDetail
{
Category = billDetail.Category,
Description = billDetail.Description,
Amount = billDetail.Amount,
BillHeaderId = billHeaderId,
BillDetailId = SessionBillDetails.Max(d => d.BillDetailId) + 1
};
//Get next Id in sequence
billDetail.BillDetailId = target.BillDetailId;
SessionBillDetails.Add(target);
}
return Json(new[] { billDetail }.ToDataSourceResult(new DataSourceRequest(), ModelState));
}
And here's the view:
#(Html.Kendo().Grid<BillHeader>()
.Name("BillHeaders")
.Columns(columns =>
{
columns.Bound(h => h.BillHeaderId);
columns.Bound(h => h.Category);
columns.Bound(h => h.Description);
columns.Bound(h => h.Amount);
})
.Pageable()
.Selectable(selectable => selectable
.Mode(GridSelectionMode.Multiple)
.Type(GridSelectionType.Row))
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(6)
.Events(events => events.Error("error_handler"))
.Read(read => read.Action("BillHeaders_Read", "Bill"))
)
.Events(events => events.DataBound("dataBound"))
.ClientDetailTemplateId("BillDetails")
)
<script id="BillDetails" type="text/kendo-tmpl">
#(Html.Kendo().Grid<BillDetail>()
.Name("BillDetails_#=BillHeaderId#")
.Columns(columns =>
{
columns.Bound(d => d.BillHeaderId).Width(50);
columns.Bound(d => d.BillDetailId).Width(70);
columns.Bound(d => d.Category).Width(70);
columns.Bound(d => d.Description).Width(150);
columns.Bound(d => d.Amount).Width(80);
columns.Command(command =>
{
command.Edit();
command.Destroy();
}).Width(75);
})
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(10)
.Model(model =>
{
model.Id(d => d.BillDetailId);
model.Field(d => d.BillDetailId).Editable(false);
})
.Events(events => events.Error("error_handler"))
.Read(read => read.Action("BillDetails_Read", "Bill", new { billHeaderId = "#=BillHeaderId#" }))
.Update(update => update.Action("BillDetail_Update", "Bill"))
**.Create(create => create.Action("BillDetail_Create", "Bill", new { billHeaderId = "#=BillHeaderId#" }))**
.Destroy(destroy => destroy.Action("BillDetail_Destroy", "Bill")))
.Pageable()
.ToolBar(tools => tools.Create())
.ToClientTemplate()
)
</script>
Managed to finally fix this. Unbelievable really....
I named the parameter in my controller (and view) to be "id"
So Controller:
public ActionResult BillDetail_Create(BillDetail billDetail, int id)
And View:
.Read(read => read.Action("BillDetails_Read", "Bill", new { id = "#=BillHeaderId#" }))
.Update(update => update.Action("BillDetail_Update", "Bill"))
.Create(create => create.Action("BillDetail_Create", "Bill", new { id = "#=BillHeaderId#" }))
.Destroy(destroy => destroy.Action("BillDetail_Destroy", "Bill")))
For a better explanation of why that worked:
This can occur if the BillDetail class has a property with the same name. In this case the MVC model binder will override the value sent with the request route values with the value sent as form data for the grid model. If this is the case then the simplest option to avoid the problem would be to rename the parameter. that is why renaming to ID worked.