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));
Related
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.
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
I am trying to get a telerik grid to display json data that is being return from a controller action but the only it displays the actual json data in the browser window.
Am i supposed to call .BindTo after read?
Am i doing something wrong in my action?
am i going about this all wrong?
[HttpGet]
public ActionResult ReadLeads([DataSourceRequest]DataSourceRequest request)
{
var model = new RecordLookupViewModel();
using (var db = new RGI_MasterEntities())
{
db.Configuration.ProxyCreationEnabled = false;
var results = db.tblMasterLeads
.Where(
x => (model.FirstName == null || x.FirstName.Equals("Eric"))
&& (model.RecordType == null || x.MasterLeadType.Equals("Responder"))
)
.Select(s => new LookupGridResults
{
FirstName = s.FirstName,
LastName = s.LastName,
City = s.city,
State = s.state,
County = s.county,
Zip = s.zip
}).Take(10);
var result = results.ToDataSourceResult(request);
return Json(result, JsonRequestBehavior.AllowGet);
}
}
Hers is my view code for the grid.
#(Html.Kendo().Grid<LookupGridResults>()
.Name("grid")
.AutoBind(false)
.Columns(columns =>
{
columns.Bound(p => p.FirstName).Filterable(ftb => ftb.Cell(cell => cell.Operator("contains"))).Width(225);
columns.Bound(p => p.LastName).Width(225).Filterable(ftb => ftb.Cell(cell => cell.Operator("contains")));
columns.Bound(p => p.City).Width(225).Filterable(ftb => ftb.Cell(cell => cell.Operator("contains")));
columns.Bound(p => p.County).Width(225).Filterable(ftb => ftb.Cell(cell => cell.Operator("contains")));
columns.Bound(p => p.State).Width(225).Filterable(ftb => ftb.Cell(cell => cell.Operator("contains")));
columns.Bound(p => p.Zip).Width(225).Filterable(ftb => ftb.Cell(cell => cell.Operator("contains")));
})
.Pageable()
.Sortable()
.Scrollable()
.Filterable(ftb => ftb.Mode(GridFilterMode.Row))
.HtmlAttributes(new { style = "height:550px;" })
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(20)
.ServerOperation(true)
.Read(read => read.Action("ReadLeads", "LeadsManagement").Type(HttpVerbs.Get))
)
)
Here are my results btw.
{"Data":[{"LastName":"COFFEY","FirstName":"EDWARD","City":"FRANKFORT","County":"FRANKLIN","State":"KY","Zip":"40601-2304"},{"LastName":"DESPAIN","FirstName":"TONY","City":"CAMPBELLSVILLE","County":"TAYLOR","State":"KY","Zip":"42718-9397"},{"LastName":"HALBIG","FirstName":"RONALD","City":"CAMPBELLSVILLE","County":"TAYLOR","State":"KY","Zip":"42718-1556"},{"LastName":"KRAUS","FirstName":"REBECCA","City":"FRANKFORT","County":"FRANKLIN","State":"KY","Zip":"40601-2714"},{"LastName":"LAWLESS","FirstName":"MEREDITH","City":"CAMPBELLSVILLE","County":"TAYLOR","State":"KY","Zip":"42718-1556"},{"LastName":"RANKIN","FirstName":"PAULINE","City":"LAWRENCEBURG","County":"ANDERSON","State":"KY","Zip":"40342-1374"},{"LastName":"SHIRLEY","FirstName":"LORRAINE","City":"CAMPBELLSVLLE","County":"TAYLOR","State":"KY","Zip":"42718-1557"},{"LastName":"STAPLES","FirstName":"DAMON","City":"HODGENVILLE","County":"LARUE","State":"KY","Zip":"42748-1208"},{"LastName":"WILLIAMS","FirstName":"LUCY","City":"FRANKFORT","County":"FRANKLIN","State":"KY","Zip":"40601-2308"},{"LastName":"WILSON","FirstName":"BELIDA","City":"FRANKFORT","County":"FRANKLIN","State":"KY","Zip":"40601-1321"}],"Total":10,"AggregateResults":null,"Errors":null}
Thanks for all the help, it seemed i was missing a reference to a bundle. I do credit Mark Schultheiss for pointing me in the right direction.
Got it completly working today. Here is what fixed it.
I changed my actionresult to a JsonResult.
I had filtering turned on in the grid but none of my columns had filtering attributes.
I think thats about it. It works great now.
I have the following code on a page.
The problem is that when the "MainSearchSubmit" button is clicked the method "RefreshGrid()" is resulting in 2 subsequent ajax calls to the server. I found this is because the call to "datasource.read()" and "datasource.page(1)" are both posting back to the server (they are both running the read() method of the dataSource).
I still need to set the grid page to 1 when the search button is clicked because otherwise someone can be on page 3, then click the search button, get the updated results, but still be on page 3. They need to be reset to page 1 when the click the search button.
Also I still need to run the read() because otherwise the updated parameters are not passed and the data is not refreshed.
I've found similar posts, some with people suggesting using the .query() method. I tried that (code commented out in "RefreshGrid()" method), but that was also resulting in 2 posts to the server.
Any ideas on how I can fix this?
Code:
<script>
$(document).ready(InitializeNDCMapping);
function InitializeMainMapping() {
$("#MainSearchSubmit").click(function (e) {
RefreshGrid();
e.preventDefault();
});
}
function RefreshGrid() {
$("#MainListGrid").data("kendoGrid").dataSource.read();
$("#MainListGrid").data("kendoGrid").dataSource.page(1);
//Defunct code
//var dataSource1 = $("#NDCListGrid").data("kendoGrid").dataSource;
//dataSource.query({
// read: dataSource1.read(),
// page: dataSource1.Page(1),
// pagesize: dataSource1.PageSize(25)
//});
}
</script>
<div class="search-buttons">
<input type="submit" value="Search" id="MainSearchSubmit" />
<input type="button" value="Reset" onclick="ResetMainSearch()" />
</div>
<div>
#(Html.Kendo().Grid<BackOffice.ViewModels.NDCItem>()
.Name("MainListGrid")
.Filterable()
.Pageable(p => p.PageSizes(new int[] { 25, 50, 100 }).Input(true)
.Messages(m=>m.Empty("No Main-Ingredient found")).Numeric(false))
.Sortable()
.Navigatable()
.Selectable(selectable => selectable.Mode(GridSelectionMode.Single))
.Columns(columns =>
{
columns.Bound(l => l.NDCCode).Title("NDC Code").Width("5%")
.ClientTemplate(#Html.DialogFormLink("#=NDCCode#", Url.Action("NDCMappingEdit", new { NDCCode = "#=NDCCode#", mainMultumDrugCode = "#=MainMultumDrugCode#", drugId = "#=DrugId#", isMapped = "#=IsMapped#" }), "NDC Mapping", "", "", "NDCDialogLink", "1100", "600", "").ToHtmlString());
columns.Bound(l => l.Name).Title("Generic Name").Width("15%");
columns.Bound(l => l.MainCode).Hidden();
columns.Bound(l => l.MainId).Hidden();
columns.Bound(l => l.MainDescription).Title("Main Description").Width("15%");
columns.Bound(l => l.MainIngredientCode).Hidden();
columns.Bound(l => l.MainIngredient).Title("Main Ingredient").Width("15%");
columns.Bound(l => l.MainNumAmount).Title("Main Qty").Width("5%");
columns.Bound(l => l.MainNum).Title("Main Unit").Width("5%");
columns.Bound(l => l.MainDenomAmount).Title("Main Denom").Width("5%");
columns.Bound(l => l.MainDenom).Title("Main Denom Unit").Width("5%");
columns.Bound(l => l.IsMapped).Title("Mapped").Width("5%").Filterable(false).Sortable(false)
.ClientTemplate("<input type='checkbox' name='selected_#=MainCode#' class='chkbx select'" + "#= IsMapped ? 'checked' : ''#" + " />")
.HtmlAttributes(new { #class = "center", #onclick = "return false" })
.HeaderHtmlAttributes(new { #class = "center" });
})
.AutoBind(false)
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.ServerOperation(false)
.Read(read => read.Action("MainList_Read", "Drug").Data("getRouteParams"))
.Model(model =>
{
model.Field(f => f.MainCode).Editable(false);
model.Field(f => f.MainName).Editable(false);
model.Field(f => f.MainDCCode).Editable(false);
model.Field(f => f.MainId).Editable(false);
model.Field(f => f.MainDescription).Editable(false);
model.Field(f => f.MainIngredientCode).Editable(false);
model.Field(f => f.MainIngredient).Editable(false);
model.Field(f => f.MainNumAmount).Editable(false);
model.Field(f => f.MainNum).Editable(false);
model.Field(f => f.MainDenomAmount).Editable(false);
model.Field(f => f.MainDenom).Editable(false);
model.Field(f => f.IsMapped).Editable(false);
})
.PageSize(25)
.Events(events => events.Error("MainRead_Error"))
)
)
</div>
In the past, when I had similar problems, it was almost always because the click handler was registered twice and even though the user clicked the button once, the handler executed twice.
Where do you call InitializeMainMapping()? Also, I am not sure if this is a copy and paste error, but the InitializeMainMapping() is missing a closing }. Are you getting any javascript errors when the user clicks on the button?
Just a few ideas to get you started.
I was able to find the solution. If the page is already set to 1 and you run...
grid.dataSource.page(1);
This results in a read. Otherwise if the page is not yet set to 1 and you run the code above it doesn't result in a read. So the solution to the issue is below.
if (grid.dataSource.page() != 1) {
grid.dataSource.page(1);
}
grid.dataSource.read( {parameter: "value"} );
Found this solution here:
KendoUI: resetting grid data to first page after button click
I have a pretty basic grid using the razor wrapper and my anticipated 3 rows are not showing up.
#(Html.Kendo().Grid<ManageUserViewModel>()
.Name("grid")
.Columns(columns =>
{
columns.Bound(c => c.UserName);
columns.Bound(c => c.FirstName);
columns.Bound(c => c.LastName);
columns.Bound(c => c.Email);
columns.Bound(c => c.LastNameFirstLetter).Hidden(true);
})
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action("list", "User"))
)
)
Here is the json being returned from /User/list which I ran through jsonLint to make sure it was valid.
[{"UserName":"dfeinberg","FirstName":null,"LastName":null,"Email":"daniel.feinberg#rcsg.com","Password":null,"Roles":[],"LastNameFirstLetter":""},{"UserName":"jmarley","FirstName":"Jacob","LastName":"Marley","Email":"ebuntom#gmail.com","Password":null,"Roles":[{"Id":"9a0af7ad-e38f-4300-a49e-1051dc18c8e4","Name":"Administrator","Checked":false}],"LastNameFirstLetter":"M"},{"UserName":"tcoakley","FirstName":"Tom","LastName":"Coakley","Email":"tom.coakley#redcedarsolutionsgroup.com","Password":null,"Roles":[],"LastNameFirstLetter":"C"}]
It is my day for dumb questions. My issue was not understanding the correct way to return the datasource from the controller. Here is my corrected controller method.
public ActionResult List([DataSourceRequest]DataSourceRequest request)
{
List<AspNetUser> users = SdContext.AspNetUser.ToList();
var models = UserAssembler.GetManageUserViewModels(users);
return Json(models.ToDataSourceResult(request));
}