Error event handling causes Read to not be called - kendo-grid

Below is my PartialView for a Kendo Grid. As of now, my Parts_Read action is NOT called. But, if I comment out
.Events(events => events.Error("error_handler"))
Then everything works fine. Any ideas of why this is happening?
#using Kendo.Mvc.UI;
#using eRPortalDashboard.Models
#model PMPartsViewModel
#{
int PMNumber = Model.PMNumber;
string uniqueName = Model.PartsGrid.ID;//Regex.Replace(Guid.NewGuid().ToString(), "[^A-Za-z]+", "");
bool enableToolbar = Model.PartsGrid.EnableToolbar;
bool enablePageable = Model.PartsGrid.Pageable;
bool enableSortable = Model.PartsGrid.Sortable;
bool enableColumnMenu = Model.PartsGrid.EnableColumnMenu;
bool enableGroupable = Model.PartsGrid.Groupable;
bool enableColumnResizing = Model.PartsGrid.AllowColumnResizing;
bool enableScrolling = Model.PartsGrid.Scrollable;
bool enableSelection = Model.PartsGrid.Selectable;
}
#(Html.Kendo().Grid<PartViewModel>()
.Name(uniqueName)
.Columns(columns =>
{
columns.Bound(c => c.ItemNumber);
columns.Bound(c => c.Description);
})
.ToolBar(toolBar =>
{
toolBar.Custom().Text("Test Button").Url("#").HtmlAttributes(new { id = "testButton" });
})
.Pageable(pager => pager
.Input(false) //Using pageable.numeric and pageable.input at the same time is not recommended.
.Numeric(true)
.Info(true)
.PreviousNext(true)
.Refresh(true)
.PageSizes(new object[] { 5, 10, 20, 50, "all" })
.Enabled(enablePageable)
)
.Selectable(s => s.Mode(mode: GridSelectionMode.Multiple).Enabled(enableSelection))
.AllowCopy(enableSelection) //selectable needs to be enabled and set to multiple
.Sortable(s => s.SortMode(GridSortMode.MultipleColumn).Enabled(enableSortable))
.ColumnMenu(c => c.Enabled(enableColumnMenu))
.Groupable(g => g.Enabled(enableGroupable))
.Scrollable(s => s.Height("auto").Enabled(enableScrolling))
.Resizable(resize => resize.Columns(enableColumnResizing))
//.Events(events => events.DataBound(uniqueName + "_updateGrid").Change(uniqueName + "_updateGrid"))
.DataSource(dataSource => dataSource
.Ajax()
.ServerOperation(true)
.Events(events => events.Error("error_handler"))
.Read("Parts_Read", "PM", new { PMNumber = PMNumber })
)
)
<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);
}
}

The main crux of my issue was the fact that error_handler was declared after my MVC code, thereby making it unable to bind to the method.
Apparently, with Kendo (maybe others), when using Partial Views, the javascript function must be declared BEFORE it is used in Razor syntax for that view.
see also: telerik forum post

Related

Kendo UI Grid SignarlR datasource not firing update event

I'm using asp.net core razor pages with a Kendo Grid that is datasourced using SignalR. It gets the initial read ok, but the update does not fire. I've looked at the Kendo Demo, and other stackover flow pages, but nothing seems to work.
I know the API works fine in sending the update because I see the call when debugging through Chrome that the websocket received an update commmand with the new data in json format. But the Grid doesn't update, or fire any update commands. It's as if it never received it, or doesn't know that it received it.
Index.cshmtl
<script src="~/signalr/signalr.js"></script>
<script>
var url = https://demosite.com/hub/controller;
var hub = new signalR.HubConnectionBuilder()
.configureLogging(signalR.LogLevel.Information)
.withUrl(url,
{
transport: signalR.HttpTransportType.WebSockets | signalR.HttpTransportType.LongPolling
})
.build();
var hubStart = hub.start();
</script>
#(Html.Kendo().Grid<myModel>
()
.Name("grid")
.Columns(columns =>
{
columns.Bound(c => c.Name);
columns.Bound(c => c.Date);
})
.HtmlAttributes(new { style = "width: 98%;" })
.DataSource(dataSource => dataSource
.SignalR()
.AutoSync(true)
.ServerFiltering(true)
.ServerSorting(true)
.PageSize(10)
.Transport(tr => tr
.Promise("hubStart")
.Hub("hub")
.Client(c => c
.Read("read") //Read works, initial data loads
.Create("create")
.Update("update")
.Destroy("destroy")
)
.Server(s => s
.Read("read")
.Create("create")
.Update("update")
.Destroy("destroy")
)
)
.Schema(schema => schema
.Data("Data")
.Total("Total")
.Aggregates("Aggregates")
.Model(model =>
{
model.Id(p => p.ID);
model.Field(p=> p.Name);
model.Field(p => p.Date);
}
)
)
)
.Group(g => g.Add(x => x.Name))
)
.Events(x=>
{
x.DataBound("collapseAllGroups");
}
)
.Groupable(true)
.Sortable()
.Filterable()
.Pageable(pager => pager.AlwaysVisible(true).PageSizes(new int[] { 10, 20, 50, 100 }))
.Resizable(resize => resize.Columns(true))
.Reorderable(reorder => reorder.Columns(true))
.Selectable()
)
Api
[HttpGet]
public async Task<myModel> GetTest()
{
myModel mod= new myModel();
mod.Name = "New Name";
mod.Date = DateTime.UTCNow.ToString();
//Send update command to connected SignalR Clients
await _hub.Clients.All.SendAsync("update", mod);
return mod;
}
Any help would be appreciated.
Ok I got this to work. Turns out that since the Grid is originally expecting a DataSourceResult I had to convert myModel to a DataSourceResult type before doing the update. Example below should be considered Pseudocode as I’m typing this on my phone.
await _hub.Clients.All.SendAsync("update", new DataSourceResult(mod));

Kendo Method ToDataSourceResult gives timeout exception for large number of records

I am using Kendo Grid as below:
Html.Kendo().Grid<MyViewModel>()
.Name("grid")
.Columns(c => c.AutoGenerate(false))
.Filterable(f =>
{
//coding for filteration
})
.Selectable(selectable => selectable.Mode(GridSelectionMode.Single))
.Reorderable(r => r.Columns(true))
.Pageable(pager => pager
.Refresh(true)
//coding for pages
)
.Sortable().Selectable(selectable => selectable.Mode(GridSelectionMode.Multiple))
.Scrollable(s => s.Height(450))
.Filterable()
.Resizable(r => r.Columns(true))
.DataSource(dataSource => dataSource.Ajax()
.Events(ev => ev.RequestEnd("onRequestEnd"))
.Sort(sort => { sort.Add(model => model.Date).Descending(); })
.Filter(f => f.Add(model => model.SerialNo).IsEqualTo((this.Session["Id"] as MyModel).SerialNo)) // Applying Custom Filter
.PageSize(10)
.ServerOperation(true)
.Read(read => read.Action("Read", "Controller").Data("Grid_Index_additionalData")))
.Events(events => events.DataBound("onDataBound"))
.Render();
Code in Controller :
[OutputCache(Duration = 0, VaryByParam = "None")]
public ActionResult Read([DataSourceRequest]DataSourceRequest request, string searchBox)
{
IQueryable<MyViewModel> filteredResult;
var collection1 = manager.Model1SelectAll();
var collection2 = manager.Model2SelectAll();
filteredResult = from c1 in collection1
join c2 in collection2
on c1.SerialNo equals c2.SerialNo
select new MyViewModel
{
Prop1 = c1.Prop1,
Prop2 = c1.Prop2,
Prop3 = c1.Prop3,
Prop4 = c1.Prop4,
SerialNumber = c1.SerialNo,
Prop5 = c2.Prop5,
Prop6 = c2.Prop6
};
var result = filteredResult.ToDataSourceResult(request);
// filteredResult is IQueryable of around 3 million records
// filtering these on basis of request.Sorts and request.Filter will give 100 records
// But I receive TimeOut exception here
try
{
var serializer = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
var jsonResult = new ContentResult
{
Content = serializer.Serialize(result),
ContentType = "application/json"
};
return jsonResult;
}
catch (Exception ex)
{
ExceptionService.LogException(ex);
}
}
My filteredResult contains 3 million records which gives TimeOut Exception while calling ToDataSourceResult().
Also if I try to use filteredResult.ToList() => that gives me MemoryException.
I am unable to remove Timeout exception. Kendo says that if we pass IQueryable to ToDataSourceResult, it will handle all filtering, But in my case it gives exception.
I have tried creating my own alternative of ToDataSourceResult() by handling all request.Sorts and Filters and passign just pagesize records using
this.ObjectSet.Where(whereCondition).OrderBy("it." + sortBy).Skip(pageSize * (pageNumber - 1)).Take(pageSize);
But this way I am unable to handle all the cases of filter and make this method generic for all the grids in my project.

How to pass object as parameter to kendo grid read method

I have kendo grid as follow.
#(Html.Kendo().Grid<ManualInputDetail>()
.Name("gManualInputDetail")
.Columns(columns =>
{
columns.Bound(c => c.Id).Hidden(true);
columns.Bound(c => c.Month).Title("Month");
columns.Bound(c => c.Value).Title("Value");
})
.Editable(editable => editable.Mode(GridEditMode.InLine))
.Pageable()
.Navigatable()
.Selectable(selectable =>
{
selectable.Mode(GridSelectionMode.Single);
selectable.Type(GridSelectionType.Row);
})
.Sortable(sortable =>
{
sortable.SortMode(GridSortMode.MultipleColumn);
})
.DataSource(dataSource => dataSource
.WebApi()
.Model(model => model.Id(p => p.Id))
.PageSize(12)
.Read(read => read.Url(Url.HttpRouteUrl("ActionApi", new { controller = "ManualInputDetails", action = "GetManualInputDetails" })).Data("getFilterData"))
)
.Pageable(p => p.Refresh(true))
)
using getFilterData function I want to pass object parameter to read method. getFilterData function as follow
function getFilterData() {
var header= {
SectorId: 1,
BrandId: 2,
LocationId: 1,
DataElementId:2
}
return {
header: header
};
}
GetManualInputDataElements method as follow
[ActionName("GetManualInputDetails")]
public DataSourceResult GetManualInputDetails([System.Web.Http.ModelBinding.ModelBinder(typeof(WebApiDataSourceRequestModelBinder))] DataSourceRequest request,ManualInputHeader header)
{
var model = new DataElementMgt().GetAll(header).Select(x => new DataElement()
{
Id = x.Id,
DataElementTypeId = x.DataElementTypeId,
Name = x.Name,
Descriptionn = x.Descriptionn
}).ToList().ToDataSourceResult(request);
return model;
}
In here header value always gives as null. What is the reason for that. Is any thing wrong? Please help..
change the getFilterData method to this
function getFilterData() {
var _header= {
SectorId: 1,
BrandId: 2,
LocationId: 1,
DataElementId:2
}
return {
header: _header
};
}
and it should work. dont use the same name for what you return and declare.

Populating ASP.NET MVC Kendo Grid Via Ajax Call

I have an MVC Kendo Grid and I want to fill it via a jQuery Ajax Call. I used jQuery 'each' method to do it like this :
function FillRowsByRequest(reqRow) {
var readDataUrl = '#Url.Action("GetGoodsByReq")';
var targetGrid = $("#storeReceiptRowsGrid").data("kendoGrid");
$.get(readDataUrl, { reqseq: reqRow }, function (d, t, j) {
var counter = 0;
targetGrid.cancelChanges();
$(d).each(function (i, e) {
targetGrid.dataSource.insert(counter++, {
GOOD_ID: e.GOOD_ID,
GOOD_CODE: e.GOOD_CODE,
GOOD_CODE_DESC: e.GOOD_CODE_DESC,
GOOD_DESC: e.GOOD_DESC
});
});
});
}
I can see my Kendo Grid that is filled with data ( not completely ) but the thing is that when I click on the Save button, it does not trigger the Save Action Method and consequently nothing is inserted in the table and Grid contains nothing after it is refreshed.
#(Html.Kendo()
.Grid<Tpph.Models.STORE_RECEIPT_ROW>()
.Name("storeReceiptRowsGrid")
.Columns(columns =>
{
columns.Bound(o => o.GOOD_ID).Title("Good ID").HtmlAttributes(new { #class = "goodid" }).Visible(false);
columns.Bound(o => o.GOOD_CODE).Title("Good Code").HtmlAttributes(new { #class = "goodcode" }).Width(100);
columns.Bound(o => o.GOOD_CODE_DESC).Title("Good Code Desc").HtmlAttributes(new { #class = "goodcodedesc" }).Width(100);
columns.Bound(o => o.GOOD_DESC).Title("Good Desc").HtmlAttributes(new { #class = "gooddesc" }).Width(155);
})
.ToolBar(toolbar =>
{
toolbar.Create().Text("New Row").HtmlAttributes(new { #class = "k-primary", style = "background-color: #e6ffe6; border-color: #10c4b2; min-width: 100px; color: black;" });
toolbar.Save().Text("Save").SaveText("Save").CancelText("Cancel").HtmlAttributes(new { #class = "k-primary", style = "background-color: #e6ffe6; border-color: #10c4b2; min-width: 100px; color: black;" });
})
.ColumnMenu()
.Selectable(s => s.Type(GridSelectionType.Row))
.Sortable()
.Editable(editable => editable.Mode(GridEditMode.InCell).DisplayDeleteConfirmation("Delete?"))
.Filterable()
.Groupable()
.Scrollable()
.Pageable(p => p.Refresh(true))
.Resizable(resize => resize.Columns(true))
.Reorderable(reorder => reorder.Columns(true))
.DataSource(dataSource => dataSource
.Ajax()
.Events(ev => ev.RequestEnd("storeReceiptRowsGridOnRequestEnd"))
.Batch(true)
.ServerOperation(true)
.Model(model =>
{
model.Id(p => p.GOOD_ID);
})
.Read(read => read.Action("StoreReceiptRowsRead", "StorageForms"))
.Update(u => u.Action("StoreReceiptRowsEdit", "StorageForms"))
.Create(c => c.Action("StoreReceiptRowsCreate", "StorageForms"))
.Destroy(de => de.Action("StoreReceiptRowsDestory", "StorageForms")))
.Events(ev =>
{
ev.DataBound("storeReceiptRowsGridOnBound");
})
)
How can I do this ?
After lots of struggling with this issue, I finally found out that the Kendo Grid triggers the CRUD Action Methods only when the "dirty" attribute of a row is set to true. ( dirty flag is a tiny little red triangle which appears in the corner of a cell when you edit that cell ). So the solution to this issue is setting dirty flag of each row to true like this :
.set("dirty", true);
So my final JavaScript Code is like this :
function FillRowsByRequest(reqRow) {
var readDataUrl = '#Url.Action("GetGoodsByReq")';
var targetGrid = $("#storeReceiptRowsGrid").data("kendoGrid");
$.get(readDataUrl, { reqseq: reqRow }, function (d, t, j) {
var counter = 0;
targetGrid.cancelChanges();
$(d).each(function (i, e) {
targetGrid.dataSource.insert(counter++, {
GOOD_ID: e.GOOD_ID,
GOOD_CODE: e.GOOD_CODE,
GOOD_CODE_DESC: e.GOOD_CODE_DESC,
GOOD_DESC: e.GOOD_DESC
}).set("dirty", true);
});
});
}

How to pass selected row of Kendo grid data to controller

I've got a kendo grid with an image near it that will act like a button. When pressed, it will call a controller method. I want to send the selected row data to that method.
VIEW
<a href="#" id="ic_open" class="tooltip2" title="Abrir">
<span title="">
<img class="toolbar-icons" src="../../Images/open.png"/>
</span>
</a>
...
<div id="datagrid">
#(Html.Kendo().Grid(Model)
.Name("datagrid_Concessoes")
.Columns(columns =>
{
columns.Bound(c => c.Id).Width(70);
columns.Bound(c => c.Code);
columns.Bound(c => c.Description);
columns.Bound(c => c.CreationDate);
columns.Bound(c => c.CreationUser);
})
.HtmlAttributes(new { style = "height: 534px;" })
.Scrollable()
.Sortable()
.Selectable()
.Pageable(pageable => pageable
.Refresh(true)
.ButtonCount(5))
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(15)
.Read(read => read.Action("GetConcessoes", "MasterData"))
)
)
</div>
And the script:
<script type="text/javascript">
$(function () {
$('.tooltip2').click(function () {
var id = this.id;
$.get('#Url.Content("GetPartialView")',
{ "id": id },
function (data) {
$('#div-for-partial').html(data);
});
});
});
</script>
This script sends the link's id (ic_open) to the controller successfully. I want to send the selected row data, via this same function or some other (doesn't matter), to the controller so I can manipulate that info.
EDIT
Controller method
public ActionResult GetPartialView(string id)
{
switch (id)
{
case "":
return PartialView("_Concessoes");
case "tab1":
return PartialView("_Concessoes");
case "tab2":
return PartialView("_AutoEstradas");
case "ic_open":
return PartialView("_NovaConcessao");
}
return RedirectToAction("Index");
}
Your Script should be as below:
<script type="text/javascript">
$(function () {
$('.tooltip2').click(function () {
var id = this.id;
var concessoesGrid = $("#datagrid_Concessoes").data("kendoGrid");
var row = concessoesGrid.select();
$.get('#Url.Content("GetPartialView")',
{ "id": id, "modelData":row },
function (data) {
$('#div-for-partial').html(data);
});
});
});
</script>
Now there will be change on your controller side also:
public ActionResult GetPartialView(string id, ModelClass modelData)
{
//here you can access the modelData Object which will have the value of Selcted row of Grid
switch (id)
{
case "":
return PartialView("_Concessoes");
case "tab1":
return PartialView("_Concessoes");
case "tab2":
return PartialView("_AutoEstradas");
case "ic_open":
return PartialView("_NovaConcessao");
}
return RedirectToAction("Index");
}
NOTE: if you want to pass multiple selected grid records to controller then you will need to change the controller argument to accept List of objects.
I am using KendoJS but I believe this would also work on your side too:
var grid = $("yourgrid's id or class");
var selectedRow;
grid.change = function()
{
grid.select().each(function () {
var dataItem = grid.dataItem($(this));
selectedRow = dataItem;
});
}
I also found this for Kendo ASP.NET MVC:
#(Html.Kendo().Grid(Model)
.Name("grid")
.Events(e => e
.DataBound(#<text>
function() {
//Handle the dataBound event inline
}
</text>)
.Change(#<text>
var selectedRow;
function() {
var grid = this;
grid.select().each(function () {
var dataItem = grid.dataItem($(this));
selectedRow = dataItem;
});
}
</text>)
)
)
After getting the selected row, the rest is easy. Just send the value you want into your hidden value element on your cshtml page or just call the ajax method for your controller right over on your JavaScript code.