JqGridResponse.JsonReader.RepeatItems for SubGrid

Sep 17, 2013 at 8:22 PM
Hi Tomasz,

Setting JqGridResponse.JsonReader.RepeatItems = false works fine for a grid (no shift left of columns), but it looks that it has no effect for a subgrid. Is there a workaround, or did I miss something?

Thanks in advance for any help,
Gerard
Coordinator
Sep 17, 2013 at 9:07 PM
Hi Gerard,

It depends to which scenario you are referring.

In case of "Subgrid as Grid" it will work as expected when you set it globally (there is a static option which you can set for example in Global.asax) or in subgrid options object (and the proper action response).

For the standard Subgrid scenario there is a separate set of options which jqGrid is using: JqGridResponse.JsonReader.SubgridReader (you will find RepeatItems there as well).

Regards,
Tomasz Pęczek
Sep 17, 2013 at 9:35 PM
The following has no effect (still a left shift)
        JqGridResponse response = new JqGridResponse(true);

        response.Reader.RepeatItems = false;

        response.Records.AddRange(from obj
                                  select new JqGridRecord<SomeModel>(null, new SomeModel(obj)));

        return new JqGridJsonResult() { Data = response };
JqGridResponse.JsonReader.RepeatItems = false in stead of response.Reader.RepeatItems = false has no effect either

Setting response.Reader.RepeatItems = false in Global.asax clears the whole SubGrid!
Sep 18, 2013 at 7:32 AM
Ps: the scenario is SubGrid as Grid
Coordinator
Sep 18, 2013 at 9:17 PM
Hi Gerard,

Lets go through both configuration approaches one by one:

Global approach

This is the approach which the demo project is using by default, if you look in the Global.asax you will find this:
protected void Application_Start()
 {
    ...
    JqGridResponse.JsonReader.RepeatItems = false;
    JqGridResponse.JsonReader.RecordId = "Id";
    JqGridResponse.JsonReader.SubgridReader.RepeatItems = false;
}
So if one wants to extend Subgrid as Grid sample with action columns the helper intialization should look something like this:
var grid = new Lib.Web.Mvc.JQuery.JqGrid.JqGridHelper<jqGrid.Models.OrderViewModel>("orders",
    dataType: Lib.Web.Mvc.JQuery.JqGrid.JqGridDataTypes.Json,
    methodType: Lib.Web.Mvc.JQuery.JqGrid.JqGridMethodTypes.Post,
    pager: true,
    rowsNumber: 10,
    sortingName: "Id",
    sortingOrder: Lib.Web.Mvc.JQuery.JqGrid.JqGridSortingOrders.Asc,
    subgridEnabled: true,
    subgridHelper: new Lib.Web.Mvc.JQuery.JqGrid.JqGridHelper<jqGrid.Models.OrderDetailViewModel>("orderDetails",
        dataType: Lib.Web.Mvc.JQuery.JqGrid.JqGridDataTypes.Json,
        methodType: Lib.Web.Mvc.JQuery.JqGrid.JqGridMethodTypes.Post,
        pager: true,
        rowsNumber: 10,
        sortingName: "Product",
        sortingOrder: Lib.Web.Mvc.JQuery.JqGrid.JqGridSortingOrders.Asc,
        url: Url.Action("OrderDetailsSubgridAsGrid"),
        viewRecords: true
    ).AddActionsColumn("actions", ...)
    .Navigator(new Lib.Web.Mvc.JQuery.JqGrid.JqGridNavigatorOptions() { Add = false, Delete = false, Edit = false, View = false, Search = false }),
    url: Url.Action("Orders"),
    viewRecords: true
).AddActionsColumn("actions", ...);
This gives the desired result:

Subgrid as grid with ActionColumn

I can't confirm that using global setting leads to clearing the whole Subgrid - everything works as expected.

Local approach

If you don't want to configure this setting globaly you can set it for specific grid. This requires changing the setting on both: the options for the helper and the response of proper action. Coming back to the demo project, the helper initialization would have to look like this:
var grid = new Lib.Web.Mvc.JQuery.JqGrid.JqGridHelper<jqGrid.Models.OrderViewModel>("orders",
    dataType: Lib.Web.Mvc.JQuery.JqGrid.JqGridDataTypes.Json,
    methodType: Lib.Web.Mvc.JQuery.JqGrid.JqGridMethodTypes.Post,
    pager: true,
    rowsNumber: 10,
    sortingName: "Id",
    sortingOrder: Lib.Web.Mvc.JQuery.JqGrid.JqGridSortingOrders.Asc,
    subgridEnabled: true,
    subgridHelper: new Lib.Web.Mvc.JQuery.JqGrid.JqGridHelper<jqGrid.Models.OrderDetailViewModel>("orderDetails",
        dataType: Lib.Web.Mvc.JQuery.JqGrid.JqGridDataTypes.Json,
        methodType: Lib.Web.Mvc.JQuery.JqGrid.JqGridMethodTypes.Post,
        pager: true,
        rowsNumber: 10,
        sortingName: "Product",
        sortingOrder: Lib.Web.Mvc.JQuery.JqGrid.JqGridSortingOrders.Asc,
        url: Url.Action("OrderDetailsSubgridAsGrid"),
        viewRecords: true,
        jsonReader: new Lib.Web.Mvc.JQuery.JqGrid.JqGridJsonReader() { RepeatItems = false, RecordId = "Id" }
    ).AddActionsColumn("actions", ...)
    .Navigator(new Lib.Web.Mvc.JQuery.JqGrid.JqGridNavigatorOptions() { Add = false, Delete = false, Edit = false, View = false, Search = false }),
    url: Url.Action("Orders"),
    viewRecords: true,
    jsonReader: new Lib.Web.Mvc.JQuery.JqGrid.JqGridJsonReader() { RepeatItems = false, RecordId = "Id" }
).AddActionsColumn("actions", ...);
And both actions must be changed as well:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Orders(JqGridRequest request)
{
    int totalRecords = _ordersRepository.GetCount();

    JqGridResponse response = new JqGridResponse()
    {
        TotalPagesCount = (int)Math.Ceiling((float)totalRecords / (float)request.RecordsCount),
        PageIndex = request.PageIndex,
        TotalRecordsCount = totalRecords
    };
    response.Records.AddRange(from order in _ordersRepository.FindRange(String.Format("{0} {1}", request.SortingName, request.SortingOrder), request.PageIndex * request.RecordsCount, request.RecordsCount)
                              select new JqGridRecord<OrderViewModel>(Convert.ToString(order.Id), new OrderViewModel(order)));
    response.Reader.RepeatItems = false;

    return new JqGridJsonResult() { Data = response };
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult OrderDetailsSubgridAsGrid(int id, JqGridRequest request)
{
    IEnumerable<OrderDetail> details = _ordersRepository.FindDetails(id);
    int totalRecords = details.Count();

    JqGridResponse response = new JqGridResponse()
    {
        TotalPagesCount = (int)Math.Ceiling((float)totalRecords / (float)request.RecordsCount),
        PageIndex = request.PageIndex,
        TotalRecordsCount = totalRecords
    };

    response.Records.AddRange(from OrderDetail orderDetails in details.Skip(request.PageIndex * request.RecordsCount).Take(request.RecordsCount)
                              select new JqGridRecord<OrderDetailViewModel>(orderDetails.ProductId.ToString(), new OrderDetailViewModel(orderDetails)));
    response.Reader.RepeatItems = false;

    return new JqGridJsonResult() { Data = response };
}
The result is exactly the same as previous screenshot.

So both ways work correct. If despite this description you will not be able to solve your issue please send me a small but fully working repro project which shows the problem - I will look into it.

Regards,
Tomasz Pęczek

P.S. The "clearing" issue might suggest that the RecordId on JsonReader is not set to the correct field name (so in the result your Subgrid action is not getting the actual id of parent row).
Sep 22, 2013 at 10:02 AM
Selecting the local approach (key names differ, don't like "global stuff"): The right number of rows are shown, but with no data displayed.
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult AnswersSubgrid(int? id)
    {
         ...
         JqGridResponse response = new JqGridResponse(true);

        response.Reader.RepeatItems = false;

        response.Records.AddRange(from Answer ans in qstRps.FindByKey((int) id).Answers
                                  select new JqGridRecord<AnswerFormattedModel>(null, new AnswerFormattedModel(this, ans)));

        return new JqGridJsonResult() { Data = response };
    }
The key is hidden, but changing that property has no effect.
    [JqGridColumnMapping(Key = true)]
    [HiddenInput]
    public int AnswerId { get; set; }

    [JqGridColumnSortable(true, Index = "Text")]
    [JqGridColumnLayout(Width=600, CellAttributes="function (rowId, tv, rawObject, cm, rdata) { return 'style=\"white-space: normal;\"' }")]
    [JqGridColumnEditable(true, EditType = JqGridColumnEditTypes.Text)]
    [StringLength(4000)]
    [Required]
    public string Text { get; set; }

    [JqGridColumnEditable(true, "AnswerTypes", "Answer", EditType = JqGridColumnEditTypes.Select)]
    ...
Repeattimes is ALSO set in the view subgrid declaration
 subgridHelper: new Lib.Web.Mvc.JQuery.JqGrid.JqGridHelper<Assessary.Models.JQGrid.AnswerFormattedModel>("answers",
          dataType: Lib.Web.Mvc.JQuery.JqGrid.JqGridDataTypes.Json,
          editingUrl: Url.Action("UpdateAnswer"),
          methodType: Lib.Web.Mvc.JQuery.JqGrid.JqGridMethodTypes.Post,
          pager: true,
          rowsNumber: 10,
          sortingName: "Ordinal",
          sortingOrder: Lib.Web.Mvc.JQuery.JqGrid.JqGridSortingOrders.Asc,
          url: Url.Action("AnswersSubgrid"),
          viewRecords: true,
          jsonReader: new Lib.Web.Mvc.JQuery.JqGrid.JqGridJsonReader() { __RepeatItems = false__, RecordId = "AnswerId" }
      ).AddActionsColumn("actions",
              inlineEditingOptions: new Lib.Web.Mvc.JQuery.JqGrid.JqGridInlineNavigatorActionOptions() { Keys = true },
              deleteOptions: new Lib.Web.Mvc.JQuery.JqGrid.JqGridNavigatorDeleteActionOptions() { Url = Url.Action("DeleteAnswer") })
         .Navigator(new Lib.Web.Mvc.JQuery.JqGrid.JqGridNavigatorOptions() { Add = false, Edit = false, Delete = false })
          .InlineNavigator(new Lib.Web.Mvc.JQuery.JqGrid.JqGridInlineNavigatorOptions()
          {
              Edit = false,
              AddActionOptions = new Lib.Web.Mvc.JQuery.JqGrid.JqGridInlineNavigatorAddActionOptions()
              {
                  RowId = "_empty",
                  Options = new Lib.Web.Mvc.JQuery.JqGrid.JqGridInlineNavigatorActionOptions()
                  {
                      Url = Url.Action("InsertAnswer")
                  }
              }
          }),
Coordinator
Sep 22, 2013 at 12:33 PM
If you are using "Subgrid as Grid" scenario you shouldn't be passing true to JqGridResponse constructor. That is because the receiver is not a subgrid (in jqGrid terminology) but a standard grid for which the helper is performing some smart rendering with help of jqGrid subGridRowExpanded event (this the exact mechanism described here).

Passing true to JqGridResponse constructor will make the serializer use the settings from response.Reader.SubgridReader not directly from response.Reader, also the serialized data format for subgrid is a little bit different.
Sep 23, 2013 at 8:45 AM
Great, thanks! That was the problem.