1
votes

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: enter image description here

enter image description here

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?

1
HTTP Status Code 401 is related to the website configuration on your IIS server. A 401 is returned by the web server, not your code. - whipdancer
Additionally, if you don't specify the ajax type, I believe that the Kendo Datasource will default to GET. - whipdancer
For the datasource update, the ajax type is set as POST. - Keith Barrows
Is the correct URL being called - ProductReuse/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? - whipdancer
That is the main page's controller and action. When I submit the form (via a submit button) it works as expected. The call that is failing is the ProductReuse/AjaxProductReuse/UpdateBomItem call when I submit the popup form. - Keith Barrows

1 Answers

0
votes

I ended up changing the id binding on the datasource:

.Model(model =>
{
    model.Id(g => g.Row);
    model.Field(m => m.Row).Editable(false);
})

Changed to:

.Model(model => 
{ 
    model.Id(g => g.BomId); 
})

This fixed it. While Row is a unique int on the collection, BomId is the defined PK in the DB. Why this should make a difference I don't know. However, it does even though Kendo (and the supporting model) do not know anything about PKs as it is a POCO with no entity information.

If any of you might know why this would affect it I would be very interested in the explanation!