Kendo, how to do Grid server paging using mvc4 helper - asp.net-mvc

I am using mvc4. On one of my page, it has Kendo Grid. I want to show 5 rows per page. I have no problem doing it using pure javascript, however, If I am using mvc helper. I got lost, couldn't find any samples online.
here's my javascript code.
<script language="javascript" type="text/javascript">
$(document).ready(function () {
$("#grid").kendoGrid({
dataSource: {
type: "json",
serverPaging: true,
pageSize: 5,
transport: { read: { url: "Products/GetAll", dataType: "json"} },
schema: { data: "Products", total: "TotalCount" }
},
height: 400,
pageable: true,
columns: [
{ field: "ProductId", title: "ProductId" },
{ field: "ProductType", title: "ProductType" },
{ field: "Name", title: "Name" },
{ field: "Created", title: "Created" }
],
dataBound: function () {
this.expandRow(this.tbody.find("tr.k-master-row").first());
}
});
});
now if i am using mvc helper
#(Html.Kendo().Grid(Model)
.Name("Grid") //please help me to finish the rest
Update:
Adding the action code.
[HttpPost]
public ActionResult GetAll([DataSourceRequest]DataSourceRequest request, int id)
{
var products= ProductService.GetAll(id);
return Json(products.ToDataSourceResult(request));
}
GetAll method in the service layer:
public IQueryable<Product> GetAll(int id)
{
var products= ProductRepository.Get(p => p.Id== id && p.IsActive == true, null, "ProductionYear")
.OrderBy(o => o.Name); //.ToList();
return Product.Select(p => new ProductVM()
{
Name = p.Name,
ProductionYear= p.ProductionYear.Year.ToString()
Id = p.Id
}).AsQueryable();
}
now, if I run the app, i will get following error:
"LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression."}
in the GetAll method, I comment out the "ToList()". If I use ToList, everything works. but I will query all the rows back instead just those rows I needed for that page.

You can set the PageSize inside the DataSource method. So you will need something like:
#(Html.Kendo().Grid(Model)
.Name("Grid")
.DataSource(dataSource => dataSource.Ajax()
.PageSize(5)
.Read(c => c.Action("GetAll", "Products")
.Model(s => s.Id(m => m.Id)))
.Columns(columns =>
{
columns.Bound(m => m.ProductId).Title("ProductId");
//other colums
})
.Events(g => g.DataBound("somefunction"))
.Pageable(true))
You can find more info the KendoUI Grid's Asp.NET MVC wrappers documentation.

If you are not using Kendo's ASP.NET MVC wrappers and are using the Kendo JavaScript objects directly and you are trying to do server side paging, then you need to create your datasource as follows:
var dataSource = {
"type":"aspnetmvc-ajax",
"serverSorting": true,
"serverPaging": true,
"page": 1,
"pageSize": 8,
"schema": {
"data":"items",
"total":"count",
"errors":"errors"
}
};
And your Read controller method will look something like:
public ActionResult List_Read([DataSourceRequest]DataSourceRequest request) {
try {
var model = null /* prepare your model object here contain one page of grid data */;
// using Json.NET here to serialize the model to string matching the
// schema expected by the data source
var content = JsonConvert.SerializeObject(new { count = model.Count, items = model.Items }, Formatting.None);
return Content(content, "application/json");
}
catch (Exception exception) {
// log exception if you need to
var content = JsonConvert.SerializeObject(new { errors = exception.Message.ToString() }, Formatting.None);
return Content(content, "application/json");
}
}
type, serverSorting and serverPaging are important to make sure paging and sorting happens server-side. type must be aspnetmvc-ajax, otherwise the query data will not be recognized by the model binder that has been specified by the [DataSourceRequest] attribute in the Read method. You cannot omit that attribute unless you want to write your own custom modelbinder to parse the query data sent by the kendo dataSource, which does not conform to the modelbinding conventions used by the DefaultModelBinder in ASP.NET MVC.

The other answer will work if you are using Kendo UI for ASP.NET MVC. If you don't you need to implement paging yourself. The Kendo DataSource will send pageSize and current page when making a request. You can use that to do the paging. It also sends "take" and "skip" which makes things even easier (hint Linq).

The error
"LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression."}
is self explanatory, to gat around this, you should do something like
return Product.**AsEnumerable**.Select(p => new ProductVM()
{
Name = p.Name,
ProductionYear= p.ProductionYear.Year.ToString()
Id = p.Id });
using AsEnumerable brings all records from the db, so best to use after any filtering
products.where(x => x.active = true).AsEnumerable
rather then
products.AsEnumerable.where(x => x.active = true)

Download kendo examples, then unzip and follow
<your directory>\Kendo UI for ASP.NET MVC Q1 2013\wrappers\aspnetmvc\Examples\Areas\razor\Views\web\grid\
for the view index
and
<your directory>\Kendo UI for ASP.NET MVC Q1 2013\wrappers\aspnetmvc\Examples\Controllers
for IndexController
in your view you might also want .ServerOperation(true) as bellow to avoid
Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.
If you have big data to fetch
#(Html.Kendo().Grid(<yourmodel>)
.Name("Grid")
.Columns(columns =>
...
})
.Filterable()
.Pageable()
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(8)
.ServerOperation(true) )
.Model(model =>
{
model.Id(p => p.Id);
...
})
)
)
also in controller in ActionResult Products_Read consider
DataSourceResult result = GetProducts().ToDataSourceResult(request, o => new { Id = o.Id, ... } );
return Json(result , JsonRequestBehavior.AllowGet);

Related

Kendo Grid Inline Editing Get current row data when updating

I am using Inline editing in a Kendo Grid and I want to be able to get the current row data when doing an update.
Html.Kendo().Grid<WheresMyTool.Models.COMPANYADDRESS>()
.Name("ShipToLocationsGrid")
.Editable(editable => editable.Mode(Kendo.Mvc.UI.GridEditMode.InLine))
.Pageable(pager => pager.PageSizes(new List<object> { 5, 10, 20, 100 }))
.DataSource(dataSource => dataSource
.Ajax()
.Model(model => model.Id(p => p.COMPANY))
.Read(read => read.Action("IndexShipToLocations", "Company", new { company = Model.company }))
.Update(update => update.Action("ShipToLocations_Update"))
.PageSize(20)
)
Here is my update method
public ActionResult ShipToLocations_Update([DataSourceRequest] DataSourceRequest request, COMPANYADDRESS ca)
{
Repository repo = new Repository();
repo.UpdateCompanyAddressRecord(ca, username);
return Json(ca);
}
I want to be able to access the current data. It seems like only the modified data is passed in.
[HttpPost]
public ActionResult EditTestStationInLine([DataSourceRequest] DataSourceRequest request, TestStationViewModel tsvm)
{
if(tsvm != null && ModelState.IsValid)
{
TestStation ts = _testStationService.Find(tsvm.Id);
ts.ProductionLineId = tsvm.ProductionLineId;
ts.ProdLine = _productionLineService.Find(tsvm.ProductionLineId);
ts.Name = tsvm.Name;
_testStationService.Update(ts);
tsvm.Id = ts.Id;
tsvm.ProductionLineId = ts.ProductionLineId;
}
return this.Json(new[] { tsvm }.ToDataSourceResult(request, ModelState));
}
This is an example of one of my InLine edits. Your view model is used to create a database model. Once your database model is created, you can pass back the hidden attributes, in my case the Id and CreateAt. Hope this helps answer your question.
Edit:
function Edit(e) {
var grid = $('#NAMEOFKENDOGRID').data('kendoGrid');
var dataItem = grid.dataItem($(e.target).parents('tr'))
var id = dataItem.id;
$.ajax({//Makes an ajax call
success: function (data) {
$('#NAMEOFDIV').load("/Controller/Update/" + id);
}
});
}
You can make a custom command, conveniently called Edit, which then gets the Id from that row. It also makes an ajax call to your update method. Modify the ajax to fit your needs.

kendo ui autocomplete - how to pass parameters to controller method to filter results

How can I pass parameters that reference other elements in the UI (such as textbox values, radio buttons etc) to the controller that gets called to fetch the data for autocomplete?
eg. from the example in the docs, when I call the Home controller GetProducts action, how can I send some parameters along with the call?
Or even better, post a json object that contains values referencing data from 2-3 other widgets in the ui?
https://docs.telerik.com/aspnet-mvc/helpers/autocomplete/overview#ajax-binding
#(Html.Kendo().AutoComplete()
.Name("productAutoComplete") //The name of the AutoComplete is mandatory. It specifies the "id" attribute of the widget.
.DataTextField("ProductName") //Specify which property of the Product to be used by the AutoComplete.
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetProducts", "Home"); //Set the Action and Controller names.
})
.ServerFiltering(true); //If true, the DataSource will not filter the data on the client.
})
)
You could pass additional data either using object route values
E.g.
.Read(read => read.Action("Products_Read", "Home", new { name = "test", id = 2 }))
OR
via the Data method,
which specifies a JavaScript function, which will return the additional parameters.
E.g.
.Read(read => read.Action("Products_Read", "Home").Data("additionalInfo"))
function additionalInfo() {
return {
name: "test",
id: 2
}
}
or Via Template Delegate
//By Template Delegate
Read(read => read.Action("Products_Read", "Home").Data(#<text>
function() {
//pass parameters to the Read method
return {
name: "test",
id: $("#search").val()
}
}
</text>))
In all cases, you should add additional parameters to your Action.
E.g.
public ActionResult Products_Read([DataSourceRequest] DataSourceRequest request, string name, int id) {...}

Kendo Grid has correct JSON but data not showing up

I'm trying to use Teleric's Kendo Grid in ASP.NET MVC 5. I'm following the example here, but the grid is not populating with data. The columns show up, but it says "No items to display".
http://docs.telerik.com/kendo-ui/getting-started/using-kendo-with/aspnet-mvc/helpers/grid/ajax-binding
This is my Words/Read function:
public ActionResult Read([DataSourceRequest] DataSourceRequest request)
{
var items = db.Words.Take(1).ToList();
//this code was necessary to get the correct JSON result
JsonSerializerSettings jsSettings = new JsonSerializerSettings();
jsSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
var converted = JsonConvert.SerializeObject(items, null, jsSettings);
return Content(converted);
}
I got the following JSON result from Words/Read:
[{"WordId":1,"Rank":1,"PartOfSpeech":"article","Image":"Upload/29/1/Capture1.PNG","FrequencyNumber":"22038615","Article":null,"ClarificationText":null,"WordName":"the | article","MasterId":0,"SoundFileUrl":"/UploadSound/7fd752a6-97ef-4a99-b324-a160295b8ac4/1/sixty_vocab_click_button.mp3","LangId":1,"CatId":null,"IsActive":false}]
Finally, my view page:
#(Html.Kendo().Grid<NextGen.Models.Word>
()
.Name("grid")
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(15)
.Read(read => read.Action("Read", "Words"))
)
.Columns(columns =>
{
columns.Bound(c => c.WordName);
columns.Bound(c => c.PartOfSpeech);
})
.Pageable()
.Sortable()
)
I've seen some similar questions to this one, but most are using Javascript rather than Html helpers. Am I doing something dumb?
I found an alternative way to get around the circular reference problem I was having. Basically, the answer is here: A circular reference was detected while serializing an object of type 'SubSonic.Schema .DatabaseColumn'.
For people with the same problem: I took the two properties I needed - WordName and PartOfSpeech, and passed only those attributes to the toDataSource function Kendo provides.
My new read function looks like this:
public ActionResult Read([DataSourceRequest] DataSourceRequest request)
{
var items = db.Words.Select(w => new WordL{
WordName = w.WordName,
PartOfSpeech = w.PartOfSpeech
});
return Json(items.ToDataSourceResult(request));
}
where WordL is a model with just PartOfSpeech and WordName attributes, rather than all the attributes of my class.
It is probably because you are not using 2 methods in your controller. While your page view is first ActionResult you should use second one as in the grid as you did in your example. I hope it is obvious.
public ActionResult ReadPage()
{
return View();
}
public ActionResult Read([DataSourceRequest] DataSourceRequest request)
{
var items = db.Words.Take(1).ToList();
//this code was necessary to get the correct JSON result
JsonSerializerSettings jsSettings = new JsonSerializerSettings();
jsSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
var converted = JsonConvert.SerializeObject(items, null, jsSettings);
return Content(converted);
}

max json length error when populating telerik grid

I am trying to populate a telerik grid from a table based on date selected.
I get an error message:
System.InvalidOperationException: Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property
Is there any other way I can populate Telerik grids on client events?
view:
<%= Html.Telerik().DatePicker().Min("01/01/2011")
.Name("Date")
.ClientEvents(e => e.OnChange("Date_onChange"))
%>
<div>
<%= Html.Telerik().Grid<DataAccess.Entity.Dto.ReportData>()
.Name("tblGrid")
.DataBinding(databinding => databinding.Ajax()
.Select("MasterDataGrid", "Report"))
.Columns(col =>
{
col.Bound(f => f.ID).Title("ID Number");
col.Bound(f => f.Date).Title("Date");
})
.Filterable(filter => filter.Enabled(true))
.Pageable(page => page.PageSize(200))
.Sortable(sort => sort.Enabled(true))
%>
</div>
JavaScript:
function Date_onChange() {
var link = '/Report/DataGrid';
var Date = $("#Date").val().toString();
$.ajax({
type: 'POST',
url: link,
data: { date: Date },
dataType: 'json',
success: function (result) {
$("#tblGrid").data("tGrid").dataBind(result.data);
},
error: function (result) {
$("#ErrorMessage").text(result.message);
}
});
};
Controller:
[HttpPost]
[GridAction]
public ActionResult DataGrid(DateTime date)
{
var model = context.GetReportData_tblData(date);
return View(new GridModel { Data = model });
}
It looks like you are returning far too much data. There are two possible scenarios here, and it may be one or the other or both.
(1) As your page size is 200, this implies that you are expecting a lot of data, so you may be returning far too many objects. By default, the Telerik Grid will take the entire collection and do the sorting and filtering client-side. I would try implementing server-side paging and sorting so that you only return 1 page of data at a time.
View:
#(Html.Telerik.Grid<IEnumerable<MyClass>>()
.Name("MyGrid")
.Columns(col => // column definitions)
.EnableCustomBinding(true)
.Pageable(pg =>
{
paging.Total(Model.TotalCount);
paging.PageSize(Model.PAGE_SIZE);
})
.DataBinding(db =>
{
db.Ajax().Select("DataGrid", "MyController");
}
)
Controller:
[GridAction(EnableCustomBinding = true)]
public ActionResult DataGrid(GridCommand command, DateTime date)
{
MyModel model = new MyModel();
IEnumerable<MyClass> data = context.GetReportData_tblData(date);
// apply paging, filtering, grouping, and sorting
GridModel gm = data.AsQueryable().ToGridModel(command.Page, command.PageSize, command.SortDescriptors, command.FilterDescriptors, command.GroupDescriptors);
return View(gm);
}
Telerik provides a handy extension method to apply all the paging, sorting, filtering and grouping: IQueryable<T>.ToGridModel(), which you can find in the Telerik.Web.Mvc.Extensions assembly (h/t: vladimir77).
(2) If your DataAccess.Entity.Dto.ReportData class has a lot of properties, and/or some of its properties have long strings or child objects which are also recursively serialized, then you can easily hit the transfer limit with just a few objects. I notice you are only using two properties in the Grid. Please consider creating view model objects that only have the data that you need (that's good practice anyway).
You can increase the default JavaScriptSerializer.MaxJsonLength. Just see this answer: https://stackoverflow.com/a/7207539/443379
After this the filtering, paging... functionality won't work anymore. All you have to do though is use CustomBinding (inspired from #howcheng's answer):
[GridAction(EnableCustomBinding=true)]
public ActionResult DataGrid(GridCommand command, DateTime date)
{
var data = context.GetReportData_tblData(date)
.ToGridModel(command.Page, command.PageSize, command.SortDescriptors, command.FilterDescriptors, command.GroupDescriptors);
var serializer = new JavaScriptSerializer();
var result = new ContentResult();
serializer.MaxJsonLength = Int32.MaxValue; // overriding the default max length
result.Content = serializer.Serialize(data);
result.ContentType = "application/json";
return result;
}
This works even for Client Operation Mode. You can refactor this to a class: http://macaalay.com/2011/09/27/large-json-result-for-teleriks-mvc-grid/

ASP.NET MVC Sort Listing by Selected item in DropDownList

I have a List of Listings within my Database. On my View, I have a DropDownList which contains categories. A category contains many listings.
When I select a specific category, I wish to only display those listings which have the category selected.
My main worry is how to generate the JQuery to call my SortListing method in my ListingController.
Here is the current HTML (Razor):
#Html.DropDownListFor(m => m.Categories, Model.Categories, "Select a Category")
My SortListing Method:
public List<Listing> SortListing(string categoryId)
{
var listings = new List<Listing>();
foreach (var listing in _service.ListAllEntities<Listing>())
{
if (listing.CategoryId == categoryId)
{
listings.Add(listing);
}
}
return listings;
}
EDIT I have the following. Put the categoryGuid is coming in null.
This is my code:
$(function () {
$('#SelectedCategoryGuid').change(function () {
var url = $(this).data('url');
var categoryId = $(this).val();
$.ajax({
url: url,
type: 'GET',
cache: false,
data: { categoryId: categoryId },
success: function (result) {
// TODO: manipulate the result returned from the controller action
}
});
});
});
</script>
<h2>Index</h2>
#Html.ActionLink("Create New Listing", "Create")
<br/>
<strong>Filter Listings</strong>
#Html.DropDownListFor(
m => m.SelectedCategoryGuid,
Model.Categories,
"Select a Category",
new {
id = "SelectedCategoryGuid",
data_url = Url.Action("SortListing", "Listing")
}
)
My main worry is how to generate the JQuery to call my SortListing
method in my ListingController.
Actually you should be having other worries as well that I will try to cover throughout my answer.
There's an issue with your DropDownListFor helper. You have used the same property on your model for both binding the selected category value and the list of categories which is wrong. The first parameter of the DropDownListFor helper represents a lambda expression pointing to a primitive type property on your view model:
#Html.DropDownListFor(
m => m.CategoryId,
Model.Categories,
"Select a Category",
new {
id = "categoryDdl",
data_url = Url.Action("SortListing", "Listing")
}
)
and then subscribe to the .change() event and trigger the AJAX request:
$(function() {
$('#categoryDdl').change(function() {
var url = $(this).data('url');
var categoryId = $(this).val();
$.ajax({
url: url,
type: 'GET',
cache: false,
data: { categoryId: categoryId },
success: function(result) {
// TODO: manipulate the result returned from the controller action
}
});
});
});
Now let's take a look at your SortListing controller action because there are issues with it. In ASP.NET MVC standard convention dictates that controller actions must return ActionResults. In your case you seem to be returning some List<Listing>. So the first thing you have to decide is the format you would like to use. One possibility is to return those listings as JSON formatted values:
public ActionResult SortListing(string categoryId)
{
var listings = _service
.ListAllEntities<Listing>()
.Where(x => x.CategoryId == categoryId)
.ToList();
return Json(listings, JsonRequestBehavior.AllowGet);
}
In this case inside your AJAX success callback you will receive this collection of listings and you will have to update your DOM:
success: function(result) {
// result represents an array of listings
// so you could loop through them and generate some DOM elements
}
Another possibility is to have your controller action return a partial view:
public ActionResult SortListing(string categoryId)
{
var listings = _service
.ListAllEntities<Listing>()
.Where(x => x.CategoryId == categoryId)
.ToList();
return PartialView("Listings", listings);
}
and then you will have a corresponding partial view:
#model List<Listing>
#foreach (var listing in Model)
{
<div>#listing.SomeProperty</div>
}
and then inside the success callback you will refresh some containing placeholder:
success: function(result) {
$('#SomeDivIdThatWrapsAroundTheListingsPartial').html(result);
}
So to recap you could either have the controller action return JSON and then manually build the corresponding DOM tree using javascript or return a partial view which will already contain the corresponding markup and simply refresh some containing div with this partial.

Resources