Kendo Sortable integrated with a grouped Grid

1.3k views Asked by At

Here's my View, in which I have a Sortable that's integrated with a Grid. This works fine, but the problem is that the Grid is grouped. And, I want each group to have its own Sortable functionality, meaning that the user should not be able to drag and drop rows from one group to another. How do I do that? Do I have to have separate Sortables for each group?

@(Html.Kendo().Grid<QRMT.ViewModels.SubsystemViewModel>()
    .Name("subsystems")
    .ToolBar(toolbar => toolbar.Create().Text("Add new Subsystem"))
    .Columns(columns =>
    {
        columns.ForeignKey(c => c.SystemId, new SelectList(ViewBag.Systems, "Value", "Text")).Hidden();
        columns.Bound(c => c.SubsystemCode);
        columns.Bound(c => c.SubsystemDesc);
        columns.Command(c => { c.Edit(); c.Destroy(); }).Width(200);
    })
    .Editable(e => e.Mode(GridEditMode.PopUp).Window(window => window.Width(500)))
    .DataSource(dataSource => dataSource
        .Ajax()
        .Events(events => events.Sync("onSync").Error("onError"))
        .Model(model =>
        {
            model.Id(m => m.SubsystemId);
        })
        .Group(group => group.Add(m => m.SystemId))
        .Create(create => create.Action("Add", "Subsystems"))
        .Read(read => read.Action("Read", "Subsystems"))
        .Update(update => update.Action("Update", "Subsystems"))
        .Destroy(destroy => destroy.Action("Delete", "Subsystems"))
    )
    .Events(events => events.Edit("onEdit"))
)


@(Html.Kendo().Sortable()
    .For("#subsystems")
    .Filter("table > tbody > tr:not(.k-grouping-row)")
    .Cursor("move")
    .HintHandler("noHint")
    .PlaceholderHandler("placeholder")
    .ContainerSelector("#subsystems tbody")
    .Events(events => events.Change("onChange"))
)

<script type="text/javascript">
    var noHint = $.noop;

    function placeholder(element) {
        return element.clone().addClass("k-state-hover").css("opacity", 0.65);
    }

    function onEdit(e) {
        if (e.model.isNew()) {
            $('.k-window-title').text("Add");
        }
    }

    function onSync(e) {
        this.read();
    }

    function onError(e) {
        alert(e.errors);
    }

    function onChange(e) {
        var grid = $("#subsystems").data("kendoGrid"),
            skip = grid.dataSource.skip(),
            oldIndex = e.oldIndex + skip,
            newIndex = e.newIndex + skip,
            data = grid.dataSource.data(),
            dataItem = grid.dataSource.getByUid(e.item.data("uid"));

        grid.dataSource.remove(dataItem);
        grid.dataSource.insert(newIndex, dataItem);
    }
</script>

<style>
    .k-grid tbody tr:not(.k-grouping-row) {
        cursor: move;
    }
</style>
2

There are 2 answers

0
sasheto On BEST ANSWER

Kendo Sortable widget does not work with grouped Grid. This is written in the known limitations section of Sortable-Grid integration help topic.

2
SDM On

Kendo says using KendoSortable is impossible on grouped grids, I did however find a possible solution! in your question you limit yourself to only being able to drag within the same group. If you have a field to store the order and sort correctly, this does not matter.

My solution posted below, was made for an angular implementation, but the onchange event is both javascript so I think this can be reworked. (tr filter [data-uid] will ensure no grouping-row is 'selected')

  • first number all rows correctly, possibly needed to initiate the SortOrder value
  • then set the new index on the moved row
  • then change where needed
  • then resort the grid. Possibly hardcoded fieldname is needed here instead of 'primarySortField'
$scope.initMySortOrderGrouped = function(grid, myDatasource, primarySortField) {
  grid.table.kendoSortable({
    filter: ">tbody>tr[data-uid]",
    hint: $.noop,
    cursor: "move",
    placeholder: function(element) {
      return element.clone().addClass("k-state-hover").css("opacity", 0.65);
    },
    container: "#" + grid.element[0].id + " tbody",
    change: function(e) {
      /*Needed on grid for sorting to work conditionally*/
      if (grid.canWrite) {
        var skip = 0,
          oldIndex = e.oldIndex + skip,
          newIndex = e.newIndex + skip,
          view = myDatasource.view(),
          dataItem = myDatasource.getByUid(e.item.data("uid")); 
        /*retrieve the moved dataItem*/

        /*set initial values where needed*/
        var countRows = 0;
        for (var i = 0; i < view.length; i++) {
          for (var j = 0; j < view[i].items.length; j++) {
            if (view[i].items[j].SortOrder != countRows + j) {
              view[i].items[j].SortOrder = countRows + j;
              view[i].items[j].dirty = true;
            }
          }
          countRows += view[i].items.length
        }

        dataItem.SortOrder = newIndex; /*update the order*/
        dataItem.dirty = true;

        countRows = 0;
        /*shift the order of the records*/
        for (var i = 0; i < view.length; i++) {
          for (var j = 0; j < view[i].items.length; j++) {
            if (oldIndex < newIndex) {
              if (countRows + j > oldIndex && countRows + j <= newIndex) {
                view[i].items[j].SortOrder--;
                view[i].items[j].dirty = true;
              }
            } else {
              if (countRows + j >= newIndex && countRows + j < oldIndex) {
                view[i].items[j].SortOrder++;
                view[i].items[j].dirty = true;
              }
            }
          }
          countRows += view[i].items.length
        }
        myDatasource.sync(); 
        /*submit the changes through the update transport and refresh the Grid*/
      }
      myDatasource.sort([{
        field: primarySortField,
        dir: "asc"
      }, {
        field: "SortOrder",
        dir: "asc"
      }]);
    }
  });
};

This will allow you to drag items over it's group, the number will be far higher or lower than it could be in the group, but the resulting sort will show correctly!

I hate it when programmers say something is impossible...