Conditionnal styles for cytoscape.js graph - javascript

I want to change the style of my graph according to global Javascript variables. For exemple, assumung my edges got name and price attributes, I would like to make the labels of edges different, depending on a global label_type variable :
let lable_type = 'I_want_name_labels'
switch(lable_type) {
case 'I_want_name_labels':
cy.style().selector('edge').style({'label': 'data(name)'});
break;
case 'I_want_price_labels':
cy.style().selector('edge').style({'label': 'data(price)'});
break;
}
The above code does not do anything at all (no label displayed), I do not really understand why.
My edges have the following structure :
{
"data": {
"id": "node_node2",
"source": "node1",
"target": "node2",
"directed": true,
"name": "Baltazar",
"price": 1095.73
}
}
Note : I tried using cy.filter('edge').style({'label': 'data(name)'}) instead, but then data does not seems accessible this way, I got this warning :
The style property `label: data(name)` is invalid
So, how to get conditionnal styling with cytoscape.js ? What am I missing here ?

Here is the line you are looking for:
// .data() gets you all properties of the target element, .id() for example directly the id of the element
targetElement.style('label', targetElement.data('faveColor'));
Here is a working demo on how to initialize and then alter the nodes/edges label:
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
boxSelectionEnabled: false,
autounselectify: true,
style: [{
selector: "node",
css: {
"label": "data(id)",
"text-valign": "center",
"text-halign": "center",
"height": "60px",
"width": "100px",
"shape": "rectangle",
"background-color": "data(faveColor)"
}
},
{
selector: "edge",
css: {
"curve-style": "bezier",
"control-point-step-size": 40,
"target-arrow-shape": "triangle"
}
}
],
elements: {
nodes: [{
data: {
id: "Top",
faveColor: "#2763c4",
wants: "id"
}
},
{
data: {
id: "yes",
faveColor: "#37a32d",
wants: "id"
}
},
{
data: {
id: "no",
faveColor: "#2763c4",
wants: "id"
}
},
{
data: {
id: "Third",
faveColor: "#2763c4",
wants: "color"
}
},
{
data: {
id: "Fourth",
faveColor: "#56a9f7",
wants: "color"
}
}
],
edges: [{
data: {
source: "Top",
target: "yes"
}
},
{
data: {
source: "Top",
target: "no"
}
},
{
data: {
source: "no",
target: "Third"
}
},
{
data: {
source: "Third",
target: "Fourth"
}
},
{
data: {
source: "Fourth",
target: "Third"
}
}
]
},
layout: {
name: "dagre"
}
}));
cy.bind('click', 'node, edge', function(event) {
cy.nodes().each(function(ele, i, eles) {
ele.style('label', (ele.data('wants') == 'id') ? ele.data('id') : ele.data('faveColor'));
});
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 85%;
width: 100%;
float: right;
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>
<!-- 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>

Related

can parent node support other shape?

I need to define the parent node shape custom, but if I assign the shape as usual, the shape remains rectangular (instead of a star shape like in the example):
{
selector: 'node:parent',
style: {
'background-color': 'lightgrey',
'shape': 'star',
'border-color': 'cyan',
'border-radius': '50',
'background-fill': 'radial-gradient',
}
}
Desired round parent shape:
As described in this GitHub Issue, cytoscape.js does not support all node shapes for parent elements due to problems with the calculation of the parents BoundingBox.
The only supported shapes are:
rectangle
cutrectangle
roundrectangle
Here is an example for this feature:
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
style: [{
selector: "node",
css: {
content: "data(id)",
"text-valign": "center",
"text-halign": "center",
height: "60px",
width: "60px"
}
},
{
selector: ':parent',
css: {
//shape: 'rectangle',
shape: 'cutrectangle',
//shape: 'roundrectangle',
}
},
{
selector: "edge",
css: {
label: "\u2B24",
"curve-style": "bezier",
"target-arrow-shape": "data(arrow)"
}
},
{
selector: ".selectedNode",
style: {
"border-width": 8,
"border-color": "#5da963"
}
}
],
elements: {
nodes: [{
data: {
id: "n0",
parent: "n4"
}
},
{
data: {
id: "n1",
parent: "n5"
}
},
{
data: {
id: "n2",
parent: "n5"
}
},
{
data: {
id: "n3",
parent: "n5"
}
},
{
data: {
id: "n4",
parent: "n5"
}
},
{
data: {
id: "n5"
}
}
],
edges: [{
data: {
source: "n0",
target: "n1",
arrow: "triangle"
}
},
{
data: {
source: "n1",
target: "n2",
arrow: "triangle"
}
},
{
data: {
source: "n1",
target: "n3",
arrow: "triangle"
}
}
]
},
layout: {
name: "concentric",
minNodeSpacing: 140
}
}));
cy.unbind("click");
cy.bind("click", "node", evt => {
cy.elements().removeClass("selectedNode");
evt.target.addClass("selectedNode");
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 100%;
left: 0;
top: 0;
float: left;
position: absolute;
}
.cxtmenu-disabled {
opacity: 0.333;
}
<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://cdn.jsdelivr.net/npm/cytoscape#3.10.1/dist/cytoscape.min.js"></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>

Change edge label after graph was rendered

I'd like to change the label of a few specific edges after a certain event.
What would be the best way to do so?
Do I need to remove these edges, change the label tag and then add them again to cytoscape?
Best way would be something like "refresh graph".
Thanks in advance!
Here is a simple way to edit the label of any cy element:
// unbind first to prevent issues with binding conflicts
cy.unbind('click');
// change label of node to new text
cy.bind('click', 'node', function (evt) {
var target = evt.target;
target.data('label', 'new node label');
});
// change label of node to new text
cy.bind('click', 'edge', function (evt) {
var target = evt.target;
target.data('label', 'new edge label');
});
Here is a working demonstration:
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
boxSelectionEnabled: false,
autounselectify: true,
style: [{
selector: "node",
css: {
"label": "data(label)",
"text-valign": "center",
"text-halign": "center",
"height": "60px",
"width": "60px"
}
},
{
selector: "edge",
css: {
"target-arrow-shape": "triangle"
}
},
{
selector: "edge[label]",
css: {
label: "data(label)",
"text-rotation": "autorotate",
"text-margin-x": "0px",
"text-margin-y": "0px"
}
}
],
elements: {
nodes: [{
data: {
id: "Peter",
label: "Peter"
}
},
{
data: {
id: "Claire",
label: "Claire"
}
},
{
data: {
id: "Mike",
label: "Mike"
}
},
{
data: {
id: "Rosa",
label: "Rosa"
}
}
],
edges: [{
data: {
source: "Peter",
target: "Claire",
label: "edge 01"
}
},
{
data: {
source: "Claire",
target: "Mike",
label: "edge 02"
}
},
{
data: {
source: "Mike",
target: "Rosa",
label: "edge 03"
}
},
{
data: {
source: "Rosa",
target: "Peter",
label: "edge 04"
}
}
]
},
layout: {
name: "circle"
}
}));
// This is the important part
cy.unbind('click');
cy.bind('click', 'node', function(evt) {
var target = evt.target;
target.data('label', 'new node label');
});
cy.bind('click', 'edge', function(evt) {
var target = evt.target;
target.data('label', 'new edge label');
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 75%;
position: absolute;
left: 0;
top: 0;
float: left;
}
<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>
</head>
<body>
<div id="cy"></div>
</body>
</html>
All you have to do now is to call the .data() function on the edges array (one by one).

Cytoscape, how to highlight the clicked edge?

I'm trying to get the clicked edge so to get the highlight the edge.connectedNodes().
The issue that I've found is:
even if I'm able to store the edge id using the
cy.on('tap', function (event) {
if (event.target.isEdge()) {
selectedEdgeId = event.target.id;
}
}
using
cy.edges("#" + vm.selectedEdgeId);
I get only data with length equals to 0, i.e. ca {length: 0, _private{…}}
Any suggestion on how to get the clicked edge so to get the connectedNodes()?
Event.target is the clicked edge itself, so you don't need to save the id or anything. I would suggest this approach:
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: "100px",
shape: "rectangle",
"background-color": "data(faveColor)"
}
},
{
selector: ".highlight",
css: {
"background-color": "red"
}
},
{
selector: "edge",
css: {
"curve-style": "bezier",
"control-point-step-size": 40,
"target-arrow-shape": "triangle"
}
}
],
elements: {
nodes: [{
data: {
id: "Top",
faveColor: "#2763c4"
}
},
{
data: {
id: "yes",
faveColor: "#37a32d"
}
},
{
data: {
id: "no",
faveColor: "#2763c4"
}
},
{
data: {
id: "Third",
faveColor: "#2763c4"
}
},
{
data: {
id: "Fourth",
faveColor: "#56a9f7"
}
}
],
edges: [{
data: {
source: "Top",
target: "yes"
}
},
{
data: {
source: "Top",
target: "no"
}
},
{
data: {
source: "no",
target: "Third"
}
},
{
data: {
source: "Third",
target: "Fourth"
}
},
{
data: {
source: "Fourth",
target: "Third"
}
}
]
},
layout: {
name: "dagre"
}
}));
// bind tapstart to edges and highlight the connected nodes
cy.bind('tapstart', 'edge', function(event) {
var connected = event.target.connectedNodes();
connected.addClass('highlight');
});
// bind tapend to edges and remove the highlight from the connected nodes
cy.bind('tapend', 'edge', function(event) {
var connected = event.target.connectedNodes();
connected.removeClass('highlight');
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 100%;
float: right;
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>
<!-- 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>

Kendo grid insert new row with custom class

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..

How to have a node's name be different than its ID using Cytoscape.js?

I'm building a web application using Cytoscape.js that visualizes protein interaction data.
Proteins (nodes) need to have ID's corresponding to strings representing their chromosome locations, because this is universal. However, I would like them to be visualized or displayed alongside their common names, and not their ID's.
Any idea how to do this? The Cytoscape documentation doesn't seem to have an answer.
You can specify the nodes name in the data field. If you want to diplay that name as the nodes label, just use the field label: "data(name)":
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
boxSelectionEnabled: false,
autounselectify: true,
style: [{
selector: "node",
css: {
label: "data(name)", //access the nodes data with "data(...)"
// label: "data(id)",
"text-valign": "center",
"text-halign": "center",
height: "60px",
width: "100px",
shape: "rectangle",
"background-color": "data(faveColor)"
}
},
{
selector: "edge",
css: {
"curve-style": "bezier",
"control-point-step-size": 40,
"target-arrow-shape": "triangle"
}
}
],
elements: {
nodes: [{
data: {
id: "Top",
faveColor: "#2763c4",
name: "Steve"
}
},
{
data: {
id: "yes",
faveColor: "#37a32d",
name: "Larry"
}
},
{
data: {
id: "no",
faveColor: "#2763c4",
name: "Kiwi"
}
},
{
data: {
id: "Third",
faveColor: "#2763c4",
name: "Alex"
}
},
{
data: {
id: "Fourth",
faveColor: "#56a9f7",
name: "Vader"
}
}
],
edges: [{
data: {
source: "Top",
target: "yes"
}
},
{
data: {
source: "Top",
target: "no"
}
},
{
data: {
source: "no",
target: "Third"
}
},
{
data: {
source: "Third",
target: "Fourth"
}
},
{
data: {
source: "Fourth",
target: "Third"
}
}
]
},
layout: {
name: "dagre"
}
}));
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>
<!-- 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>
You can find this in the documents as well:
Mapping with data()
Label data field

Categories

Resources