Kendo TreeView with KendoGrid children with drag&drop operation and editing inside KendoGrid implemented

In this post we will demonstrate how to implement Kendo UI Grid inside Kendo TreeView. Before reading this article you should be familiar with how to create and initialize Kendo UI elements, especially Kendo Grid and Kendo TreeView.

Examples are located at http://demos.telerik.com/kendo-ui/treeview/index and http://demos.telerik.com/kendo-ui/grid/index.

Implementing Kendo TreeView

First we have to create Kendo treeview and fill it with data.

<div id="example">
    <div class="demo-section k-content col-sm-6">
        <div class="col-sm-6">
            <div id="treeview"></div>
        </div>
    </div>
</div>

$("#treeview").kendoTreeView({
    dataSource: [
        { id: "1", text: "CarBrand#1", items: [{ id: "1", text: "Car#1" }, 
        { id: "2", text: "Car#2" }, { id: "3", text: "Car#3" }] },
        { id: "2", text: "CarBrand#2", items: [{ id: "1", text: "Car#1" }, 
        { id: "2", text: "Car#2" }, { id: "3", text: "Car#3" }] }
    ]
});

Your TreeView should look something like this:

Implementing KendoGrid

Next step is to implement KendoGrid. We have to create a template in Kendo TreeView to create div elements with id created from item id's from DataSource. We start by modifying DataSource of Kendo TreeView, so that our children have an additional id added (parentId).

{ id: "1", text: "CarBrand#1", items: [
    { id: "1", text: "Car#1", parentId: "1" }, 
    { id: "2", text: "Car#2", parentId: "1" }, 
    { id: "3", text: "Car#3", parentId: "1" }] 
},
{ id: "2", text: "CarBrand#2", items: [
    { id: "1", text: "Car#1", parentId: "2" },
    { id: "2", text: "Car#2", parentId: "2" }, 
    { id: "3", text: "Car#3", parentId: "2" }] 
}

Next we have to create Kendo template so we can initialize grids. Kendo template allows us to customize TreeView cells.

<script id="treeview-template" type="text/kendo-ui-template">
    <div id=" ${ item.id }"> ${ item.text }</div>
    # if (!item.items) { #
        <div id="exampleGrid_${ item.parentId }_${ item.id }" class="example-grid">
        </div>
    # } #
</script>

Note: if (!item.items) checks if node has any children. Only if node is a child item we append div with grid id inside.

Initialize KendoGrid inside each child node, with jquery each function:

$(document).ready(function () {
    $(".example-grid").each(createTreeView);
});

function createTreeView(index, value) {
    $(this).kendoGrid({
        dataSource: {
            data: [
                { Name: "CarName#1", Number: 32, New: true },
                { Name: "CarName#2", Number: 123, New: true },
                { Name: "CarName#3", Number: 232, New: false }
            ],
            schema: {
                model: {
                    fields: {
                        Name: { type: "string" },
                        Number: { type: "number" },
                        New: { type: "boolean" }
                    }
                }
            }
        },
        editable: true,
        columns: [
            { field: "Name", title: "Name" },
            { field: "Number", title: "Number" },
            { field: "New" }
        ]
    });
}

Implementing Drag and Drop

Next goal is to implement drag and drop oparation inside TreeView and limit where user can drop nodes. For example: We should not drop car element on the same level as CarBrand element. We have to call preventDefault() on drop event and check if node can be dropped there.

$("#treeview").kendoTreeView({
    template: kendo.template($("#treeview-template").html()),
    dataSource: [
        { id: "1", text: "CarBrand#1", items: [
            { id: "1", text: "Car#1", parentId: "1" }, 
            { id: "2", text: "Car#2", parentId: "1" }, 
            { id: "3", text: "Car#3", parentId: "1" }] 
        },
        { id: "2", text: "CarBrand#2", items: [
            { id: "1", text: "Car#1", parentId: "2" },
            { id: "2", text: "Car#2", parentId: "2" }, 
            { id: "3", text: "Car#3", parentId: "2" }] 
        }
    ],
    dragAndDrop: true,
    drop: function (e) {
        e.preventDefault();

        var tree = $("#treeview").data('kendoTreeView');

        var data = tree.dataItem(e.sourceNode);
        var newData = tree.dataItem(e.destinationNode);

        if (!newData)
            return;

        if (data.level() != newData.level())
            return;
        else if (data.level() == newData.level() && e.dropPosition == "over")
            return;
        else if (data.level() == newData.level() && e.dropPosition == "after")
            $(e.sourceNode).insertAfter($(e.destinationNode));
        else
            $(e.sourceNode).insertBefore($(e.destinationNode));
    }
});

Implementing editing inside Kendo Grid

If we try to edit data in kendo grid we unfortunately couldn't. That happens because Kendo TreeView is taking focus from KendoGrid. One solution to prevent this is to delete click event on TreeView and all nodes in TreeView. Then we manually add click events on each node. That prevents grid from losing focus and allows us to edit grid data. Create a function and call in document ready function after you unbind click from TreeView and also append it to Kendo Grid.

$(document).ready(function () {
    $("#treeview").unbind("click");
    BindClickEventsToTreeView();

    $(".example-grid").each(createGrid);
});


function BindClickEventsToTreeView() {
    $("#treeview").find(".k-item .k-icon").unbind("click");
    $("#treeview").find(".k-item .k-icon").click(function (e) {
        var tree = $("#treeview").data('kendoTreeView');
        tree.toggle($(e.target).closest(".k-item"));
    });
}

Append this line to kendo grid konfiguration:

edit: BindClickEventsToTreeView

Now we should have functional Treeview with drag and drop functionality ( we can only drag&drop nodes that are on the same level) with grid on child nodes inside and grid editing. You can download example from https://bitbucket.org/evizija/kendotreeviewwithgrid