Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Javascript

Kendo Grid UI: Multiple Row Selection with Custom Text/Cell Text Copy Functionality

5.00/5 (1 vote)
8 Jul 2021CPOL2 min read 12.7K   50  
How to do multiple row selection with custom text/cell text copy functionality in Kendo Grid UI
When multiple row selection is enabled in Kendo Grid UI, the user cannot copy a single cell value or specific text from the grid which is the very basic requirement. We will be implementing this in this article.

Background

You need to have basic knowledge of using Telerik Kendo UI with JQuery/JavaScript(must).

Using the Code

The Problem

So first of all, below is the code for creating a basic Kendo UI grid with multiple row selection. Here if you run this, you will see that you can do multiple row selection. When you select any row or rows and do a CTRL+C to copy, you will copy the entire row data.

Now, suppose you just want to copy the text from a single cell or let's say only a part of the cell value, you can do that here because as soon as you try to select the text, it will invoke the multiple row selection features and won't let you select text. For example, try copying only 'Green' from the first row first column, we will not able to do that.

Use this link to refer to fiddler/dojo for the below code:

HTML
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Kendo Grid</title>
    <link rel="stylesheet" 
     href="https://kendo.cdn.telerik.com/2021.2.616/styles/kendo.default-v2.min.css"/>
    <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
    <script src="https://kendo.cdn.telerik.com/2021.2.616/js/kendo.all.min.js"></script>
</head>
<body>
  
<div id="grid"></div>
<script>
$("#grid").kendoGrid({
    selectable: "multiple row",
    allowCopy: true,
    columns: [
        { field: "productName" }, 
        { field: "category" }
    ],
    dataSource: [
        { productName: "Geen Tea", category: "Beverages" },
        { productName: "Hot Coffee", category: "Beverages" },
        { productName: "Ham Burger", category: "Food" },
        { productName: "Brown Bread", category: "Food" }
    ]
});
</script>
</body>
</html>

The Solution

Now we will manually implementing this feature step by step.

Step 1

Firstly, we will write a function to select a row or multiple rows on click. The below function will mark the grid row as select by highlighting the row background.

JavaScript
const embedMultiSelectGrid = () => {
            //Just to clear if any click event is attached previously
            $('#grid tr[role="row"]').off('click');
            $('#grid tr[role="row"]').on("click", function () {
                $("#grid").off("copy");
                var sel = getSelection().toString();
                if (!sel) {
                    var el = $("#grid"),
                        grid = el.data("kendoGrid"),
                        row = el.find("tbody>tr[data-uid=" + this.dataset.uid + "]");

                    if (row.length > 0) {
                        if (row.hasClass('k-state-selected')) {
                            row.removeClass('k-state-selected');
                        }
                        else {
                            grid.select(row);
                        }
                    }
                }
            });
        };

As you can see, we are adding class to the clicked row and pushing it to the grid select list.

Step 2

Now, we will write code to prepare the data to be copied. Here, we can do all kinds of formatting with data like how we want data to be copied. I will just copy the original data of each column with ',' as a delimiter but you can customize it as you want.

JavaScript
const bindCopyToGrid = (delimiter) => {
            $("#grid").on("copy", function (e) {
                const selected = myGrid.select();
                var selectedRowsData = "";
                for (let i = 0; i < selected.length; i++) {
                    var newRowData = '';
                    myGrid.columns.filter(x => x.hidden == undefined || 
                                          x.hidden == false).forEach((filter) => {
                        newRowData = newRowData == '' ? 
                        myGrid.dataItem(selected[i])[filter.field] : newRowData + 
                        delimiter + myGrid.dataItem(selected[i])[filter.field]
                    });
                    if (selectedRowsData.indexOf(newRowData) == -1)
                        selectedRowsData = selectedRowsData == "" ? 
                        selectedRowsData + newRowData : selectedRowsData + 
                        "\r\n" + newRowData;
                   
                }  
              return selectedRowsData;
            });
        }

In the above code, we are retrieving the data item value for the visible column only and concatenating them using a delimiter. You can choose the delimiter of your choice as I choose comma ',' as it's most common.

Note: The above function will invoke when the user tries to copy. So we are binding the copy event.

Step 3

Now, we will write code to invoke the copy command that actually copies the data to the user clipboard:

JavaScript
const copyToClipboard = str => {
           const el = document.createElement('textarea');
           el.value = str;
           el.setAttribute('readonly', '');
           el.style.position = 'absolute';
           el.style.left = '-9999px';
           document.body.appendChild(el);
           el.select();
           document.execCommand('copy');
           document.body.removeChild(el);
       };

So we are basically creating a hidden/invisible textarea and adding formatted data using the 'str' parameter following which we are calling the copy command.

Step 4, Final 1

Now, we just have to call these functions in the correct order as below:

Call embedMultiSelectGrid > Call bindCopyToGrid as call back or within embedMultiSelectGrid > Call copyToClipboard as call back or within bindCopyToGrid.

Below is the final code after setting order:

HTML
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Kendo Grid: Custom Copy with Multiselect</title>

    <link rel="stylesheet" 
     href="https://kendo.cdn.telerik.com/2021.2.616/styles/kendo.default-v2.min.css"/>

    <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
    <script src="https://kendo.cdn.telerik.com/2021.2.616/js/kendo.all.min.js"></script>
</head>
<body>
  
<div id="grid"></div>
<script>
  
 const copyToClipboard = str => {
            const el = document.createElement('textarea');
            el.value = str;
            el.setAttribute('readonly', '');
            el.style.position = 'absolute';
            el.style.left = '-9999px';
            document.body.appendChild(el);
            el.select();
            document.execCommand('copy');
            document.body.removeChild(el);
        };

        const bindCopyToGrid = (delimiter) => {
            $("#grid").on("copy", function (e) {
                const selected = myGrid.select();
                var selectedRowsData = "";
                for (let i = 0; i < selected.length; i++) {
                    var newRowData = '';
                    myGrid.columns.filter(x => x.hidden == undefined || 
                                          x.hidden == false).forEach((filter) => {
                        newRowData = newRowData == '' ? 
                        myGrid.dataItem(selected[i])[filter.field] : newRowData + 
                        delimiter + myGrid.dataItem(selected[i])[filter.field]
                    });
                                     
               
                    if (selectedRowsData.indexOf(newRowData) == -1)
                        selectedRowsData = selectedRowsData == "" ? 
                        selectedRowsData + newRowData : selectedRowsData + 
                        "\r\n" + newRowData;                   
                }
              
                copyToClipboard(selectedRowsData);
            });
        }

        const embedMultiSelectGrid = () => {
            $('#grid tr[role="row"]').off('click');
            $('#grid tr[role="row"]').on("click", function () {
                $("#grid").off("copy");
                var sel = getSelection().toString();
                if (!sel) {
                    var el = $("#grid"),
                        grid = el.data("kendoGrid"),
                        row = el.find("tbody>tr[data-uid=" + this.dataset.uid + "]");

                    if (row.length > 0) {
                        if (row.hasClass('k-state-selected')) {
                            row.removeClass('k-state-selected');
                        }
                        else {
                            grid.select(row);
                        }
                    }

                    bindCopyToGrid(",");
                }
            });
        };
  
var myGrid = $("#grid").kendoGrid({
    columns: [
        { field: "productName" }, 
        { field: "category" }
    ],
    dataBound: function(e){
      embedMultiSelectGrid();
    },
    dataSource: [
        { productName: "Tea", category: "Beverages" },
        { productName: "Coffee", category: "Beverages" },
        { productName: "Ham", category: "Food" },
        { productName: "Bread", category: "Food" }
    ]
}).data("kendoGrid");
  
</script>
</body>
</html>

 

Give it a Try: Demo

Use this link to see the functionality in action. Also, you can download the application with this implementation.

History

  • 8th July, 2021: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)