Update Kendo grid with editor dropdownlist value - asp.net-mvc

I have a Kendo grid set up like so:
#(Html.Kendo().Grid<ParticipatingDentalEE>()
.Name("DentalEE")
.Columns(columns =>
{
columns.Bound(p => p.State).Title("State").Width(150).EditorTemplateName("State");
columns.Bound(p => p.Count).Title("Count").Width(150);
columns.Command(c => { c.Edit(); c.Destroy(); });
})
.DataSource(dataSource => dataSource
.Ajax()
.Model(m => {
m.Id(p => p.State);
m.Field(p => p.State).Editable(true);
m.Field(p => p.Count).Editable(true).DefaultValue("");
})
.Create(update => update.Action("EditingInline_Create", "Dental"))
.Read(read => read.Action("EditingInline_Read", "Dental"))
.Update(update => update.Action("EditingInline_Update", "Dental"))
.Destroy(update => update.Action("EditingInline_Destroy", "Dental"))
)
//.Scrollable()
//.Sortable()
.Editable(e => e.Mode(GridEditMode.InLine))
)
The "State" column consists of a dropdown template that looks like this:
#(Html.Kendo().DropDownList()
.Name("States") // Name of the widget should be the same as the name of the property
.DataValueField("CODE") // The value of the dropdown is taken from the EmployeeID property
.DataTextField("NAME") // The text of the items is taken from the EmployeeName property
.BindTo((System.Collections.IEnumerable)ViewData["States"]) // A list of all employees which is populated in the controller
)
My dropdown shows up properly when I edit or create an item, but when I save the item the dropdown value does not stay in the grid. Is there something else I need to set up in order to do this?

as you say in your own comment,
.Name("States") // Name of the widget should be the same as the name of the property
which is to say, it must match the name of the column, and the column name is "State" not "States".

Obviously this is an old thread, however the fix is to use the DropDownListFor method (as opposed to the DropDownList) and not to specify a name. I suspect Kendo does some internal name matching to apply the edited value back to the model.
#model int // ...or whatever type works for your model
#(Html.Kendo().DropDownListFor(i => i)
.DataValueField("CODE")
.DataTextField("NAME")
.BindTo((System.Collections.IEnumerable)ViewData["States"]))

I'm not sure if this will fix your problem, but the editor templates for my grid didn't work correctly until I had set the UIHint decorator in the model, and the EditorTemplateName in the view.
E.g.
public class ParticipatingDentalEE {
...
[UIHint("State")] // this should be the name of your EditorTemplate
public State State { get; set; }
}
I speculate that UIHint is used for the grid in 'view' mode, while the EditorTemplateName is used while in 'edit' mode, and both are required to link the two together.

Related

How to open a modal using Kendo UI grid that has a list inside it

I am using Kendo UI in my asp.net mvc project.
I have an issue for displaying modal. In my grid, I want to add an extra column that has a button in it.
When the user clicks that button, I need to show a list from another table that has the ID of the current table, and show it in modal.
I would really appreciate if you could help me.
#(Html.Kendo().Grid<ClinicManagement.Models.Reagent>().Name("PersonGrid")
.Name("PersonGrid")
.Columns(columns =>
{
columns.Bound(p => p.Name).Filterable(ftb => ftb.Cell(cell => cell.Operator("contains").SuggestionOperator(FilterType.Contains))).Width(90);
columns.Bound(p => p.Family).Filterable(ftb => ftb.Cell(cell => cell.Operator("contains").SuggestionOperator(FilterType.Contains))).Width(90);
columns.Bound(p => p.CardNumber).Filterable(ftb => ftb.Cell(cell => cell.Operator("contains").SuggestionOperator(FilterType.Contains))).Width(90);
columns.Command(command => command.Custom("ViewDetails").Click("showDetails")).Width(150);
})
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(20)
.Events(events => events.Error("error_handler"))
.Sort(sort => sort.Add(p => p.Name).Ascending())
.Model(model => model.Id(p => p.Id))
.Create(update => update.Action("Create", "Reagents"))
.Read(read => read.Action("ReadReagent", "Reagents"))
.Update(update => update.Action("Edit", "Reagents"))
.Destroy(destroy => destroy.Action("Delete", "Reagents"))
))
OK, your code looks similar to the demo here. You just need to finish it up:
First, create a modal on the page:
#(Html.Kendo().Window().Name("Details")
.Title("Customer Details")
.Visible(false)
.Modal(true)
.Draggable(true)
.Width(300)
)
The sample uses a kendo template to display the details, so you could add a kendo list in there (you would need to use .ToHtmlString() since it is a nested control). I prefer a different approach where I can use a partial view with a view model:
Create a view model with the list items and other properties to display:
public class DetailsViewModel
{
public int PersonId { get; set; }
public string Name { get; set; }
... etc
public List<string> MyListItems;
}
Create a partial view for the details with a list (or grid) on it:
#model DetailsViewModel
<div>
... // Show fields, etc.
#(Html.Kendo().ListBox()
... other list options
.BindTo(Model.MyListItems)
.Deferred() // Need for nested control
</div>
Create a controller action to return the partial:
public PartialViewResult GetDetailsView(int personId)
{
// fetch data
// Fill the viewmodel
var vm = new DetailsViewModel
{
PersonId = data.PersonId,
Name = data.Name,
MyListItems = context.Items.Where(i => i.PersonId == personId).ToList()
}
return PartialView("_Details", vm);
}
The javascript code for the button click will open the window and tell it to load the partial view from controller action:
<script type="text/javascript">
function showDetails(personId) {
var wnd = $("#Details").data("kendoWindow");
wnd.refresh({
url: '#Url.Action("GetDetailsView","Person", new { personId = "__personid__" })'
.replace("__personid__", personId )
});
wnd.open();
}
</script>
Finally, change the custom command to pass in the Id:
.Click("showDetails(PersonId)")
EDIT - or use a template for your button:
columns.Template(t => { }).Width(150)
.ClientTemplate(#"<a class='btn btn-info btn-xs' onclick="showDetails(PersonId)">Details</a>");

Model Binding fails with Kendo Grid

I can't use edit and create command of a Kendo Grid, because binding always fails and controller always receives a null object, even though DataSourceRequest is fine.
I used a #html.EditorForModel() and it worked fine and controller received the data. So MVC Model binding and my class aren't the problem.
Also, using F12, I can see the data being posted and it's fine too.
Any Idea about possible problem and debugging? There is an issue with all of my Kendo Grids, sometimes they post a model twice and I receive double Create on the server, which one of them always fails because of repetitive Keys. Or sometimes create will be triggered by edit command too.
I have no idea where I'm going wrong, in most scenarios the user will choose the primary key as well, so my returned model doesn't need an update.
With this particular class, server always receives two create and none of them binds correctly. The first create, posts all of the fields, and the second posts all of the navigation properties' fields too!
This is the action method:
[HttpPost]
public ActionResult Create([DataSourceRequest] DataSourceRequest request, Stock stock){
if (stock != null && ModelState.IsValid)
{
repo.Insert(stock);
return Json(new[] { stock }.ToDataSourceResult(request, ModelState));
}
else
return Json(null);
}
And the Grid:
#(Html.Kendo().Grid<Stock>()
.Name("stock")
.Columns(columns =>
{
columns.ForeignKey(x => x.CGoodCode, (IEnumerable)ViewData["Goods"], "Id", "Text");
///
columns.Command(command => { command.Edit(); command.Destroy(); });
})
.ToolBar(toolbar => {
toolbar.Create();
toolbar.Excel();
toolbar.Pdf();
})
.Editable(edit => edit.Mode(GridEditMode.PopUp))
.Sortable()
.Pageable(p => p.PageSizes(true))
.DataSource(ds =>
ds.Ajax()
.Model(model => {
model.Id(x => x.CGoodCode);
model.Id(x => x.SWhCode);
model.Id(x => x.LiIdPeriod);
model.Field(x => x.CGoodCode).DefaultValue(ViewData["GoodsDefault"]);
////
})
.Read("Read", "Stock")
.Update("Edit", "Stock")
.Create("Create", "Stock")
.Destroy("Delete", "Stock")
)
)
UPDATE
It seems Kendo Grid doesn't support composite primary keys yet!
This question helped me out! I just need to rename the parameter like:
public ActionResult Create([DataSourceRequest] DataSourceRequest request, Stock somethingElse){
Because my class contains some fields with that name, Stock!

Kendo UI for ASP.Net MVC. Grid sorting error with additional entity property

I have a sorting problem with kendo ui grid.
I have a Entity class Port, in partial class I added additional property:
public partial class Port {
[NotMapped]
[XmlIgnore]
[ScriptIgnore]
public string CountryName { get; set; }
}
I am using this property in other program parts.
In controller I have:
public ActionResult Index()
{
if (!CanViewPorts())
return new EmptyResult();
ViewBag.CanEditPorts = CanEditPorts();
return View();
}
public ActionResult Ports([DataSourceRequest] DataSourceRequest request)
{
if (!CanViewPorts())
return new EmptyResult();
var all = _classificatoryService.GetAllPorts();
return Json(all.ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}
I'am using method Ports in my view to populate kendo ui grid.
My view:
#(Html.Kendo().Grid<Port>()
.Name("Ports")
.ToolBar(toolbar => toolbar.Template(Toolbar().ToString()))
.Columns(columns =>
{
columns.Bound(c => c.PortId);
columns.Bound(c => c.Code);
columns.Bound(c => c.Name);
columns.Bound(c => c.AlternativeName);
columns.Bound(c => c.SubDivision);
columns.Template(#<text></text>).ClientTemplate("<a class='k-button k-button-icontext' href='" + Url.Action("Details", "Port") + "/#=PortId#'><span class='glyphicon glyphicon-edit mr5'></span>Details</a>").Width(110); //"<#=PortId#>"
columns.Template(#<text></text>).ClientTemplate("<a class='newPortBtn k-button k-button-icontext' href='" + Url.Action("NewPort", "Port") + "/?portId=#=PortId#'><span class='glyphicon glyphicon-edit mr5'></span>Edit</a>").Visible(canEditPorts).Width(110);
})
.Sortable()
.ColumnMenu()
.Filterable()
.Pageable(pageable => pageable
.Refresh(true)
.PageSizes(true)
.ButtonCount(5))
.DataSource(dataSource => dataSource
.Ajax()
.Model(model => model.Id(x => x.PortId))
.Read(read => read.Action("Ports", "Port"))
.Sort(sort => sort.Add(x => x.ChartererId).Descending())
.Create(c => c.Action("NewPort", "Port"))
))
As you can see I'a not using additional property Countryname in this view.
Ok, grid gets ports and show it properly.
Then I'am trying to sort and click column header.
First click (ASC) - Ok, grid sorted by this column in ascending order.
Second click (DESC) - Ok, grid sorted by this column in descending order.
Third click (unsort) - Grid must be unsorted, but I have 500 (Internal Server Error).
In chrome/Network/Preview I have:
Server Error in '/' Application. The specified type member 'CountryName' is not supported in LINQ to Entities. Only initializers,
entity members, and entity navigation properties are supported.
How to fix this problem?

Pass Model to column ClientTemplate editor in Kendo Grid

I have a Kendo grid whereas the columns are defined as:
.Columns(columns =>
{
columns.Bound(b => b.Field);
columns.Bound(b => b.OldValue);
columns.Bound(b => b.NewValue);
columns.Bound(b => b.DateImported).Format("{0:dd-MMM-yyyy}");
columns.Bound(b => b.BuildingChangeValidationStatusType).ClientTemplate("#=BuildingChangeValidationStatusType.Value#").Width(250);
columns.Command(command => command.Custom("Update").Click("updateValidation"));
columns.Command(command => { command.Edit(); }).Width(172);
})
The BuildingChangeValidationStatusType client template is defined as:
#model Rep.Models.BuildingChangeValidationViewModel
#(Html.Kendo().DropDownList()
.Name("BuildingChangeValidationStatusType") // Name of the widget should be the same as the name of the property
.DataValueField("Id")
.DataTextField("Value")
.BindTo((System.Collections.IEnumerable)Model.BuildingChangeValidationStatuses)
)
I'm wondering how I might pass the model for the Grid to the client template so that the line:
.BindTo((System.Collections.IEnumerable)Model.BuildingChangeValidationStatuses)
)
would resolve properly. Any ideas?
I solved it a different way by passing the data I needed to the DropdownList in the client template via a javascript function. So the client template containing the dropdown is as so:
#(Html.Kendo().DropDownList()
.Name("BuildingChangeValidationStatusType") // Name of the widget should be the same as the name of the property
.DataValueField("Id")
.DataTextField("Value")
//.BindTo((System.Collections.IEnumerable)Model.BuildingChangeValidationStatuses)
.DataSource(
source => source.Read(read =>
read.Action("BuildingValidationLookups_Read", "Plan").Data("getBuildingId")
)
.ServerFiltering(true)
)
.SelectedIndex(0)
)
Notice the data source read action, it invokes the method on my "Plan" controller named: "BuildingValidationLookups_Read", but also passes the data retrieved from the "getBuildingId" javascript function which is defined as:
function getBuildingId() {
var entityGrid = $("#BuildingValidationGrid").data("kendoGrid");
var selected = entityGrid.dataItem(entityGrid.select());
return {
buildingId: selected.BuildingId
};
}
My controller method is defined as:
public JsonResult BuildingValidationLookups_Read([DataSourceRequest] DataSourceRequest request, int buildingId)
{
return Json(PopulateBuildingChangeValidationTypes(buildingId), JsonRequestBehavior.AllowGet);
}
All is well now.

What "EditorViewData" is for in Telerik MVC Grid column definition?

i have a telerik grid as follow:
Html.Telerik().Grid<MatchViewModel>().Name("Matches").Columns(cols =>
{
cols.Bound(e => e.Name);
cols.Bound(e => e.Date);
cols.Bound(e => e.GuestTeamId);
cols.Bound(e => e.HostTeamId);
cols.Bound(e => e.PostponedDate);
==> cols.Bound(e => e.RefereeId).EditorViewData(new { RefereeName = '' });
cols.Bound(e => e.StatusId);
})
in the column reffered by arrow i wanna send referee name as additional data for EditorTemplate.i inferred from the EditorViewData method name that it can help me doing this.but i can't get it working.can anyone help me with this?
thanks.
If you have a well defined Model for your page you should not ever need to use ViewBag or ViewData, they are sloppy. EditorViewData allows you to create ViewData on-the-fly to pass extra data to your EditorTemplate.
For example, say you want to have different DropDownList values in the EditorTemplate for each item in your grid, you will need to pass extra data to do this. With EditorViewData you can add additional values from your Model for exactly that purpose without having to resort to coding up any ViewBag or ViewData objects in your Controller.
The first place I used it was a People grid that allowed editing of defined Qualifications added to a Qualifications grid inside a nested TabStrip. The trick was I didn't want the DropDownList for each person to contain any of the qualifications they already had earned. Like this...
People Grid
#using Kendo.Mvc.UI
#model PeopleViewModel
#(Html.Kendo().Grid<PersonModel>()
.Name("PersonGrid")
.Columns(columns => {
columns.Bound(b => b.LastName).EditorTemplateName("_TextBox50");
columns.Bound(b => b.FirstName).EditorTemplateName("_TextBox50");
...
columns.Command(cmd => { cmd.Edit(); cmd.Destroy(); }).Width(180);
})
.ClientDetailTemplateId("personTemplate")
.ToolBar(toolbar => toolbar.Create())
.Selectable()
.Editable(editable => editable.Mode(GridEditMode.InLine))
.DataSource(dataSource => dataSource
.Ajax()
.Model(model =>
{
model.Id(a => a.Id);
})
.Create(create => create.Action("CreatePerson", "People"))
.Read(read => read.Action("ReadPeople", "People"))
.Update(update => update.Action("UpdatePerson", "People"))
.Destroy(destroy => destroy.Action("DestroyPerson", "People"))
)
.Events(events => events.DataBound("dataBound"))
)
<script type="text/javascript">
function dataBound() {
this.expandRow(this.tbody.find("tr.k-master-row").first());
}
</script>
<script id="personTemplate" type="text/kendo-tmpl">
#(Html.Kendo().TabStrip()
.Name("TabStrip_#=Id#")
.Items(items =>
{
...
items.Add().Text("Edit Qualifications")
.LoadContentFrom("PersonQualifications", "People", new {personId = "#=Id#"});
...
})
.ToClientTemplate()
)
</script>
PeopleViewModel
Ignore the inheritance stuff, it is beyond this discussion. But note that I use this same Model on all the sub Views related to this top level View.
public class PeopleViewModel : PageViewModel
{
public int PersonId { get; set; }
public PersonModel Person { get; set; }
public IList<QualificationModel> AllQualifications { get; set; }
...
public PeopleViewModel(BaseViewModel baseViewModel) : base(baseViewModel)
{}
}
PersonQualifications Controller
The data providers are injected elsewhere, but note the POCO to Model flattening - just a static method that applies a List to the Model constructor.
public ActionResult PersonQualifications(int personId)
{
SetBaseContext(HttpContext);
var model = new PeopleViewModel(BaseViewModel)
{
PersonId = personId,
AllQualifications = QualificationModel.FlattenToThis(_qualificationDataProvider.Read())
};
return View(model);
}
Nested Grid (View Loaded Inside TabStrip)
#using Kendo.Mvc.UI
#model PeopleViewModel
#{
Layout = null;
}
#(Html.Kendo().Grid<PersonQualificationModel>()
.Name("QualificationEditGrid_" + Model.PersonId)
.Columns(columns =>
{
columns.ForeignKey(f => f.QualificationId, Model.AllQualifications, "Id", "Display")
===> .EditorViewData(new {personId = Model.PersonId})
.EditorTemplateName("PersonQualificationDropDownList");
columns.Command(cmd =>
{
cmd.Edit();
cmd.Destroy();
}).Width(180);
})
.ToolBar(toolbar => toolbar.Create())
.DataSource(dataSource => dataSource
.Ajax()
.Events(events => events.Error("error_handler"))
.Model(model => {
model.Id(a => a.Id);
})
.Create(create => create.Action("CreatePersonQualification", "People"))
.Read(read => read.Action("ReadPersonQualifications", "People", new {personId = Model.PersonId}))
.Destroy(destroy => destroy.Action("DestroyPersonQualification", "People"))
)
)
The EditorTemplate (Finally!)
Ignore the first ViewData reference that helps make this EditorTemplate shared. The one we are interested in is a little further down.
#using Kendo.Mvc.UI
#(Html.Kendo().DropDownList()
.Name(ViewData.TemplateInfo.GetFullHtmlFieldName(""))
.DataValueField("Id")
.DataTextField("Name")
.OptionLabel("Select...")
.DataSource(dataSource => dataSource
===> .Read(read => read.Action("ReadDdlQualifications", "People", new {personId = ViewData["personId"]}))
)
)
Controller Method (Just to be Thorough)
public JsonResult ReadDdlQualifications(int personId)
{
var qualification = _qualificationDataProvider.ReadAvailableToPerson(personId);
IList<IdNamePair> results = IdNamePair.FlattenToThis(qualification);
return Json(results, JsonRequestBehavior.AllowGet);
}
Obviously there is a lot of other stuff going on in this example (I hope I left enough code in for it to make sense), but it should get across the point of when it is needed - and it is REALLY needed.
Enjoy.
I ran into the same issue as Chad and as Trey mentioned, this cannot be done via passing information to EditorViewData. There is no way to pass the row data item, only page data.
As an alternative you can add this script to the editor template. Then you can access a field value from the grid row and pass it to the datasource call, filtering the dropdownlist based on each rows data.
<script type="text/javascript">
function getParentId() {
var row = $(event.srcElement).closest("tr");
var grid = $(event.srcElement).closest("[data-role=grid]").data("kendoGrid");
var dataItem = grid.dataItem(row);
return { EmployeeId: dataItem.EmployeeId };
}
</script>
And then add the data item to the read method on the data source.
#(Html.Kendo().DropDownList()
.Name("Product")
.DataValueField("ProductId")
.DataTextField("ProductName")
.DataSource(ds => ds
.Read(read => read.Action("ProductsRead", "Home")
.Data("getParentId")
))
)

Resources