First: I have found several questions for this and so far none of the answers are working for me. In fact, I've not yet found an accepted answer!
I have a MVC 4 app, a cshtml page and a couple of layers of partial views under it.
Main View (kendo tabs, no form element, drilling down on tab #1):
<div class="container-fluid" style="margin-left: 10px; margin-right: 10px; padding-right: 30px;">
@(Html.Kendo().TabStrip()
.Name("tabStrip")
.Events(e => e.Select("tabstrip_select"))
.Items(items =>
{
items.Add()
.Text("Clone Assembly")
.Selected(true)
.Content(@<text>@Html.Partial("_Assembly", Model)</text>);
items.Add()
.Text("Submit")
.Visible(@Model.Status == (int)SessionStatuses.Started)
.HtmlAttributes(new { userWorkSessionId = Model.UserWorkSessionId })
.LoadContentFrom("_AssemblyStarted", "ProductReuse", new { userWorkSessionId = Model.UserWorkSessionId });
items.Add()
.Text("Session Results")
.Visible(@Model.Status >= (int)SessionStatuses.Submitted)
.HtmlAttributes(new { userWorkSessionId = Model.UserWorkSessionId })
.LoadContentFrom("_AssemblySubmitted", "ProductReuse", new { userWorkSessionId = Model.UserWorkSessionId });
})
)
</div>
_assembly Partial View (kendo grid):
@using (Html.BeginForm("Assembly", "ProductReuse", FormMethod.Post, new {id = "frmStartScreen"}))
{
@Html.HiddenFor(m => m.Status)
@Html.HiddenFor(m => m.UserWorkSessionId)
<div class="row">
<div class="panel panel-info">
<div class="panel-heading">
<div class="panel-heading text-center">
<h2 class="pr">@string.Format("{0} | {1} | {2}", Model.GlobalPartNum, Model.CloneScopeDisplay, Model.StatusDisplay)</h2>
</div>
</div>
<div class="panel-body" style="margin-bottom: 10px; margin-top: 10px;">
<div class="row">
@(Html.Kendo().Grid<BomViewModel>()
.Name("bom-prGrid-kendoGrid")
.HtmlAttributes(new {@class = "prGrid"})
.ClientRowTemplate("")
.Columns(columns =>
{
if (Model.StatusDisplay.Equals("Started"))
{
columns.Command(cmd => cmd.Edit()).Width(80);
}
columns.Bound(g => g.BomId).Hidden();
columns.Bound(g => g.IsEditable).Hidden();
columns.Bound(g => g.Row).Width(75).Title("Row");
columns.Bound(g => g.PartNum).Width(125).Title("DWG/<br/>Part No");
columns.Bound(g => g.Qty).Width(80).HtmlAttributes(new {style = "text-align:center"}).Title("Qty");
columns.Bound(g => g.ItemDesc).Width(350).Title("Description");
})
.DataSource(dataSource => dataSource
.Ajax()
.Model(model =>
{
model.Id(g => g.Row);
model.Field(m => m.Row).Editable(false);
})
.PageSize(100)
.Read(r => r.Action("GetCloneAssembly", "AjaxProductReuse").Data("ProductReuseGridReadData"))
.Update(u => u.Action("UpdateBomItem", "AjaxProductReuse").Type(HttpVerbs.Post))
.Events(e => e.Error("ajax_error").Sync("dataSource_sync").Change("dataSource_change"))
)
.Events(e => e.DataBound("onDataBound").Edit("onEdit"))
.Pageable(pager => pager
.Input(true)
.Numeric(true)
.Info(true)
.PreviousNext(true)
.Refresh(true)
.PageSizes(new int[] {100, 250, 500, 1000})
)
.ToolBar(toolbar =>
{
toolbar.Template(
@<text>
<div class="container-fluid otherElements" style="padding-top: 5px; padding-bottom: 5px;">
@Html.Partial("_CloneAssembly", Model)
</div>
</text>);
})
.Excel(excel => excel.FileName("ClonableBom.xlsx").Filterable(true).AllPages(true).ProxyURL(Url.Action("ExcelProxy", "AjaxProductReuse")))
.Sortable()
.Scrollable()
.Filterable()
//.Editable(editable => editable.Mode(GridEditMode.PopUp).TemplateName("BOMForm").Window(w => w.Title("Manage BOM Item").Name("BOMForm")))
.Editable(editable => editable.Mode(GridEditMode.PopUp).TemplateName("BOMForm"))
.Resizable(resizing => resizing.Columns(true)).Reorderable(reorder => reorder.Columns(true))
)
</div>
</div>
</div>
</div>
}
Controller (get/post for main page):
[HttpGet]
public ActionResult Assembly(int userWorkSessionId = int.MinValue)
{
var response = GetCachedCurrentWorkSession(userWorkSessionId);
return View(response);
}
[HttpPost]
public ActionResult Assembly(AssemblyViewModel command)
{
var saveServiceCommand = _mapper.Map<WorkSessionModel>(command);
var serviceResponse = _productReuseService.ApplyGlobalSettingsCommand(saveServiceCommand);
var response = SaveCachedCurrentWorkSession(serviceResponse);
return View(response);
}
Ajax Controller (handles datasource read & update):
[HttpPost]
public JsonResult UpdateBomItem([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]BomViewModel bomViewModel)
{
var command = _mapper.Map<BomModel>(bomViewModel);
var commandResponse = _productReuseService.UpdateBomItem(command);
var response = _mapper.Map<List<BomViewModel>>(commandResponse);
return Json(ToDataSourceResult(response, request, modelState: ModelState));
}
Everything looks right as compared to several demos on Telerik's site. However, I am getting weird behavior when I submit the popup form for the update:
In both instances, the Ajax call never broke in my code (i.e.:, no evidence that it actually entered the method!) When I do break back in the Assembly POST method, the model is empty, initialized but empty.
What it looks like it is doing in the 2nd instance is sending every row in the grid to the main action method instead of the ajax handler. And, to top it off, the 401 error lines in the trace look like they are hitting the GET instead of the POST and being rejected as the GET expects just an INT.
We've had several of us look at this now and none of us have a clue as to what might be creating this behavior.
Any thoughts?
GET
. - whipdancerProductReuse/ProductReuse/Assembly?userWorkSession=45
? That's your controller method. What happens if you attempt to call the controller using a tool like Postman or Fiddler? - whipdancerProductReuse/AjaxProductReuse/UpdateBomItem
call when I submit the popup form. - Keith Barrows