Related
I have an editable multi selectable kendo Treelist. I would like to be able to select part of the grid and copy paste its data in the same grid (other columns and rows) or to a text file. It is important to paste it with the same structure in the new table.
The copy feature is not supported for kendo Treelist.
Is there a way to do that with use of JavaScript and jQuery?
Kendo demo
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Kendo UI Snippet</title>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2023.1.117/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/2023.1.117/js/kendo.all.min.js"></script>
</head>
<body>
<div id="treeList"></div>
<script>
$("#treeList").kendoTreeList({
columns: [
{ field: "name" },
{ field: "age" }
],
selectable: "multiple, cell",
editable:"incell",
dataSource: [
{ id: 1, parentId: null, name: "Jane Doe", age: 22 },
{ id: 2, parentId: 1, name: "John Doe", age: 24 },
{ id: 3, parentId: 1, name: "Jenny Doe", age: 3 }
]
});
</script>
</body>
</html>
I have used two buttons, one for copying and one for pasting. The events functions are as below. This solved my problem and I can also paste the copied text in excel.
<button onClick="copying()" >Copy</button>
<button onClick="pasting()" >Paste</button>
<div id="treeList"></div>
<script>
$("#treeList").kendoTreeList({
columns: [
{ field: "name" },
{ field: "age" }
],
selectable: "multiple, cell",
editable:"incell",
dataSource: [
{ id: 1, parentId: null, name: "Jane Doe", age: 22 },
{ id: 2, parentId: 1, name: "John Doe", age: 24 },
{ id: 3, parentId: 1, name: "Jenny Doe", age: 3 }
]
});
</script>
var copiedText="";
function copying(){
if(copiedText !== ""){
return;
}
var grid = $("#treeList").data("kendoTreeList");
var selected = grid.select();
var previousRowID = selected.eq(0).parent().index();
var isNewLine = true;
selected.each(function() {
var row = $(this).closest("tr");
var dataItem = grid.dataItem(this);
if (previousRowID !== $(this).parent().index()) {
copiedText += "\r\n";
isNewLine = true;
}
previousRowID = $(this).parent().index();
var colIndx = $("td", row).index(this);
var column = grid.columns[colIndx];
var data = dataItem;
var value = dataItem[column.field];
if (!isNewLine) {
copiedText += "\t";
}
copiedText += value;
isNewLine = false;
});
var textarea = $("<textarea>");
var offset = $(this).offset();
// Position the textarea on top of the Treelist and make it transparent.
textarea.css({
position: 'absolute',
opacity:0,
border: 'none',
width: $(this).find("table").width(),
height: $(this).find(".k-grid-content").height()
});
textarea.val(copiedText)
.appendTo('body')
.focus()
.select();
document.execCommand('copy');
setTimeout(function(){
textarea.remove();
});
}
function pasting() {
var pasteVal = copiedText;
var grid = $("#treeList").data("kendoTreeList");
if (pasteVal) {
var selectedArr= Object.values($(".k-grid td.k-selected"));
var pasteArray = pasteVal.split("\r\n").filter(r => r !== "").map(r => r.split("\t"));
pasteArray.forEach(function( item, index) {
selectedArr[index].innerHTML = item;
});
grid.refresh();
}
copiedText= "";
}
I am having a kendo grid where the multiple row selection is enabled. With multiple selection functionality, when users try to copy custom text like any value from the column, it enables multiple select features thus users cannot copy custom text.
Below is the sample:
https://dojo.telerik.com/#erpuneet507/ivAfoFup
The issue is, in the above example you can select text from a single cell/ or any partial text from the cell.
If you have multiple selection enabled, then your app has to handle the copy text to clipboard. In doing so, you'll need the clipboard.js library and add a Kendo ContextMenu. Handle the copy text in the ContextMenu select event. Copy Text should pop up on right click.
Try the code below in the Telerik DOJO:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Kendo UI Snippet</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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js"></script>
</head>
<body>
<div id="grid"></div>
<ul id="context-menu">
<li id="copyText">Copy Text</li>
</ul>
<script>
$(document).ready(function() {
$("#grid").kendoGrid({
selectable: "multiple row",
allowCopy: true,
columns: [
{ field: "productName" },
{ field: "category" }
],
dataSource: [
{ productName: "Tea", category: "Beverages" },
{ productName: "Coffee", category: "Beverages" },
{ productName: "Ham", category: "Food" },
{ productName: "Bread", category: "Food" }
]
});
$("#context-menu").kendoContextMenu({
target: "#grid",
filter: "td",
select: function (e) {
var cell = e.target;
var row = $(cell).parent()[0];
var grid = $("#grid").data("kendoGrid");
var itemId = e.item.id;
var cellText = cell.innerText;
if (itemId === 'copyText') {
new Clipboard('#copyText', {
text: function (trigger) {
return cellText;
}
});
};
}
});
});
</script>
</body>
</html>
Here's a revised version of the above. You'll no longer need the clipboard.js. This will show/pop up the text (e.g. coffee) when you right click so that you can copy the 'ffee'.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Kendo UI Snippet</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>
<ul id="context-menu">
<li id="copyText">
<input id="valueText"></input>
<span id="closeValueText" class="k-icon k-i-close"></span>
</li>
</ul>
<script>
$(document).ready(function() {
$("#grid").kendoGrid({
selectable: "multiple row",
allowCopy: true,
columns: [
{ field: "productName" },
{ field: "category" }
],
dataSource: [
{ productName: "Tea", category: "Beverages" },
{ productName: "Coffee", category: "Beverages" },
{ productName: "Ham", category: "Food" },
{ productName: "Bread", category: "Food" }
]
});
var closeContextMenu = false;
var contextMenu = $("#context-menu").kendoContextMenu({
target: "#grid",
filter: "td",
close: function(e) {
if (!closeContextMenu) {
e.preventDefault();
}
},
open: function(e) {
var cell = e.target;
var row = $(cell).parent()[0];
var grid = $("#grid").data("kendoGrid");
var itemId = e.item.id;
var cellText = cell.innerText;
$(e.item).find("#valueText").val(cellText);
closeContextMenu = false;
}
}).data("kendoContextMenu");
$("#closeValueText").on("click", function(e) {
closeContextMenu = true;
contextMenu.close();
});
});
</script>
</body>
</html>
The sorting function will no longer work on column that is using itemTemplate and headerTemplate.
You can see a fiddle from here.
As you can see, in the column "Client ID", the sorting works really well. But on column "Client Name", the sorting doesn't work as I am using itemTemplate and headerTemplate for customization.
Any workaround is really appreciated.
Here's the code:
$("#jsGrid").jsGrid({
width: "100%",
sorting: true,
paging: true,
data: [{
ClientId: 1,
Client: "Aaaa Joseph"
},
{
ClientId: 2,
Client: "Zzzz Brad"
},
{
ClientId: 3,
Client: "Mr Nice Guy"
}
],
fields: [{
width: 80,
name: "ClientId",
type: "text",
title: "Client ID"
},
{
width: 80,
itemTemplate: function(value, item) {
return "<div>" + item.Client + "</div>";
},
headerTemplate: function() {
return "<th class='jsgrid-header-cell'>Client Name</th>";
}
},
]
});
In jsgrid name is a property of data item associated with the column. And on header click this _sortData function will call in jsgrid.js for sorting data. And this name config will use here. So for this you have to provide this config other it will blank and no data sorting on header click.
Please search this below function in jsgrid.js
_sortData: function() {
var sortFactor = this._sortFactor(),
sortField = this._sortField;
if (sortField) {
this.data.sort(function(item1, item2) {
return sortFactor * sortField.sortingFunc(item1[sortField.name], item2[sortField.name]);
});
}
},
In above code sortField.name as column config and it is must mandatory.
DEMO
$("#jsGrid").jsGrid({
width: "100%",
sorting: true,
paging: true,
data: [
{ ClientId : 1, Client: "Aaaa Joseph"},
{ ClientId : 2, Client: "Zzzz Brad"},
{ ClientId : 3, Client: "Mr Nice Guy"}
],
fields: [
{
width: 80,
name: "ClientId",
type: "text",
title: "Client ID"
},
{
width: 80,
name:'Client',
itemTemplate: function (value, item) {
return "<div>"+item.Client+"</div>";
},
headerTemplate: function () {
return "<th class='jsgrid-header-cell'>Client Name</th>";
}
},
]
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid.min.css" />
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid-theme.min.css" />
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid.min.js"></script>
<div id="jsGrid"></div>
Another way you can make manually sorting on header click.
Way to sort JS grid on column after grid load :
onRefreshing: function (args) {
fundCodeList = [];
jsonNumLst = [];
jsonNANLst = [];
if(this._visibleFieldIndex(this._sortField) == -1
|| this._visibleFieldIndex(this._sortField)==1){
$.each(filteredData, function(inx, obj) {
if($.isNumeric(obj.fundCode)){
jsonNumLst.push(obj);
}else{
jsonNANLst.push(obj);
}
});
if(this._sortOrder == undefined || this._sortOrder == 'asc'){
jsonNumLst.sort(sortByNumFCAsc);
jsonNANLst.sort(sortByNANFCAsc);
}else if(this._sortOrder == 'desc'){
jsonNANLst.sort(sortByNANFCDesc);
jsonNumLst.sort(sortByNumFCDesc);
}
if(jsonNumLst.length>0 || jsonNANLst.length>0){
filteredData = [];
if(this._sortOrder == undefined || this._sortOrder == 'asc'){
$.each(jsonNumLst, function(inx, obj) {
filteredData.push(obj);
});
if(filteredData.length == jsonNumLst.length){
$.each(jsonNANLst, function(inx, obj) {
filteredData.push(obj);
});
}
}else if(this._sortOrder == 'desc'){
$.each(jsonNANLst, function(inx, obj) {
filteredData.push(obj);
});
if(filteredData.length == jsonNANLst.length){
$.each(jsonNumLst, function(inx, obj) {
filteredData.push(obj);
});
}
}
}
if((filteredData.length>0) && filteredData.length==(jsonNumLst.length+jsonNANLst.length)){
$("#measureImportGrid3").data("JSGrid").data = filteredData;
//isSortGrid = false;
//saveEffectControlData = $('#saveEffectiveControlGrid').jsGrid('option', 'data');
}
}
}
//Ascending order numeric
function sortByNumFCAsc(x,y) {
return x.fundCode - y.fundCode;
}
//Ascending order nonnumeric
function sortByNANFCAsc(x,y) {
return ((x.fundCode == y.fundCode) ? 0 : ((x.fundCode > y.fundCode) ? 1 : -1 ));
}
//Descending order numeric
function sortByNANFCDesc(x,y) {
return ((x.fundCode == y.fundCode) ? 0 : ((y.fundCode > x.fundCode) ? 1 : -1 ));
}
//Descending order non-numeric
function sortByNumFCDesc(x,y) {
return y.fundCode - x.fundCode;
}
I didn't know what had went wrong where i had defined the function but it still giving error.
var sampleData = [
{ id: 1, name: "name", items: ["foo", "bar"] }
];
var defaultColumns=[
{ field: "id" },
{ field: "name" },
{ field: "items", "template":kendo.template("#= showItems(items) #") }
];
$("#grid").kendoGrid({
dataSource: {data:sampleData},
columns:defaultColumns
});
function showItems(arr) {
return arr.join("</br>") ;
}
My sample code
Make sure that showItems is defined in the global JavaScript scope in your actual application. This is required for custom functions called from inside Kendo UI templates:
http://docs.telerik.com/kendo-ui/framework/templates/overview#handle-external-templates-and-expressions
var sampleData = [
{ id: 1, name: "name", items: ["foo", "bar"] }
];
var defaultColumns=[
{ field: "id" },
{ field: "name" },
{ field: "items", template: showItems }
];
$("#grid").kendoGrid({
dataSource: {data:sampleData},
columns:defaultColumns
});
function showItems(arr) {
return arr.items.join("</br>") ;
}
I was able to solve the solution by declaring the showItems in global and it perfectly fine.
function showItems(arr) {
return arr.join("</br>");
}
var sampleData = [{
id: 1,
name: "name",
items: ["foo", "bar"]
}];
var defaultColumns = [{
field: "id"
}, {
field: "name"
}, {
field: "items",
"template": kendo.template("#= showItems(items) #")
}];
$("#grid").kendoGrid({
dataSource: {
data: sampleData
},
columns: defaultColumns
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Kendo UI Grid</title>
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2016.3.914/styles/kendo.common.min.css" />
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2016.3.914/styles/kendo.silver.min.css" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://kendo.cdn.telerik.com/2016.3.914/js/kendo.all.min.js"></script>
</head>
<body>
<div id="grid"></div>
</body>
</html>
I have uploaded a page where the error occurs. It´s displayed in the console (please use F12 in Firefox or Chrome Browser).
http://preventdefault.lima-city.de/index.php
This line is wrong: "kendo.stringify(grid.getOptions())"
My Question: How must i change this code so that i could store the changed table settings?
I also insert the html code here, Thx for answer !
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1411/styles/kendo.common.min.css">
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1411/styles/kendo.rtl.min.css">
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1411/styles/kendo.default.min.css">
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1411/styles/kendo.dataviz.min.css">
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1411/styles/kendo.dataviz.default.min.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.16/angular.min.js"></script>
<script src="http://cdn.kendostatic.com/2014.3.1411/js/jszip.min.js"></script>
<script src="http://cdn.kendostatic.com/2014.3.1411/js/kendo.all.min.js"></script>
<style type="text/css">
.button-center {
text-align: center; /* button position in grid */
}
</style>
</head>
<body role="document">
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">WebSiteName</a>
</div>
<div>
<ul class="nav navbar-nav">
<li class="active">one</li>
<li>two</li>
</ul>
</div>
</div>
</nav>
<div class="container theme-showcase" id="main" role="main">
<div class="container">
<h1>Page<small> one</small></h1>
</div>
<div class="row-fluid">
<div id="grid_one"></div>
</div> <!-- row -->
<div class="row-fluid">
<div id="log"></div>
</div> <!-- row -->
</div> <!-- container -->
<script>
<!-- --------------------------------------------------------------------------------- -->
var firstNames = ["Nancy", "Andrew", "Janet", "Margaret", "Steven",
"Michael", "Robert", "Laura", "Anne", "Nige"],
lastNames = ["Davolio", "Fuller", "Leverling", "Peacock", "Buchanan",
"Suyama", "King", "Callahan", "Dodsworth", "White"],
cities = ["Seattle", "Tacoma", "Kirkland", "Redmond", "London",
"Philadelphia", "New York", "Seattle", "London", "Boston"],
titles = ["Accountant", "Vice President, Sales", "Sales Representative",
"Technical Support", "Sales Manager", "Web Designer",
"Software Developer", "Inside Sales Coordinator", "Chief Techical Officer",
"Chief Execute Officer"],
birthDates = [new Date("1948/12/08"), new Date("1952/02/19"), new Date("1963/08/30"),
new Date("1937/09/19"), new Date("1955/03/04"), new Date("1963/07/02"),
new Date("1960/05/29"), new Date("1958/01/09"), new Date("1966/01/27"),
new Date("1966/03/27")];
function createRandomData(count) {
var data = [], now = new Date();
for (var i = 0; i < count; i++) {
var firstName = firstNames[Math.floor(Math.random() * firstNames.length)],
lastName = lastNames[Math.floor(Math.random() * lastNames.length)],
city = cities[Math.floor(Math.random() * cities.length)],
title = titles[Math.floor(Math.random() * titles.length)],
birthDate = birthDates[Math.floor(Math.random() * birthDates.length)],
age = now.getFullYear() - birthDate.getFullYear();
data.push({
Id: i + 1,
FirstName: firstName,
LastName: lastName,
City: city,
Title: title,
BirthDate: birthDate,
Age: age
});
}
return data;
}
<!-- --------------------------------------------------------------------------------- -->
function onChangeSelection() {
var selectedItem = this.dataItem(this.select());
var Text = '<h1><small>row name=</small>' + selectedItem.FirstName + " " + selectedItem.LastName + "</h1>";
console.log(Text);
$("#log").html(Text);
$("#ordernumber").val(selectedItem.ordernumber);
}
<!-- --------------------------------------------------------------------------------- -->
function startbuttonclick(e) {
var data = this.dataItem($(e.currentTarget).closest("tr"));
var Text = "<h1><small>Button click name=</small> " + data.FirstName + " " + data.LastName + "</h1>";
console.log(Text);
$("#log").html(Text);
e.preventDefault();
}
<!-- --------------------------------------------------------------------------------- -->
$(document).ready(function() {
$("#grid_one").kendoGrid({
dataSource: {
data: createRandomData(10),
schema: {
model: {
fields: {
FirstName: { type: "string" },
LastName: { type: "string" },
City: { type: "string" },
Title: { type: "string" },
BirthDate: { type: "date" },
Age: { type: "number" }
}
}
},
pageSize: 10
},
height: 500,
dataBound: saveState,
columnResize: saveState,
columnShow: saveState,
columnHide: saveState,
columnReorder: saveState,
columnLock: saveState,
columnUnlock: saveState,
selectable: true,
resizable: true,
columnMenu: true,
scrollable: true,
sortable: true,
filterable: true,
change: onChangeSelection,
pageable: {
refresh: true,
pageSizes: true,
buttonCount: 5,
pageSizes: [5, 10, 20, 250]
},
columns: [
{
field: "FirstName",
title: "First Name",
width: "150px",
},
{
field: "LastName",
title: "Last Name",
width: "150px",
},
{
field: "City",
hidden: true
},
{
field: "Title",
hidden: true
},
{
field: "BirthDate",
title: "Birth Date",
template: '#= kendo.toString(BirthDate,"MM/dd/yyyy") #',
width: "175px",
},
{
field: "Age",
width: "150px",
},
{
command: {
text: "Start",
click: startbuttonclick },
title: "Start",
width: "65px",
attributes:{
"class":"button-center"}
}
]
});
<!-- ------------------------------------------------------------------------------ -->
var grid = $("#grid_one").data("kendoGrid");
function saveState(e) {
e.preventDefault();
localStorage["kendo-grid-one"] = kendo.stringify(grid.getOptions());
};
$(function (e) {
var options = localStorage["kendo-grid-one"];
if (options) {
grid.setOptions(JSON.parse(options));
} else {
grid.dataSource.read();
}
});
});
<!-- --------------------------------------------------------------------------------- -->
</script>
</body>
</html>
Edited for:
Your var grid = $("#grid_one").data("kendoGrid"); only defined once, and it may not have data upon defined, it maybe insert by your kendogrid after.
Domready Part should also need to have reference to it, you can either put it at origin location or move it into the function
From your and dfsq's replies, the problem is that json CANNOT store a function, so you have to add it back to when retrieved from the localstorage
In your current code, saveState will always be called before the setOptions one, which means the saveState just erased by your saveState function, so you have to move the setoptions code forward.
Changes a lot
$(document).ready(function() {
// Get options first
var options = localStorage["kendo-grid-one"];
if (options) {
options = JSON.parse(options);
// Workaround to addback event
options.columns[6].command.click = startbuttonclick;
}
$("#grid_one").kendoGrid({
dataSource: {
data: createRandomData(10),
schema: {
.....
});
if (options) {
$("#grid_one").data("kendoGrid").setOptions(options);
}
<!-- ------------------------------------------------------------------------------ -->
function saveState(e) {
var grid = $("#grid_one").data("kendoGrid");
e.preventDefault();
localStorage["kendo-grid-one"] = kendo.stringify(grid.getOptions());
};
See Demo Here, now it works.
saveState part use dfsq's may be better
options.columns[6].command.click = startbuttonclick; may be can write in a more elegant style, but here I just want to show why the issues come out and how to apply a basic solution.
I don't know kendo but the issue must be that the saveState function is called before the grid is declared.
JSFiddle: http://jsfiddle.net/8x7v7mga/1/
So somewhere in the construction of the kendo object, one of the saveState handlers is called.
You can avoid this by declaring the grid variable first and then in the saveState simply check if grid is defined or not:
var grid = null;
$("#grid_one").kendoGrid({ ... });
grid = $("#grid_one").data("kendoGrid");
function saveState(e) {
e.preventDefault();
grid && localStorage["kendo-grid-one"] = kendo.stringify(grid.getOptions());
};
The problem is that saveState function is called before grid is initialzed. you cantually don't need grid reference since saveState s called in grid context so you can simply use this instead:
function saveState(e) {
e.preventDefault();
localStorage["kendo-grid-one"] = kendo.stringify(this.getOptions());
};