I am missing something important regarding this example grid
http://demos.telerik.com/kendo-ui/web/grid/editing-custom.html?mvc
I based my shopProductAdmin view/controller closely on this example, making use of the custom editor used in the example for CategoryViewModel, for my priceStrategyViewModel.
Other differences from the reference,
I am using InLine editing instead of InCell.
I am not using Batch mode.
The read function has additional data it sends to get the products.
The read/update/delete functions all have role requirement hints for security, but i tested without these security hints and the behavior is the same.
There is something not right about the data being passed around though. Some binding somewhere must be amiss and I can't seem to spot it.
Bad behaviors I see..
a) the default priceStrategy, when no record exists in the database, the PriceStrategyViewData.priceStrategyID/name are populated with the 99_none and the proper index. That part works.
b) when I edit a field, the customEditor comes up.. good
c) the value of the priceStrategy changes from 99_none to the first value in the priceStrategies list -- bad!. it should still be 99_none. This is the first indication that some data is not being passed around properly.
d) when I update the record on the grid, the update function in the controller see's the new product and all fields are properly updated with the notable exception of the PriceStrategyViewModel.* fields.
One piece of code in the example that I haven't gleaned why it is there.. in the example that I found on the installation package.. (this isn't shown on the web version, as the entities are not part of the sample code there), the entity not only has a
public CategoryViewModel Category { get; set; }
member.. which I understand that. But it also has this member right after it.. which seems to have no function anywhere in the grid. Was this intentional? I included it in my model also only to be consistent, but I haven't decided why it is there. It seems like it isn't serving a purpose.
public int? CategoryID { get; set; }
Anyway, here is my grid in the Admin.cshtml
@(Html.Kendo().Grid<EveTools.WebUI.Models.shopProductsAdminViewModel>()
.Name("productsGrid")
.Columns(columns =>
{
columns.Bound(product => product.typeName).Width(200).Title("Shop");
columns.Bound(product => product.forSale).Title("ForSale?");
columns.Bound(product => product.PriceStrategy).ClientTemplate("#=PriceStrategy.name#").Width(150);
columns.Bound(product => product.markup).Title("markup").Width(50);
columns.Bound(product => product.price).Title("Price");
columns.Command(command => { command.Edit(); command.Destroy().Text("Clear"); }).Width(100);
})
.DataSource(dataBinding => dataBinding
.Ajax()
.ServerOperation(false)
.Model(model => model.Id(p => p.typeID))
.Read(read => read.Action("ProductListAdmin", "Shop").Data("additionalProductListData"))
.Destroy(update => update.Action("EditingInline_DeleteProduct", "Shop"))
.Update(update => update.Action("EditingInline_UpdateProduct", "Shop"))
.Events(events => events.Error("error_handler"))
.Model(model =>
{
model.Id(p => p.typeID);
model.Field(p => p.typeName).Editable(false);
model.Field(p => p.price).Editable(false);
model.Field(p => p.PriceStrategy).DefaultValue(
ViewData["defaultPriceStrategy"] as EveTools.WebUI.Models.PriceStrategyViewModel);
})
)
.Editable(editable => editable.Mode(GridEditMode.InLine))
.Pageable()
.Sortable()
.Scrollable()
.HtmlAttributes(new { style = "height:480px;" })
And the critical controller functions.
This function returns the data to the grid when requested.. by means of another list control of categories.
public ActionResult ProductListAdmin([Kendo.Mvc.UI.DataSourceRequest]Kendo.Mvc.UI.DataSourceRequest request, int? categoryID)
{
// if there is no category, there are also no products
if (!categoryID.HasValue)
return null;
EveShop.Domain.Concrete.EFEveShopDbContext db = new EveShop.Domain.Concrete.EFEveShopDbContext();
PriceStrategyViewModel defaultPriceStrategy = GetDefaultPriceStrategy();
// extract data from db, and fill in shopProductsAdminViewModels
var products = (from t in db.invTypes
join p in db.shopProducts
on t.invTypeID equals p.shopProductID into output
from o in output.DefaultIfEmpty()
select new shopProductsAdminViewModel
{
typeID = t.invTypeID,
groupID = t.groupID,
typeName = t.typeName,
volume = t.volume,
basePrice = t.basePrice,
published = t.published,
marketGroupID = t.marketGroupID,
iconID = t.iconID,
markup = (t.shopProduct == null) ? 0 : t.shopProduct.markup,
forSale = (t.shopProduct == null) ? false : t.shopProduct.forSale,
price = (t.shopProduct == null) ? 0 : t.shopProduct.shopProductPrice.price,
// not sure why i am filling this one in.. it was included in the original example though.. no binding to the grid?
priceStrategyID = (t.shopProduct == null) ? defaultPriceStrategy.priceStrategyID : t.shopProduct.shopPriceStrategy.shopPriceStrategyID,
// the actual price strategy data.. this one gets bound to the grid
PriceStrategy = new PriceStrategyViewModel
{
priceStrategyID = (t.shopProduct == null) ? defaultPriceStrategy.priceStrategyID : t.shopProduct.shopPriceStrategy.shopPriceStrategyID,
name = (t.shopProduct == null) ? defaultPriceStrategy.name : t.shopProduct.shopPriceStrategy.name
}
}).Where(e => e.marketGroupID == categoryID && e.published != false)
.OrderBy(e => e.typeName)
return Json(products.ToDataSourceResult(request));
}
This function receives the update record callback
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditingInline_UpdateProduct([DataSourceRequest] DataSourceRequest request, shopProductsAdminViewModel product)
{
// do something with updated product
// product.priceStrategy.* is always the default value, not the edited value.
return Json(new[] { product }.ToDataSourceResult(request, ModelState));
}
The PriceStrategyViewModlEditor.cshtml
@model EveTools.WebUI.Models.PriceStrategyViewModel
@(Html.Kendo().DropDownListFor(m => m)
.Name("PriceStrategyViewModel")
.DataValueField("priceStrategyID")
.DataTextField("name")
.BindTo((System.Collections.IEnumerable)ViewData["priceStrategies"])
)