How to sync Kendo UI mvc Grid with Kendo Mvc Autocomplete - asp.net-mvc

I am working asp.net mvc witk Kendo UI MVC Tools. I am trying to display list of records in a Kendo UI Mvc Grid. and i have one kendo ui autoComplete Textbox when i type a letter it shows corresponding field record that matches the criteria will be showed like a dropdown. Now i want to sync the autocomplete textbox with kendo ui mvc grid. that means when i type a letter records that matches criteria should display in the grid. I have tried with change event but it doesnt seems work for me.
#(Html.Kendo().AutoComplete().Events(c=>c.Change("GridChange"))
.Name("txtSearchItem")
.Filter("startswith")
.DataTextField("xxx")
.Value(ViewBag.SearchValue)
.BindTo((List<MyRecords>)Model).HtmlAttributes(new { #class = "required", style = "font-size:19px;width:150px;", onkeypress = "return isNumericKey(event);" })
)
please guide me.

First, create a grid with a HeaderTemplate to make a combobox that behaves as an autocomplete as well.
#( Html.Kendo().Grid(Model)
.Name("Grid")
.ClientDetailTemplateId("inventoryTemplate")
.DataSource(ds => ds.Ajax()
.Read(r => r.Action("Read", "Home"))
)
.Columns(columns =>
{
columns.Bound(p => p.Item).Width(10)
.Filterable(false).Sortable(false).HeaderTemplate(#<text>
#(Html.Kendo().ComboBox()
.DataValueField("Items")
.Placeholder("Items")
.DataTextField("Items")
.Name("Items")
.DataSource(ds => ds.Read(rea => rea.Action("ListOfItems", "Home")))
.Events(ev => ev.Change("onComboListCodeChange"))
)
</text>);
})
)
Now create this method will get an array from Dictionary of filters, you will need it later.
function getArrayFromDic(dic) {
var arr = new Array();
arr = $.map(dic, function (n, i) {
return { field: n.field, operator: n.operator, value: n.value };
});
return arr;
}
This function will get a dictionary that represents the filters available on the grid. If there is more than one filter.
function getFilterDic() {
var grid = $('#Grid').data('kendoGrid');
var filtersDicTemp = {
};
if (grid.dataSource._filter) {
$.each(grid.dataSource._filter.filters, function (index, value) {
filtersDicTemp[value.field] =
{
field: value.field, operator: value.operator, value: value.value
}
});
}
return filtersDicTemp;
}
This will be called each time you change the value of the filter Autocomplete combobox in this case.
There is a method on the kendo.data.DataSource called filter, where you can pass an array of filters.
function onComboListCodeChange(e) {
var grid = $('#Grid').data('kendoGrid');
var filtersDic = getFilterDic();
if (this.value() && this.value() != 'All') {
if (this.value() != 'Items' && this.value() != '') {
filtersDic["Items"] =
{
field: "Items", operator: "startswith", value: this.value()
}
}
}
else {
if (filtersDic["Items"]) {
delete filtersDic["Items"];
}
}
var filters = getArrayFromDic(filtersDic);
grid.dataSource.filter(
filters
);
}
Hope it helped!

Use the approach from here http://demos.kendoui.com/web/grid/toolbar-template.html
The difference would be that you will use AutoComplete (which is almost the same) and you do not need to put it inside the Toolbar via the Toolbar template.

Related

Kendo Read Action - getting the control Id

I have a partial view using a Kendo DropDownListFor and within the Datasource Read action I am passing a function name to get additional parameters eg.
#(Html.Kendo().DropDownListFor(m => m.AssignedUserId)
.OptionLabel("Assign to ...")
.DataTextField("FullName")
.DataValueField("UserId")
.Filter(FilterType.StartsWith)
.AutoBind(true)
.DataSource(source => {
source.Read(read => { **read.Action("GetStaffList", "ManageUser").Data("filterAssigned");** })
.ServerFiltering(true);
})
)
function filterAssigned(e) {
// Selector needs to be prefixed "Create_" or "Update_" depending on view/HtmlFieldPrefix.
var ctl = $("#AssignedUserId").data("kendoDropDownList");
return {
staffFilter: (ctl.filterInput.val() || 'empty'),
selectedId: (ctl.value() || 0)
};
}
Within the "FilterAssigned" function I want to get the Id of the control rather than hardcoding $("#AssignedUserId").data("kendoDropDownList"). This is because when creating the View, I am setting the ViewData.TemplateInfo.HtmlFieldPrefix = "Create" so the actual control id is rendered as "Create_AssignedUserId" and I want to use the same function for Create, Update etc.
I have tried the usual options:
this.id
this.attr('id')
this.element.attr('id')
e.sender
Telerik state that the 'this' attribute is not obtainable during a read but I have seen no other solution other than the selector method. Basically I am trying to do this:
function filterAssigned() {
var ctl = **this.id**;
return {
staffFilter: (ctl.filterInput.val() || 'empty'),
selectedId: (ctl.value() || 0)
};
}
I found the best way to solve this was to use the razor helper #Html.IdFor. The control id this returns includes the HtmlFieldPrefix. The function will now look like ...
function filterAssigned() {
var ctl = $("##Html.IdFor(m => m.AssignedUserId)").data("kendoDropDownList");
return {
staffFilter: (ctl.filterInput.val() || 'empty'),
selectedId: (ctl.value() || 0)
};
}

Retrieve all drop-down values from database and show the saved value as selected one in drop down

I am using a kendo grid with pop up editing mode and editor template in mvc4. The template consists of a drop down list which is bound through database. The problem comes when in Edit operation all other data is retrieved along with all elements of drop down list and displayed in respective html attributes such a textbox ,checkbox etc but the value which i have saved from drop down list is not shown auto selected there.
Example
During add operation i have selected "Work" from the drop down list and saved it in database. At the time of Edit i want "Work" to be shown auto selected in drop down list.
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult Update([DataSourceRequest] DataSourceRequest request,Model object)
{
if (obj == null || !ModelState.IsValid) return null;
var obj = _Repo.FindBy(t => t.ID == object.ID);
obj.Text = object.Text;
obj.Type = object.Type;
obj.MasterTypeID_Fk = new Guid(object.Type);
obj.Notes = object.Notes;
obj.Primary = object.Primary;
obj.LastVerified = DateTime.Now.ToUniversalTime();
obj.LastUpdated = DateTime.Now.ToUniversalTime();
_Repo.Update(obj);
var result = new[] { object }.ToDataSourceResult(request, ModelState);
return Json(result);
}
//this is my code for ddl in editor template
#(Html.Kendo().DropDownList()
.Name("Type")
.DataTextField("Value")
.DataValueField("TypeID")
.DataSource(source =>
{
source.Read(read => { read.Action("GetTypes", "Dashboard"); });
})
)
// this is code in controller to bind ddl
public ActionResult GetTypes()
{
List<TypeModel> Type = _Repo.GetTypes().ToList().ToMap<TypeBO, TypeModel>();
Type.Insert(0, new TypeModel() { Value = "--Select--", TypeID = Guid.Empty });
return Json(Type, JsonRequestBehavior.AllowGet);
}
You can set your value in Html helper using Value("ValueData"):
#(Html.Kendo().DropDownList()
.Name("Type")
.DataTextField("Value")
.DataValueField("TypeID")
.DataSource(source =>
{
source.Read(read => { read.Action("GetTypes", "Dashboard"); });
})
.Value("ValueData")
)
Like in this example: http://demos.telerik.com/aspnet-mvc/dropdownlist/index

Kendo, how to do Grid server paging using mvc4 helper

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);

Dynamic telerik TabStrip

I'm working on a dynamic Telerik Tab Strip.
Each tab have a Grid and I need to pass a parameter to the action for filter my model but that parameter is allways null.
View:
#{ Html.Telerik().TabStrip()
.Name("TabStripDetailArticle")
.Items(tabstrip =>
{
//Know how Many Zones are there
var zones = Model.Articles.GroupBy(e => e.Zone);
//For each Zone I need a Tab,
foreach (var inZone in zones)
{
tabstrip.Add()
.Text(inZone.Key)
.Content(() =>
{
//On each tab there's a Grid and I need to pass the zone to filter my model.
#Html.Action("TabStripSelected", Controllers.Valoration, new { idZone = inZone.Key });
});
}
}
)
.SelectedIndex(0)
.Render();
}
Controller:
public ActionResult TabStripSelected(string idZone)
{
return PartialView("_GridArticlesByZone",CurrentHvm.Articles.Where(e => e.Zone == idZone));
}
I would like to know if ther's another way to do that, or if I'm missing something.
Thank's!
I have found the problem! :)
Controller:
foreach (var inZone in zones)
{
//!! Missing!
**IGrouping<string, Article> zone = inZone;**
tabstrip.Add()
.Text(inZone.Key)
.Content(() =>
{
#Html.Action("TabStripSelected", Controllers.Valoration, new { id = **zone.Key** });
});
}
Thank's!

Persist CheckBox State in Telerik MVC Grid While Paging in ASP.NET MVC Application

I am using Telerik MVC Grid where one of the columns is checkboxes. If I select checkboxes and then go to page 2 and then come back to page 1 all the checkboxes are gone. Which is of course the way HTTP works. Now, I put all the selected checkboxes inside the hidden field but since the grid does some sort of postback my hidden field is cleared next time.
If you're using Client Side data binding you can use the javascript/jquery below to maintain checkbox state.
Maintain checkbox state:
var selectedIds = [];
$(document).ready(function () {
//wire up checkboxes.
$('#YOUR_GRID_ID :checkbox').live('change', function (e) {
var $check = $(this);
console.log($check);
if ($check.is(':checked')) {
//add id to selectedIds.
selectedIds.push($check.val());
}
else {
//remove id from selectedIds.
selectedIds = $.grep(selectedIds, function (item, index) {
return item != $check.val();
});
}
});
});
Restore checkbox state after data binding:
function onDataBound(e) {
//restore selected checkboxes.
$('#YOUR_GRID_ID :checkbox').each(function () {
//set checked based on if current checkbox's value is in selectedIds.
$(this).attr('checked', jQuery.inArray($(this).val(), selectedIds) > -1);
});
}
A more verbose explanation available on my blog:
http://blog.cdeutsch.com/2011/02/preserve-telerik-mvc-grid-checkboxes.html
You need to save the state of the checkboxes to your database, and then retrieve them again from the database when you reload the page.
During paging, you need to reload only those records that pertain to a particular page. You can do that using the Skip() and Take() methods from Linq.
to preserve checked /unchecked checkbox state using telerik grid clientemplate across postbacks and async postbacks and in refreshing grid and (auto)paging, I tried the solution above with no avail and so went up with a bit harder solution; as I could not save the state in db, I used a session variable and an hiddenfield:
first, a way to do ajax postback (see function DoAjaxPostAndMore , courtesy of somebody herearound), where in success we take care of client values of selections, adding and removing as checked /unchecked
I also had to manually check / uncheck the checkboxes inside the manual ajax post
second, an hidden field (see 'hidSelectedRefs') to preserve clientactions, as the Session variable I am using will not be seen clientside in partial rendering
#model IEnumerable<yourInterfaceOrClass>
#{
ViewBag.Title = "Select Something via checkboxes";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Select Something via checkboxes</h2>
<!-- we need a form with an id, action must be there but can be an empty string -->
<form id="frm" name ="frm" action="">
<p>
<!--we need this as Session new values will not be takein in aajax requests clientisde, so it is easier to mange this field, which, in first and subsequent complete postbacks can have the value of the Session variable -->
<input type="hidden" name="hidSelectedRefs" id="hidSelectedRefs" value= '#Session["SelectedReferencesToPrint"]' />
</p>
<br />
<script type="text/javascript">
//ajax manual post to a custom action of controller, passing the id of record and the state of the checkbox
//to adjust the Session value
//data: $form.serialize() will have the single checbox value
//but only if checked. To make my life eaasier, I added the value (is id ) and the checked/unchecked
//state of checkbox (is the $(chkClicked).attr('checked'))
function DoAjaxPostAndMore(chkClicked) {
var $form = $("#frm");
$.ajax({
type: "POST",
url: 'SelectReferences',
data: $form.serialize() + '&id=' + $(chkClicked).val() + '&checked=' + $(chkClicked).attr('checked'),
error: function (xhr, status, error) {
//do something about the error
alert("Sorry, we were not able to get your selection...");
},
success: function (response) {
//I also needed to check / uncheck manually the checkboxes:
$(chkClicked).attr('checked', !$(chkClicked).attr('checked'));
//and now put correct values in hidSelectedRefs hidden field:
if ($(chkClicked).attr('checked')) {
$('input[name=hidSelectedRefs]').val($('input[name=hidSelectedRefs]').val() + '|' + $(chkClicked).val() + '|');
} else {
var tmp = $('input[name=hidSelectedRefs]').val();
$('input[name=hidSelectedRefs]').val(tmp.toString().replace('|' + $(chkClicked).val() + '|', ''));
}
}
});
return false; // if it's a link to prevent post
}
Then I handled the OnRowDataBound, to ensure the checboxes would be correctly checked on postbacks,
function onRowDataBound(e) {
var itemsChecked = $('input[name=hidSelectedRefs]').val();
if (itemsChecked)
{
if (itemsChecked.indexOf('|' + $(e.row).find('input[name=checkedRecords]').val() + '|') >= 0)
{
$(e.row).find('input[name=checkedRecords]').attr('checked', true);
}
}
}
</script>
The telerik mvc Grid is as follows:
(you can see I also handled OnDataBinding and OnDataBound, but thats's only to show a
"Loading" gif. The controller is named "Output" and the action that normally would be called "Index" here is callled "PrintReferences". The correspondenting Ajax action is called "_PrintReferences")
Of interest here is the ClientTemplate for checkbox (cortuesy of someone else herearound, where onclick
we call our custom ajax action (named "SelectReferences") on our Output controller via a call to the
DoAjaxPostAndMore() javascript/jquery function
#(Html.Telerik().Grid<yourInterfaceOrClass>()
.Name("Grid")
.ClientEvents(e => e.OnDataBinding("showProgress").OnDataBound("hideProgress").OnRowDataBound("onRowDataBound"))
.DataBinding(dataBinding =>
{
dataBinding.Server().Select("PrintReferences", "Output", new { ajax = ViewData["ajax"]});
dataBinding.Ajax().Select("_PrintReferences", "Output").Enabled((bool)ViewData["ajax"]);
})
.Columns( columns =>
{
columns.Bound(o => o.ID);
columns.Bound(o => o.ID)
.ClientTemplate(
"<input type='checkbox' name='checkedRecords' value='<#= ID #>' onclick='return DoAjaxPostAndMore(this)' />"
)
.Width(30)
.Title("")
.HtmlAttributes(new { style = "text-align:center; padding: 0px; margin: 0px;" });
columns.Bound(o => o.TITLE);
columns.Bound(o => o.JOBCODE);
columns.Bound(o => o.ORDERCODE );
//columns.Bound(o => o.AUTHOR);
columns.Bound(o => o.STATE);
columns.Command(commands =>
commands
.Custom("Details")
.ButtonType(GridButtonType.Image)
.HtmlAttributes(new { #class = "t-icon-details" })
.DataRouteValues(route => route.Add(o => o.ID)
.RouteKey("ID"))
.Ajax(false)
.Action("Details", "Referenza")
);
})
.Pageable(paging =>
paging.PageSize(10)
.Style(GridPagerStyles.NextPreviousAndNumeric)
.Position(GridPagerPosition.Bottom))
.Sortable()
.Filterable()
.Resizable(resizing => resizing.Columns(true))
.Reorderable(reorder => reorder.Columns(true))
.NoRecordsTemplate("No Reference found. Please review your filters...")
.ColumnContextMenu()
)
</form>
that's all for the View. Now, to the controller:
//Get : this is the usally called "Index" action
//here we can load data, but we also must ensure the Session variable is fine
public ActionResult PrintReferences(bool? ajax, string msgInfo, string selectedRef)
{
if (Session["SelectedReferencesToPrint"] == null)
{
Session["SelectedReferencesToPrint"] = string.Empty;
}
if (string.IsNullOrEmpty(selectedRef))
{
selectedRef = "|0|";
}
string msgOut = string.Empty;
//this is where I get data to show
List<yourInterfaceOrClass> ret = LoadData(out msgOut);
if (!string.IsNullOrEmpty(msgInfo) && !string.IsNullOrEmpty(msgInfo.Trim()))
{
msgOut = msgInfo + ' ' + msgOut;
}
ViewBag.msgOut = msgOut;
ViewData["ajax"] = ajax ?? true;
return View(ret);
}
//GridAction: here is telerik grid Ajax get request for your "_Index"
[GridAction]
public ActionResult _PrintReferences(string msgInfo)
{
//again, we must take care of Session variable
if (Session["SelectedReferencesToPrint"] == null)
{
Session["SelectedReferencesToPrint"] = string.Empty;
}
string msgOut = string.Empty;
List<yourInterfaceOrClass> ret = LoadData(out msgOut);
return View(new GridModel(ret));
}
//Post: this is where our custom ajax post goes
//we are here if a checkbox is cheched or unchecked
//in the FormCollection parameter we get the checkbox value only if checked, and also
//(and always) the parms we passed (id of record and state of checkbox: we cannot simply add,
//we must also subtract unchecked)
[HttpPost]
public ActionResult SelectReferences(FormCollection collection)
{
//we need a session variable
if (Session["SelectedReferencesToPrint"] == null)
{
Session["SelectedReferencesToPrint"] = string.Empty;
}
//use a local variable for calculations
string wholeSelectionToPrint = Session["SelectedReferencesToPrint"].ToString();
//get value passed: id
string selectedRefId = collection["id"];
if (!string.IsNullOrEmpty(selectedRefId))
{
selectedRefId = "|" + selectedRefId + "|";
}
bool cheked = (collection["checked"].ToString()=="checked");
//get vcalue passed :checked or unchecked
if (cheked)
{
//the element is to add
wholeSelectionToPrint += selectedRefId;
}
else
{
//the element is to remove
wholeSelectionToPrint = wholeSelectionToPrint.Replace(selectedRefId, "");
}
//set session variable final value
Session["SelectedReferencesToPrint"] = wholeSelectionToPrint;
return null;
}
//normal postback:
//we will be here if we add a button type submit in our page,
//here we can collect all data from session variable to do
//something with selection
[HttpPost]
public ActionResult PrintReferences(FormCollection collection)
{
//get selected references id
if (Session["SelectedReferencesToPrint"] == null)
{
Session["SelectedReferencesToPrint"] = string.Empty;
}
//use a local variable for calculations
string wholeSelectionToPrint = Session["SelectedReferencesToPrint"].ToString();
wholeSelectionToPrint = wholeSelectionToPrint.Replace("||", "|");
string[] selectdIDs = wholeSelectionToPrint.Split(new char[] { '|' });
foreach (string id in selectdIDs)
{
if (!string.IsNullOrEmpty(id))
{
//do something with single selected record ID
System.Diagnostics.Debug.WriteLine(id);
}
}
//omitted [....]
ViewData["ajax"] = true;
return View(ret);
}

Resources