I am new to Cytoscape.js and also didn't write JavaScript or HTML before.
I installed Cytoscape.js by using npm:
npm install cytoscape
I then created a HTML file from HTML-Kit as follows:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<script src="cytoscape.js"></script>
</head>
</html>
I placed this HTML file under cytoscape.js-2.7.14 folder where the file cytoscape.js resides.
I then opened my HTML file with Chrome. The window (and tab) is opened, but there is nothing there.
I then modified my HTML file as follows:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Testing Cytoscape</title>
</head>
<body>
<script src="cytoscape.js"></script>
</body>
</html>
But there is still nothing in the window when I opened the HTML file.
Any ideas on how to run Cytoscape.js?
Thanks for advice!
Cytoscape.js is a library.
Library can't do anything by itself. You can't just run it and get a result. You need to write your code to initialize and use a library.
To use it properly, follow tutorials and guides on their website.
You can use it many ways, with a web client side only, or distributed architecture with web server.
Here is an exemple for a web client side only, where cytoscape libraries are not downloaded but referenced on the web.
You can also use it though a notebook, e.g. with Observable.
https://observablehq.com/#nfigay/test-adding-compound-with-cytoscape-js
<!DOCTYPE>
<html>
<head>
<title>Test Compound Graph Editor</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<script src="https://unpkg.com/cytoscape#3.21.0/dist/cytoscape.min.js"></script>
<script src="https://unpkg.com/layout-base#2.0.1/layout-base.js"></script>
<script src="https://unpkg.com/cose-base#2.1.0/cose-base.js"></script>
<script src="https://unpkg.com/cytoscape-fcose#2.1.0/cytoscape-fcose.js"></script>
<script src="https://unpkg.com/cytoscape-expand-collapse#4.1.0/cytoscape-expand-collapse.js"></script>
<script src="https://unpkg.com/pure-uuid#1.6.2/uuid.js"></script>
</head>
<body>
<div id="cy" style="width:100%;height:90%;background-color:white"></div>
<div class="menu-container">
<div style="text-align:center" class="panel">
<button id="compound">Add Compound Node</button>
<button id="collapseAll">Collapse all</button>
<button id="collapseRecursively">Collapse Recursively</button>
<button id="expandAll">Expand all</button>
<button id="expandRecursively">Expand Recursively</button>
</div>
</div>
</body>
<script>
var myData = [
{ data: { id: "o", "label": "Top node" } },
{ data: { id: "a", parent: "o", "label": "a" } },
{ data: { id: "b", parent: "o" } },
{ data: { id: "c", parent: "o" } },
{ data: { id: "d", parent: "o" } },
{ data: { id: "e", parent: "o" } },
{ data: { id: "f", parent: "o" } },
{ data: { id: "a1", parent: "a" } },
{ data: { id: "b1", parent: "b" } },
{ data: { id: "c1", parent: "c" } },
{ data: { id: "d1", parent: "d" } },
{ data: { id: "e1", parent: "e" } },
{ data: { id: "f1", parent: "f" } },
{ data: { id: "a2", parent: "a" } },
{ data: { id: "b2", parent: "b" } },
{ data: { id: "c2", parent: "c" } },
{ data: { id: "d2", parent: "d" } },
{ data: { id: "e2", parent: "e" } },
{ data: { id: "f2", parent: "f" } },
{ data: { id: "a3", parent: "a" } },
{ data: { id: "b3", parent: "b" } },
{ data: { id: "c3", parent: "c" } },
{ data: { id: "d3", parent: "d" } },
{ data: { id: "e3", parent: "e" } },
{ data: { id: "f3", parent: "f" } },
{ data: { id: "a4", parent: "a" } },
{ data: { id: "b4", parent: "b" } },
{ data: { id: "c4", parent: "c" } },
{ data: { id: "d4", parent: "d" } },
{ data: { id: "e4", parent: "e" } },
{ data: { id: "f4", parent: "f" } },
{ data: { id: "a5", parent: "a" } },
{ data: { id: "b5", parent: "b" } },
{ data: { id: "c5", parent: "c" } },
{ data: { id: "d5", parent: "d" } },
{ data: { id: "e5", parent: "e" } },
{ data: { id: "f5", parent: "f" } },
{ data: { id: "a6", parent: "a" } },
{ data: { id: "b6", parent: "b" } },
{ data: { id: "c6", parent: "c" } },
{ data: { id: "d6", parent: "d" } },
{ data: { id: "e6", parent: "e" } },
{ data: { id: "f6", parent: "f" } }
];
var cy = window.cy = cytoscape({
container: document.getElementById("cy"),
ready: function () {
this.layout({
name: "fcose",
randomize: true,
fit: true,
animate: true,
nodeDimensionsIncludeLabels: true,
condense: false
}).run(20, 0, 10);
var api = this.expandCollapse({
layoutBy: {
name: "fcose",
animate: true,
randomize: false,
fit: true,
nodeDimensionsIncludeLabels: true,
condense: false
},
fisheye: true,
animate: true,
undoable: false
});
api.expandAll({
layoutBy: {
name: "fcose",
animate: true,
randomize: false,
fit: true,
nodeDimensionsIncludeLabels: true,
condense: false
},
fisheye: true,
animate: true,
undoable: false,
nodeDimensionsIncludeLabels: true,
condense: false
});
},
elements: myData,
style: [
{
selector: "node",
style: {
"label": (ele) => ele.data("label"),
"color": "blue",
"shape": "diamond"
}
},
{
selector: "node.cy-expand-collapse-collapsed-node",
style: {
"shape": "rectangle",
"border-color":"black",
"border-width":3,
"color":"blue"
}
},
{
selector: ":selected",
style: {
"overlay-color": "green",
"overlay-opacity": 0.3,
'background-color': 'green',
'shape': 'rectangle',
}
},
{
selector: ":parent",
style: {
"background-color": "yellow",
"z-compound-depth": "auto",
shape: "rectangle"
}
},
{
selector: "edge",
style: {
"target-arrow-shape": "triangle",
"target-endpoint": "outside-to-node",
"target-arrow-color": "lightgreen",
"line-color": "green",
"target-distance-from-node": 0
}
}
],
layout: { name: "fcose" },
style: [
{
selector: "edge",
style: {
"curve-style": "bezier",
"target-arrow-shape": "triangle"
}
}
],
wheelSensitivity: 0.5
});
var api = cy.expandCollapse("get");
document.getElementById("collapseAll").addEventListener("click", function () {
api.collapseAll();
});
document.getElementById("expandAll").addEventListener("click", function () {
api.expandAll();
});
document
.getElementById("collapseRecursively")
.addEventListener("click", function () {
api.collapseRecursively(cy.$(":selected"));
});
document
.getElementById("expandRecursively")
.addEventListener("click", function () {
api.expandRecursively(cy.$(":selected"));
});
document.getElementById("compound").addEventListener("click", function () {
var selection = cy.nodes(":selected");
if (selection.length < 1) {
return;
}
var parent = selection[0].parent().id();
for (let i = 1; i < selection.length; i++) {
if (parent !== selection[i].parent().id()) {
return;
}
}
var myUUID = new UUID(4);
const parentNode = {
data: { id: `${myUUID}`, parent: parent, label: "label" }
};
cy.add(parentNode);
for (let i = 0; i < selection.length; i++) {
selection[i].move({ parent: `${myUUID}` });
}
api.expandAll();
});
</script>
</html>
Related
I have a collection of elements (created by using pathTo from the dijkstra function).
I need to reverse elements of this collection, for instance I want to transform
[14, 2, 37, 4, 5] into [5, 4, 37, 2, 14].
I tried using the filter function as per below but to no success. Does anyone know how to reverse a collection (not an array)?
path = dijkstraDiverse.pathTo(cy.$id('5'));
newpath = path.filter(function(ele, i, eles) {
return eles[path.length - 1 - i];
});
I used the sort function and just reversed the collection that way. The important part is using -1 as the sorting metric, that way everything is simply reversed. I added some visual clarification derived from this BFS example:
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
boxSelectionEnabled: false,
autounselectify: true,
style: [{
selector: "node",
css: {
content: "data(id)",
"text-valign": "center",
"text-halign": "center",
"height": "60px",
"width": "60px",
"border-color": "black",
"border-opacity": "1",
"border-width": "10px"
}
},
{
selector: "edge",
css: {
"label": "data(weight)",
"target-arrow-shape": "triangle"
}
},
{
selector: ".highlight",
css: {
'background-color': '#61bffc',
'line-color': '#61bffc',
'target-arrow-color': '#61bffc',
'transition-property': 'background-color, line-color, target-arrow-color',
'transition-duration': '0.5s'
}
},
{
selector: ".old",
css: {
'background-color': '#ff6e63',
'line-color': '#ff6e63',
'target-arrow-color': '#ff6e63',
'transition-property': 'background-color, line-color, target-arrow-color',
'transition-duration': '0.5s'
}
}
],
elements: {
nodes: [{
data: {
id: "n0"
}
},
{
data: {
id: "n1"
}
},
{
data: {
id: "n2"
}
},
{
data: {
id: "n3"
}
},
{
data: {
id: "n4"
}
},
{
data: {
id: "n5"
}
},
{
data: {
id: "n6"
}
},
{
data: {
id: "n7"
}
},
{
data: {
id: "n8"
}
},
{
data: {
id: "n9"
}
},
{
data: {
id: "n10"
}
},
{
data: {
id: "n11"
}
},
{
data: {
id: "n12"
}
},
{
data: {
id: "n13"
}
},
{
data: {
id: "n14"
}
},
{
data: {
id: "n15"
}
},
{
data: {
id: "n16"
}
}
],
edges: [{
data: {
source: "n0",
target: "n1",
weight: 1
}
},
{
data: {
source: "n1",
target: "n2",
weight: 11
}
},
{
data: {
source: "n1",
target: "n3",
weight: 12
}
},
{
data: {
source: "n2",
target: "n7",
weight: 2
}
},
{
data: {
source: "n2",
target: "n11",
weight: 3
}
},
{
data: {
source: "n2",
target: "n16",
weight: 1
}
},
{
data: {
source: "n3",
target: "n4",
weight: 32
}
},
{
data: {
source: "n3",
target: "n16",
weight: 7
}
},
{
data: {
source: "n4",
target: "n5",
weight: 6
}
},
{
data: {
source: "n4",
target: "n6",
weight: 4
}
},
{
data: {
source: "n6",
target: "n8",
weight: 11
}
},
{
data: {
source: "n8",
target: "n9",
weight: 12
}
},
{
data: {
source: "n8",
target: "n10",
weight: 1
}
},
{
data: {
source: "n11",
target: "n12",
weight: 1
}
},
{
data: {
source: "n12",
target: "n13",
weight: 2
}
},
{
data: {
source: "n13",
target: "n14",
weight: 3
}
},
{
data: {
source: "n13",
target: "n15",
weight: 5
}
}
]
},
layout: {
name: "dagre",
padding: 5
}
}));
cy.ready(function() {
const dijkstra = cy.elements().dijkstra(
"#n0",
function(edge) {
const weight = edge.data("weight");
return weight;
}
);
const oldPath = dijkstra.pathTo(cy.$("#n10"));
console.log(oldPath);
const newPath = oldPath.sort(function(a, b) {
return -1;
});
console.log(newPath);
let i = 0;
let j = 0;
let highlightPath = newPath;
let highlightNextEle = function() {
if (i < highlightPath.length) {
if (j == 0) {
highlightPath[i].addClass("highlight");
} else {
//highlightPath[i].removeClass("highlight");
highlightPath[i].addClass("old");
}
i++;
setTimeout(highlightNextEle, 1000);
} else if (i == highlightPath.length && j !== 1) {
i = 0;
j = 1;
highlightPath = oldPath;
setTimeout(highlightNextEle, 1000);
}
};
// kick off first highlight
highlightNextEle();
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 100%;
left: 0;
top: 0;
float: left;
position: absolute;
}
<html>
<head>
<meta charset=utf-8 />
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
<script src="https://unpkg.com/cytoscape#3.3.0/dist/cytoscape.min.js">
</script>
<script src="https://unpkg.com/jquery#3.3.1/dist/jquery.js"></script>
<!-- cyposcape dagre -->
<script src="https://unpkg.com/dagre#0.7.4/dist/dagre.js"></script>
<script src="https://cdn.rawgit.com/cytoscape/cytoscape.js-dagre/1.5.0/cytoscape-dagre.js"></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>
I am using cytoscape.js for my visualization project in which I have to show a Hierarchical structure with compound nodes.
So I initially used Cose-Bilkent layout which worked like a charm but the requirement is that all the child nodes of a parent must be in a single row. So I tried to tweak around a bit but couldn't get the exact result.
Then I tried to use grid layout by giving hardcoded row and column numbers and I got the exact result but as my data is dynamic I realized its difficult to assign row numbers and column numbers on my own.
Here is the data I used,
elements: [ // list of graph elements to start with
{ // node a
data: { id: 'X1', label: 'X1'}
},
{
data: { id: 'X2', label: 'X2'}
},
{
data: { id: 'X3', label: 'X3'}
},
{
data: { id: 'X4', label: 'X4'}
},
{
data: { id: 'X5', label: 'X5'}
},
{
data: { id: 'X6', label: 'X6'}
},
{
data: { id: 'X7', label: 'X7'}
},
{
data: { id: 'X8', label: 'X8'}
},
{
data: { id: 'X9', label: 'X9'}
},
{
data: { id: 'X10', label: 'X10'}
},
{
data: { id: 'X1e1',label: 'e1', parent: 'X1', row: '1' ,col: '1'}
},
{
data: { id: 'X1e5',label: 'e5', parent: 'X1', row: '1',col: '2'}
},
{
data: { id: 'X1e6',label: 'e6', parent: 'X1', row: '1',col: '3'}
},
{
data: { id: 'X2e2',label: 'e2', parent: 'X2', row: '3',col: '1'}
},
{
data: { id: 'X2e3',label: 'e3', parent: 'X2', row: '3',col: '2'}
},
{
data: { id: 'X3e4',label: 'e4', parent: 'X3', row: '4',col: '1'}
},
{
data: { id: 'X4e5',label: 'e5', parent: 'X4', row: '2',col: '1'}
},
{
data: { id: 'X4e6',label: 'e6', parent: 'X4', row: '2',col: '2'}
},
{
data: { id: 'X5e7',label: 'e7', parent: 'X5', row: '7',col: '1'}
},
{
data: { id: 'X6e8',label: 'e8', parent: 'X6', row: '5',col: '1'}
},
{
data: { id: 'X6e9',label: 'e9', parent: 'X6', row: '5',col: '2'}
},
{
data: { id: 'X7e10',label: 'e10', parent: 'X7', row: '7',col: '2'}
},
{
data: { id: 'X7e11',label: 'e11', parent: 'X7', row: '7',col: '3'}
},
{
data: { id: 'X7e12',label: 'e12', parent: 'X7', row: '7',col: '4'}
},
{
data: { id: 'X8e13',label: 'e13', parent: 'X8', row: '6',col: '1'}
},
{
data: { id: 'X8e14',label: 'e14', parent: 'X8', row: '6',col: '2'}
},
{
data: { id: 'X8e15',label: 'e15', parent: 'X8', row: '6',col: '3'}
},
{
data: { id: 'X8e16',label: 'e16', parent: 'X8', row: '6',col: '4'}
},
{
data: { id: 'X9e17',label: 'e17', parent: 'X9', row: '8',col: '1'}
},
{
data: { id: 'X10e18',label: 'e18', parent: 'X10', row: '8',col: '2'}
},
{
data: { id: 'X1e5X4e5', source:'X1e5', target:'X4e5'}
},
{
data: { id: 'X1e6X4e6', source:'X1e6', target:'X4e6'}
},
{
data: { id: 'X1e1X2', source:'X1e1', target:'X2'}
},
{
data: { id: 'X2e3X3', source:'X2e3', target:'X3'}
},
{
data: { id: 'X4e5X5', source:'X4e5', target:'X5'}
},
{
data: { id: 'X4e6X6', source:'X4e6', target:'X6'}
},
{
data: { id: 'X6X8e16', source:'X6', target:'X8e16'}
},
{
data: { id: 'X6e9X8', source:'X6e9', target:'X8'}
},
{
data: { id: 'X6e8X7', source:'X6e8', target:'X7'}
},
{
data: { id: 'X6X7e12', source:'X6', target:'X7e12'}
}
]
and layout
layout:{
name: 'grid',
fit: true,
position: function( node ){ return {row:node.data('row'), col:node.data('col') }}
}
And here is the result I got(and also expected) by setting manual rows and columns
Any help would be appreciated. Thanks
Well there are two extensions, which would achieve just what you need:
even-parents
no-overlap
Coincidentally, both come from the same person, so this should not be a problem at all, all you have to do from there is to apply the right styles for the application to look like your example:
document.addEventListener("DOMContentLoaded", function() {
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
layout: {
name: "evenParent"
},
style: [{
selector: "node",
style: {
"content": "data(id)",
"background-color": "#ad1a66"
}
},
{
selector: ":parent",
style: {
"background-opacity": 0.333
}
},
{
selector: "edge",
style: {
width: 3,
"line-color": "#ad1a66"
}
},
{
selector: "edge.meta",
style: {
width: 2,
"line-color": "red"
}
},
{
selector: ":selected",
style: {
"border-width": 3,
"border-color": "#DAA520"
}
}
],
elements: {
nodes: [{
data: {
id: "Jerry",
name: "Jerry"
}
},
{
data: {
id: "Elaine",
name: "Elaine"
}
},
{
data: {
id: "Kramer",
name: "Kramer"
}
},
{
data: {
id: "George",
name: "George"
}
},
{
data: {
id: "Martin",
name: "Martin"
}
},
{
data: {
id: "Philippe",
name: "Philippe"
}
},
{
data: {
id: "Louis",
name: "Louis"
}
},
{
data: {
id: "Genevieve",
name: "Genevieve"
}
},
{
data: {
id: "Leo",
name: "Leo"
}
},
{
data: {
id: "Larry",
name: "Larry"
}
},
{
data: {
id: "Logaina",
name: "Logaina"
}
}
],
edges: [{
data: {
source: "Jerry",
target: "Elaine"
}
},
{
data: {
source: "Jerry",
target: "Kramer"
}
},
{
data: {
source: "Jerry",
target: "George"
}
},
{
data: {
source: "Elaine",
target: "Martin"
}
},
{
data: {
source: "Elaine",
target: "Philippe"
}
},
{
data: {
source: "Elaine",
target: "Louis"
}
},
{
data: {
source: "Elaine",
target: "Genevieve"
}
},
{
data: {
source: "Elaine",
target: "Leo"
}
},
{
data: {
source: "Kramer",
target: "Larry"
}
},
{
data: {
source: "Kramer",
target: "Logaina"
}
}
]
}
}));
// demo your collection ext
cy.nodes().noOverlap({
padding: 5
});
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
}
<html>
<head>
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-even-parent#1.1.1/cytoscape-even-parent.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-no-overlap#1.0.1/cytoscape-no-overlap.min.js"></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>
The following code worked for me,
Please consider #Stephen's code if this doesn't work.
var cy = cytoscape({
container: /* your div within which you want to render */ ,
elements: [ /*list of graph elements to start with */ ] ,
style: [ /* the stylesheet for the graph */ ] ,
layout:{
name: 'cola',
fit: false,
infinite: false,
avoidOverlap: true
}
});
//Used to make child nodes stay on the same row
cy.ready(function(){
setTimeout(function(){
cy.zoom(0.5);
cy.nodes(':compound').forEach(function(comp,j,comps){
var nodePosition={};
if(comp.descendants().length>1)
{
var minX;
var maxY;
comp.descendants().forEach(function(ele,i,eles){
if(i==0)
{
minX=ele.renderedPosition('x');
maxY=ele.renderedPosition('y');
}
else
{
var tempX=ele.renderedPosition('x');
var tempY=ele.renderedPosition('y');
if(tempX<minX)
{
minX=tempX;
}
if(tempY>maxY)
{
maxY=tempY;
}
}
});
comp.descendants().forEach(function(ele,i,eles){
ele.renderedPosition({x:minX,y:maxY});
minX=minX+60;
});
}
cy.resize();
cy.fit();
cy.minZoom(cy.zoom());
});
},1000);
});
P.S: Don't forget to include the cytoscape scripts;)
I tried to create a inner and outer layer with the concentric layout.
But I don't get it to work.
This is how it looks for me:
The inner and outer nodes get mixed up.
And this is what I have expected:
My data:
{
data: [
{data: {
id: 'innerA', level: 1 },
group: 'nodes'
},
....
{data: {
id: 'outA', level: 2 },
group: 'nodes'
},
....
{
data: { source: 'innerA', target: 'outA' },
group: 'edges',
},
....
],
}
And my layout
layout: {
concentric: function( node ){
return node.data("level");
},
levelWidth: function( ){
return 2;
},
name: 'concentric',
minNodeSpacing: 50,
}
}
From what I have expected the concentric function in the layout, would assign the nodes
depending on the layer attribute in the node.
Hope someone has an idea.
Thank you in advance.
First of all, layout places higher level nodes towards the center, therefore your inner nodes should have level 2, while outer ones should have level 1. Secondly, I don't know how levelWidth option works, but using default value instead of 2 works.
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
style: [{
selector: 'node',
css: {
'content': 'data(id)'
}
},
{
selector: 'edge',
css: {
'curve-style': 'straight',
}
}
],
elements: [
{data: {
id: 'innerA', level: 2 },
group: 'nodes'
},
{data: {
id: 'innerB', level: 2 },
group: 'nodes'
},
{data: {
id: 'innerC', level: 2 },
group: 'nodes'
},
{data: {
id: 'innerD', level: 2 },
group: 'nodes'
},
{data: {
id: 'outA', level: 1 },
group: 'nodes'
},
{data: {
id: 'outB', level: 1 },
group: 'nodes'
},
{data: {
id: 'outC', level: 1 },
group: 'nodes'
},
{data: {
id: 'outD', level: 1 },
group: 'nodes'
},
{data: {
id: 'outE', level: 1 },
group: 'nodes'
},
{data: {
id: 'outF', level: 1 },
group: 'nodes'
},
{data: {
id: 'outG', level: 1 },
group: 'nodes'
},
{data: {
id: 'outH', level: 1 },
group: 'nodes'
},
{
data: { source: 'innerA', target: 'outA' },
group: 'edges',
},
{
data: { source: 'innerB', target: 'outC' },
group: 'edges',
},
{
data: { source: 'innerC', target: 'outE' },
group: 'edges',
},
{
data: { source: 'innerD', target: 'outG' },
group: 'edges',
},
],
layout: {
name: 'concentric',
concentric: function( node ){
return node.data("level");
},
minNodeSpacing: 50
}
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 95%;
width: 95%;
left: 0;
top: 0;
position: absolute;
}
<html>
<head>
<meta charset=utf-8 />
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
<script src="https://unpkg.com/cytoscape#3.10.0/dist/cytoscape.min.js">
</script>
</head>
<body>
<div id="cy"></div>
</body>
</html>
This is how I insert new data to kendo grid, however i want my added row have a custom class, so I can style my new added row with different background color. How can I achieve this ? I searching all the doc can't find any related
var dataSource = $('#grid').data('kendoGrid').dataSource;
dataSource.insert(0, {
"name":"ABC",
"age": 99
});
In order to add a new class to each newly row you can use .addClass(). But, every time you move to the next/prev page or add other rows you need to add again the class....
In order to achieve that you can save in a global array a list of all newly added row uuid and on dataBound event reapply the class.
Fiddle here
var newUUID = [];
$("#grid").kendoGrid({
navigatable: true,
filterable: true,
pageable: {
pageSize: 5,
alwaysVisible: false,
pageSizes: [5, 10, 20, 100]
},
dataBound: function(e) {
$.each(newUUID, function(idx, ele) {
if ($(ele).length != 0) {
$(ele).addClass('newRow');
}
})
}
});
$('#btn').on('click', function(e) {
var dataSource = $('#grid').data('kendoGrid').dataSource;
var x = dataSource.insert(0, {
"name":"ABC",
"age": 99
});
newUUID.push("[data-uid='" + x.uid + "']");
$("[data-uid='" + x.uid + "']").addClass('newRow');
})
You can look-up the newly added row by its UID and add the class to the row.
I explored the solution on this blog: "Simple Row Coloring in a KendoUI Grid"
const sampleData = getSampleData();
$(document).ready(() => {
$("#example-grid-wrapper").kendoGrid({
dataSource: {
data: sampleData.data,
schema: {
model: {
fields: sampleData.fields
}
}
},
columns: sampleData.columns
});
setTimeout(insertNewRecordAfterOneSecond, 1000);
});
function insertNewRecordAfterOneSecond() {
// Insert data
let dataGrid = $('#example-grid-wrapper').data('kendoGrid');
dataGrid.dataSource.insert(0, { id: 1, name: "Sam", location: "B", color: "blue", status: 0 });
// Re-scan table and lookup newly added row.
dataGrid = $('#example-grid-wrapper').data('kendoGrid');
let dataView = dataGrid.dataSource.view();
for (let i = 0; i < dataView.length; i++) {
if (dataView[i].id === 1) {
dataGrid.table.find("tr[data-uid='" + dataView[i].uid + "']").addClass("highlighted-row");
}
}
}
function getSampleData() {
return {
data : [
{ id: 2, name: "Grant", location: "A", color: "green", status: 1 },
{ id: 3, name: "Vaughan", location: "B", color: "red", status: 0 },
{ id: 4, name: "David", location: "A", color: "orange", status: 1 }
],
fields : {
id: { type: "number" },
name: { type: "string" },
location: { type: "string" },
color: { type: "string" }
},
columns : [
{ field: "id", title: "ID", width: "10%" },
{ field: "name", title: "Name", width: "30%" },
{ field: "location", title: "Location", width: "30%" },
{ field: "color", title: "Color", width: "20%" },
{ field: "status", title: "Status", width: "10%" }
]
};
}
.highlighted-row {
background: #FF0; /* Higlight row yellow */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="http://kendo.cdn.telerik.com/2019.2.619/js/kendo.all.min.js"></script>
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2019.2.619/styles/kendo.common.min.css" />
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2019.2.619/styles/kendo.blueopal.min.css" />
<div id="example-grid-wrapper">
<div id="example-grid"></div>
</div>
Alternative
As suggested by gaetanoM.
const sampleData = getSampleData();
var insertedUidList = [];
$(document).ready(() => {
$("#example-grid").kendoGrid({
dataSource: {
data: sampleData.data,
schema: {
model: {
fields: sampleData.fields
}
}
},
columns: sampleData.columns,
// Suggested by gaetanoM
dataBound: function(e) {
$.each(insertedUidList, function(idx, uid) {
// Re-apply class to existing rows.
$(`tr[data-uid="${uid}"]`).addClass('highlighted-row');
});
}
});
// Insert two rows, one second apart.
insertRowsWithDelay([
{ id: 0, name: "Joseph", location: "A", color: "yellow", status: 1 },
{ id: 1, name: "Sam", location: "B", color: "blue", status: 0 },
], 1000)
});
function insertRowsWithDelay(data, delayBetween) {
if (data == null || data.length === 0) return;
setTimeout(() => {
insertNewRecord(data.pop());
insertRowsWithDelay(data, delayBetween);
}, 1000);
}
function insertNewRecord(record) {
record = $('#example-grid').data('kendoGrid').dataSource.insert(0, record);
insertedUidList.push(record.uid);
$(`[data-uid="${record.uid}"]`).addClass('highlighted-row');
}
function getSampleData() {
return {
data : [
{ id: 2, name: "Grant", location: "A", color: "green", status: 1 },
{ id: 3, name: "Vaughan", location: "B", color: "red", status: 0 },
{ id: 4, name: "David", location: "A", color: "orange", status: 1 }
],
fields : {
id: { type: "number" },
name: { type: "string" },
location: { type: "string" },
color: { type: "string" }
},
columns : [
{ field: "id", title: "ID", width: "10%" },
{ field: "name", title: "Name", width: "30%" },
{ field: "location", title: "Location", width: "30%" },
{ field: "color", title: "Color", width: "20%" },
{ field: "status", title: "Status", width: "10%" }
]
};
}
.highlighted-row {
background: #FF0; /* Higlight row yellow */
}
.highlighted-row.k-alt {
background: #DD0; /* Higlight row yellow */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="http://kendo.cdn.telerik.com/2019.2.619/js/kendo.all.min.js"></script>
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2019.2.619/styles/kendo.common.min.css" />
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2019.2.619/styles/kendo.blueopal.min.css" />
<div id="example-grid"></div>
You can try to create a databound function for the grid and then try this inside the function
function onDataBound(e) {
var dataSource = $("#GridName").data("kendoGrid").dataSource;
var data = dataSource.data();
$.each(data, function (index, rowItem) {
if (data.length > 0) {
var rows = e.sender.tbody.children();
var row = $(rows[index]);
if (data[index].name == "ABC" ) {
row.addClass("NameColor");
}
}
});
}
<style>
.NameColor {
background-color: black;
}
</style>
Like this you can try..
I'm creating chart with nodes and edges. Once I created nodes, I can't create the associated edges without getting those kind of errors :
Can not create edge 5134fb65-b30f-4947-9870-cc909e293e21 with nonexistant source Peter
My code :
var myJSONdata = info;
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
boxSelectionEnabled: false,
autounselectify: true,
style: [
{
selector: 'node',
style: {
'content': 'data(id)',
'text-opacity': 0.5,
'text-valign': 'center',
'text-halign': 'right',
'shape': 'hexagon',
'label': 'data(label)',
'background-color': '#11479e'
}
},
{
selector: 'edge',
style: {
'curve-style': 'bezier',
'width': 4,
'target-arrow-shape': 'triangle',
'line-color': '#9dbaea',
'target-arrow-color': '#9dbaea'
}
}
],
// elements: {
// nodes: [
// { data: { id: 'Peter' } },
// { data: { id: 'Claire' } },
// { data: { id: 'Mike' } },
// { data: { id: 'Rosa' } }
// ],
// edges: [
// { data: { source: 'Peter', target: 'Claire' } },
// { data: { source: 'Claire', target: 'Mike' } },
// { data: { source: 'Mike', target: 'Rosa' } }
// ]
// }
});
var array = [];
// Create nodes
for (var i = 0; i <= myJSONdata.length - 1; i++) {
array.push({
group: 'nodes',
data: {
id: i,
label: myJSONdata[i].name
}
}
);
}
// Create edges
for (var i = 0; i <= myJSONdata.length - 1; i++) {
var source = myJSONdata[i].name;
array.push({
group: 'edges',
data: {
source: source,
target: myJSONdata[i].next_op_name
}
});
}
cy.add(array);
cy.layout({
name: 'circle'
}).run();
The "Create nodes" part is working, but the "Create edges" is not.
I tried the solution here but it does not work.
Actually I want to read data from JSON file to create the chart.
I can do it with :
elements: {
nodes: [
{ data: { id: 'Peter' } },
{ data: { id: 'Claire' } },
{ data: { id: 'Mike' } },
{ data: { id: 'Rosa' } }
],
edges: [
{ data: { source: 'Peter', target: 'Claire' } },
{ data: { source: 'Claire', target: 'Mike' } },
{ data: { source: 'Mike', target: 'Rosa' } }
]
}
But I want to automate it according to the JSON file in input.
This is my JSON file :
info = [
{
"name": "Peter",
"next_op_name": "Claire",
}, {
"name": "Claire",
"next_op_name": "Mike",
}, {
"name": "Mike",
"next_op_name": "Rosa",
}, {
"name": "Rosa",
"next_op_name": "Peter",
}
];
I can't understand what is wrong.
The source and target fields in the edge are the IDs of nodes, not labels.
When you create the nodes, you need to set id to myJSONdata[i].name as well.