Server Side Paging in Kendo Grid? - asp.net-mvc

I want client side grid paging in Kendo Grid. In grid only first 50 or 100 data will be shown in first page. And when customer click next page, other 50 or 100 data will be shown. I don't want to get all data from my server. because there will be million data in database and customer doesn't want to wait service to get all data from server. when he/she click next page, other data should request from server. How can I do it?
my controller
[HttpGet]
public JsonResult Getdata()
{
var reports = db.ActivityLog.OrderBy(c => c.dateTime).ToList();
var collection = reports.Select(x => new
{
username = x.uName,
location = x.locName,
devices = x.devName
});
return Json(collection, JsonRequestBehavior.AllowGet);
}
my view
function handleDataFromServer() {
$("#grid").data("kendoGrid").dataSource.read();
}
window.setInterval("handleDataFromServer()", 10000);
$(document).ready(function () {
$("#grid").kendoGrid({
sortable: true,
pageable: {
input: true,
numeric: false
},
selectable: "multiple",
dataSource: {
transport: {
read: "/Home/Getdata",
type: "json"
}
},
columns: [
{ field: "username", width: "80px" },
{ field: "location", width: "80px" },
{ field: "devices", width: "80px" }]
});
});

In Kendo you can do that easily. You just need to turn serverPaging: true. But as far as I can recall true is default. Anyway, need to declare it inside the dataSource as follows.
dataSource: {
transport: {},
pageSize: 50,
serverPaging: true,
},
pageable: {
refresh: true,
pageSizes: [25, 50, 100]
}
If serverPaging is true for every new page request Kendo will send a request to Server to fetch the next lot according to your server fetching logic. Let me know if this helps.

See also this github project KendoGridBinderEx which is also available as NuGet package.
Demo can be found here.

Follow this article which explains the serverside paging, sorting and other options with proper code and explanation.
http://blog.longle.net/2012/04/13/teleriks-html5-kendo-ui-grid-with-server-side-paging-sorting-filtering-with-mvc3-ef4-dynamic-linq/
hope this helps.

Related

What is the best way to bind a two dimensional object array to the grid that is also type safe

I have a kendo UI Grid from Telerik
I want to bind a two dimensional object array to the grid. I work in Visual Studio 2012 in ASP.NET MVC. I have a solution where I use a javascript solution. The datatype for the datasource is a two dimensional object array. This is because all the rows and columns need to be dynamic in our solution. Here's the JavaScript code to bind the grid:
function createGrid() {
var url = '#Url.Action("GetSheetData")';
$.get(url, { hospitalId: 100, screenCode: "Ledger", revisionId: 1, applicationUser: "TestUser" }, function (result) {
var columnDefs = result.Columns;
var data = result.Data;
// Now, create the grid using columnDefs as argument
var grid = $("#grid").kendoGrid({
dataSource: {
data: jQuery.parseJSON(data)
},
columns: columnDefs,
height: 430,
editable: "incell",
batch: true,
sortable: {
mode: "single",
allowUnsort: false
},
filterable: {
extra: false,
operators: {
string: { contains: "Contains" }
}
},
scrollable: {
virtual: true
},
navigatable: true
}).data("kendoGrid");
});
}
And the function to post the grid back to the server:
function saveGrid() {
var gridDataArray = $('#grid').data('kendoGrid')._data;
var url = '#Url.Action("SetSheetData")';
$.post(url, {
hospitalId: 100
, screenCode: "Ledger"
, revisionId: 1
, applicationUser: "TestUser"
, dataGrid: JSON.stringify(gridDataArray)
}
, function (result) {
var grid = $("#grid").data("kendoGrid");
});
}
The problem with this method is dat when we call the code:
var gridDataArray = $('#grid').data('kendoGrid')._data;
and post the data with:
JSON.stringify(gridDataArray);
all the items in the stringified object become string types. Even those who are numeric. I want my data to maintain the right datatypes
Does anyone know how to keep my grid data type safe?
Any other solutions that do not contain the JavaScript method are fine as well, as long as it supports a two dimensional object array as a type.
I hope the question is clear. Thanks in advance
1) _data is private (as per the _), you should not really be calling that! ds.data() will do the job.
2) the way you retrieving/storing data is not wrong, but I would suggest you to make your life easier and define kendo Transport properly :
var dataSource = new kendo.data.DataSource({
transport: {
read: {
url: "http://demos.kendoui.com/service/Products",
dataType: "jsonp"
}
}
3) as far as the types are involved, use model allowing you define types.
var dataSource = new kendo.data.DataSource({
schema: {
model: {
id: "ProductID",
fields: {
//data type of the field {Number|String|Boolean|Date} default is String
UnitPrice: {
type: "number",
defaultValue: 42
}
}
}
}
});

kendo ui Combobox binded to a Grid losts it's value and text after Grid's Cancel button pressed

I have two views in database. Bonuses and employees. One(employee)-to-many(bonuses).
I have kendo ui grid (kendo web), which displays ajax results from controller called Bonuses
And an autocompliting element - Employee Combobox binded with Employee filed of a grid.
Grid's datasource:
// bind json result from /Bonuses/GetPagedJsonBonuses
var bonusesDataSource = new kendo.data.DataSource({
transport: {
read: "#Url.Action("GetPagedJsonBonuses", "Bonuses")",
update: {
url: "#Url.Action("Edit", "Bonuses")",
type: "PUT"
},
create: {
url: "#Url.Action("Create", "Bonuses")",
type: "POST"
},
parameterMap: function(options, operation) {
if (operation === "update" || operation === "create") {
// updates the BonusDTO.EmployeeId with selected value
if (newValueEmployeeId !== undefined)
options.EmployeeId = newValueEmployeeId;
}
return options;
}
},
schema: {
data: "Data", // PagedResponse.Data
total: "TotalCount", // PagedResponse.TotalCount
model: {
id: "BonusId", // Data
fields: {
EmployeeId: { type: "number" },
EmployeeLastName: {
type: "string",
editable: true,
//validation: { required: {message: "Employee's last name is required"}}
},
Amount: {
type: "number",
editable: true,
nullable: false,
validation: {
required: { message: "Amount is required to be set" }
}
}
} // fields
} // model
}// schema
});
Grid element looks like this:
// creates bonuses grid control
$("#bonusesGrid").kendoGrid({
dataSource: bonusesDataSource,
toolbar: ["create"],
editable: "inline",
columns: [
"BonusId",
"EmployeeId",
{
field: "EmployeeLastName",
editor: employeeAutocompletingEditor,
template: "#=EmployeeLastName#"
},
"Amount",
{
command: ["edit"],
title: " "
}
],
save: function(e) {
if (newValueEmployeeId !== undefined && newValueEmployeeLastName !== undefined) {
e.model.EmployeeId = newValueEmployeeId; // it's a hack to bind model and autocomplete control
e.model.EmployeeLastName = newValueEmployeeLastName;
}
},
edit: function(e) {
setCurrentValueEmployeeIdAndLastName(e.model.EmployeeId, e.model.EmployeeLastName);
},
cancel: function(e) {
setCurrentValueEmployeeIdAndLastName(e.model.EmployeeId, e.model.EmployeeLastName);
}
});
Autocompleting combobox has it's own datasource using ajax:
// datasource for autocomlete combobox to lookup employees names from
var employeesDataSource = new kendo.data.DataSource({
transport: {
read: "#Url.Action("GetJsonEmployeesByLastName", "Bonuses")",
},
parameterMap: function(options, operation) {
if (operation === "update" || operation === "create") {
setNewValueEmployeeIdAndLastName(options.Id, options.LastName);
}
return options;
},
});
Autocompliting combobox look's like this:
function employeeAutocompletingEditor(container, options) {
$('<input required data-text-field="LastName" data-value-field="EmployeeId" data-bind="value:' + options.field + '"/>')
.appendTo(container)
.kendoComboBox({
// sets the local variables to update values of current row.
change: function() {
setNewValueEmployeeIdAndLastName(this.value(), this.text());
},
dataBinding: function (e) {
console.log("dataBinding: ", e, this.dataItem());
},
dataBound: function (e) {
console.log("dataBound: ", e, this.dataItem());
},
dataSource: employeesDataSource
});
}
I use Editor binding to pass values(EmployeeId) and Text (EmployeeLastName) from grid to combobox.
I also use a hack like temporal variables (newValueEmployeeId, currentValueEmployeeId) to
send selected Employee in combobox and pass it to grid for correct save. A found it's a most common practice to pass a value back to grid.
My problems is:
If I press Edit button on my grid first time The combobox displays current employee's name from grid row:
If I press Cancel button and again press Edit button, combobox doesn't display current value of grid (employee's name)
IF I type some name, change some other values, and Udate(save) value, next time combobox again displays employees name, but only once before Cancel was pressed.
I'm very new in Kendo UI and this problem drives me crazy...
I think that combobox losts it's binding or doesn't update smth. I tried to set values while onBound and onBinding events, but this doesn't help. Please help me with an advice and example.
PS all evenets and functions is my try to debug and find solution.
only one fix helped me:
var employeeList = new List<Employee>()
employeeList.add(new Emplpyee()) // add fake employee record.
return Json(employeeList)
I don't know why, but grid control start make cyclying empty ajax requests if I return empty list of employees or null. This doesn't work:
return Json(new List<Employee>());
return Json(null);
I think it's a problem in kendo combobox itself ,because it's not ready to receive and handle empty list or null as json result.
Also I heared something, that JQuery doesn't support empty or null results anymore...maybe that's the reason

MVC ASP.NET - Getting jtable to call httpPost method?

I'm trying my hand at jTables with MVC 3, but have run into an issue. When my page loads, I'm not getting any calls to my [HttpPost] method. I think because of this, I keep getting the 'error connecting to database' message.
Can someone explain why my [HttpPost] method isn't getting called? Here's the relevant code:
<div id="CompetitionTable""></div>
<script type="text/javascript">
$(document).ready(function () {
//Prepare jtable plugin
$('#CompetitionTable').jtable({
title: 'The Events List',
paging: true, //Enable paging
pageSize: 10, //Set page size (default: 10)
sorting: true, //Enable sorting
defaultSorting: 'Name ASC', //Set default sorting
actions: {
listAction: '#Url.Action("EventList", "CompetitionController")'
},
fields: {
EventID: {
key: true,
create: false,
edit: false,
list: false
},
EventName: {
title: 'Name',
width: '15%'
},
CompetitorEmail: {
title: 'Email address',
list: false
},
CompetitorName: {
title: 'Competitor',
width: '15%',
},
Score: {
title: 'Score',
width: '10%',
}
}
});
//Load list from server
$('#CompetitionTable').jtable('load');
});
</script>
[HttpPost]
public JsonResult EventList(int compId)
{
try
{
//Get data from database
List<Event> events = Event.getEventsByCompetitionId(compId);
//Return result to jTable
return Json(new { Result = "OK", Records = events});
}
catch (Exception ex)
{
return Json(new { Result = "ERROR", Message = ex.Message });
}
}
The way you call listAction is wrong. you should call it like this '/CompetitionController/EventList'
Your MVC action waits for a parameter (compId). But your lisAction does not provide that:
listAction: '#Url.Action("EventList", "CompetitionController")'
It must be something like that:
listAction: '#Url.Action("EventList", "CompetitionController")compId=5'
Probably, this table is populated dynamically for every competition and it's known on the server side. So, it must be something like:
listAction: '#Url.Action("EventList", "CompetitionController")compId=#ViewBag.compId'
Surely, you must set compId in the action of this view.

Simple jqgrid Delete in MVC

I've been trawling across the web for answers, and maybe it's a case of it being more complicated than I expect (or I just don't understand the solutions), but I am looking for a way to simply delete a selected row from my jqgrid by clicking the trash icon.
Currently my grid is being populated with Linq to SQL data.
Here is my grid:
jQuery("#grid").jqGrid({
url: '<%= ResolveUrl("~/Home/GetData") %>',
datatype: "json",
mtype: 'GET',
postData: { DDLid: function () { return jQuery("#DDL option:selected").val(); } },
colNames: ['Col1', 'Col2'],
colModel: [
{ name: 'Col1', index: 'Col1', width: 200, editable: false },
{ name: 'Col2', index: 'Col2', width: 200, editable: false }
],
jsonReader: {
repeatitems: false
},
rowNum: 10,
pager: jQuery('#gridpager'),
sortname: 'Type',
viewrecords: true,
sortorder: "asc",
caption: "Table"
}).navGrid('#gridpager', { del: true, add: false, edit: false, search: false }, {}, {}, {url: "Delete"});
Now the 'id' in post data is NOT the primary key in this table - I just need it to help populate the grid.
What I would like to get is the selected row id and pass it to the Delete method, but I can't find any way to do that.
I have tried using jQuery("#grid").getGridParam('selrow') in the postData but it always returns null.
Any help would be greatly appreciated.
Here is my delete method, for reference:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Delete(int DDLid)
{
int row = Convert.ToInt32(/*I NEED THIS ID*/);
var query = from x in _ctx.DataTable
where ((x.id == row))
select x;
_ctx.DataTable.DeleteOnSubmit(query.Single());
_ctx.SubmitChanges();
return Json(true);
}
This method is called and is fine, but I am getting the wrong id. I need the selected row's id. This breaks because the DDLid returns more than one row (since it is used to populate the grid).
I hope that makes sense.
I discovered where I would pass the selected index (but then I realised I was looking for the primary key, rather than selected index, but it is the same result regardless)
I needed to add this to my navGrid:
{url: "Delete", mtype: "POST", reloadAfterSubmit: true,
serializeDelData: function (postdata) {
var selectedrowindex = jQuery("#grid").jqGrid('getGridParam', 'selrow');
var dataFromCellByColumnIndex = jQuery('#grid').jqGrid ('getCell', selectedrowindex , 1);
return {DDLid: postdata.id, name: dataFromCellByColumnIndex};
}
});
So this passes a column value to my delete method as well as the DDLid, but I could easily swap dataFromCellByColumnIndex with selectedrowindex.
You should just implement Delete action having id parameter:
public JsonResult Delete(string id) {
...
}
To reference the action in the JavaScript code I would use 'url: <%= Url.Action("Delete") %>' instead of url: "Delete" which you use currently.
You can download here demo project which I created for the answer. The project implement deleting of the row together with many other features which you currently not need.
You can make another post inside delete method with your own params. After defining grid, you can define each action in detail. It is posting actual delete with correctid and original one is posting fake id. JQgrid is using row count for delete not the primary key. They may change it with recent versions, but this was working Jqgrid 3.8
jQuery("#ClientGrid").jqGrid('navGrid', '#ClientGridPager',
{ view: true, edit: true, search: false }, //options
{height: 240, caption: 'Edit Client', beforeShowForm: hideIDColumn, reloadAfterSubmit: true, mtype: 'POST', url: "/Settings/EditClient", jqModal: false, closeOnEscape: true, bottominfo: "Fields marked with (*) are required" }, // edit options
{height: 340, caption: 'Add Client', beforeShowForm: hideIDColumn, reloadAfterSubmit: true, mtype: 'POST', url: "/Settings/CreateClient", jqModal: false, closeOnEscape: true, bottominfo: "Fields marked with (*) are required", closeAfterAdd: true }, // add options
//delete method
{reloadAfterSubmit: true, beforeSubmit: function (postdata, formid)
{
var lastselectedID = -1;
if (ClientGridrow != null || typeof (ClientGridrow) != "undefined")
{
lastselectedID = $("#ClientGrid").getCell(ClientGridrow, 'ID_PK');
}
//CUSTOME delete to send taskid instead of rowid
$.ajax({ type: "POST", url: "/Settings/DeleteClient/?objid=" + lastselectedID,
data: "", success: function (response)
{
$("#ClientGrid").trigger("reloadGrid"); //reloadAfterSubmit: true is not working in Chrome
}
});
return [true, "Delete failed message"];
}, caption: 'Delete Client', datatype: 'local', url: "/Settings/DeleteClient/?objid=-1", jqModal: false, closeOnEscape: true
}, // del options..we make two posts
{closeOnEscape: true }, // search options
{height: 230, width: 350, jqModal: false, closeOnEscape: true} // view options
);

How to implement search on jqgrid?

So I've got basic example of jqgrid working in ASP.NET MVC, the javascript looks like this:
$(document).ready(function() {
$("#list").jqGrid({
url: '../../Home/Example',
datatype: 'json',
myType: 'GET',
colNames: ['Id', 'Action', 'Parameters'],
colModel: [
{ name: 'id', index: 'id', width: 55, resizable: true },
{ name: 'action', index: 'action', width: 90, resizable: true },
{ name: 'paramters', index: 'parameters', width: 120, resizable: true}],
pager: $('#pager'),
rowNum: 10,
rowList: [10, 20, 30],
sortname: 'id',
sortorder: 'desc',
viewrecords: true,
multikey: "ctrlKey",
imgpath: '../../themes/basic/images',
caption: 'Messages'
});
Now I am trying to implement the search button that they have in the jqgrid examples (click on Manipulating/Grid Data). But I don't see how they implement it. I'm expecting e.g. a "search:true" and a method to implement it.
Has anyone implemented search on jqgrid or know of examples that show explicitly how to do it?
I recently implemented this myself (yesterday actually) for the first time. The biggest hurdle for me was figuring out how to write the controller function. The function signature is what took me the longest to figure out (notice the _search, searchField, searchOper, and searchString parameters as those are missing from most of asp.net mvc examples I've seen). The javascript posts to the controller for both the initial load and for the search call. You'll see in the code that I'm checking whether the _search parameter is true or not.
Below is the controller and the javascript code. My apologies for any formatting issues as this is my first time posting on here.
public ActionResult GetAppGroups(string sidx, string sord, int page, int rows, bool _search, string searchField, string searchOper, string searchString)
{
List<AppGroup> groups = service.GetAppGroups();
List<AppGroup> results;
if (_search)
results = groups.Where(x => x.Name.Contains(searchString)).ToList();
else
results = groups.Skip(page * rows).Take(rows).ToList();
int i = 1;
var jsonData = new
{
total = groups.Count / 20,
page = page,
records = groups.Count,
rows = (
from appgroup in results
select new
{
i = i++,
cell = new string[] {
appgroup.Name,
appgroup.Description
}
}).ToArray()
};
return Json(jsonData);
}
And here is my HTML/Javascript:
$(document).ready(function() {
$("#listGroups").jqGrid({
url: '<%= ResolveUrl("~/JSON/GetAppGroups/") %>',
datatype: 'json',
mtype: 'GET',
caption: 'App Groups',
colNames: ['Name', 'Description'],
colModel: [
{ name: 'Name', index: 'Name', width: 250, resizable: true, editable: false},
{ name: 'Description', index: 'Description', width: 650, resizable: true, editable: false},
],
loadtext: 'Loading Unix App Groups...',
multiselect: true,
pager: $("#pager"),
rowNum: 10,
rowList: [5,10,20,50],
sortname: 'ID',
sortorder: 'desc',
viewrecords: true,
imgpath: '../scripts/jqgrid/themes/basic/images'
//});
}).navGrid('#pager', {search:true, edit: false, add:false, del:false, searchtext:"Search"});
See my article on codeproject, which explains how we can do multiple search in jqgrid:
Using jqGrid’s search toolbar with multiple filters in ASP.NET MVC
I use IModelBinder for grid's settings binding, expression trees for sorting and filtering data.
In case you're still wondering about dealing with optional parameters, just declare them as nullables by adding a ? after the type name.
Now you'll be able to compare them with null to check if they are absent.
Note that you don't need to do this with strings, as they are already nullable.
#Alan - ok, I used your method and extended my webservice to expect those additional three parameters and check for "_search" is true/false. But, in order to make this work, I had to add this to my ajax call in the JavaScript:
if (!postdata._search) {
jQuery("#mygrid").appendPostData( {searchField:'', searchOper:'', searchString:''});
}
Just follow this link. It has all implementations explained...
You can create a button searchBtn and can invoke search form on click
$("#searchBtn").click(function(){
jQuery("#list4").searchGrid(
{options}
)});

Resources