I'm trying to use cytoscape.js to display a graph on my page and I'm having a hard time just getting a basic instance to display properly.
Code Breakdown:
I get the graph elements via an AJAX call, pass the elements into the cytoscape constructor, and display the instance in a Bootstrap modal.
Here's my JavaScript:
var cy;
$.ajax({
url : "getGraphElements",
data : {
str : variableToGetCorrectGraphData
},
success : function(data) {
var elementsJson = JSON.parse(data.elements);
console.log(elementsJson);
cy = cytoscape({
container : document.getElementById('cy'),
wheelSensitivity : 0.25,
elements : elementsJson,
style : [
{
selector: 'node',
style:
{
'background-color' : '#666',
label : 'data(id)'
}
},
{
selector: 'edge',
style:
{
'width' : 3,
'line-color' : '#737373',
'target-arrow-color' : '#737373',
'target-arrow-shape' : 'triangle',
'curve-style' : 'bezier'
}
}
],
layout : {
name: 'grid',
fit: true, // whether to fit the viewport to the graph
padding: 0, // padding used on fit
avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space
avoidOverlapPadding: 20, // extra spacing around nodes when avoidOverlap: true
nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm
condense: false, // uses all available space on false, uses minimal space on true
sort: function(a,b) { // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
return a.degree() - b.degree();
},
animate: false, // whether to transition the node positions
transform: function (node, position ){ return position; } // transform a given node position. Useful for changing flow direction in discrete layouts
}
});
$('#cyModal').modal('show');
}
});
});
Here's my Bootstrap Modal :
<div class="modal fade bd-example-modal-lg" id="cyModal" tabindex="-1" role="dialog" aria-labelledby="cyModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span> </button>
<h4 class="modal-title" id="cyModalLabel">Graph View</h4>
</div>
<div class="modal-body">
<div id="cy" style="height : 750px"></div>
</div>
</div>
</div>
</div>
And here's a sample of the JSON returned by the AJAX (although I'm almost certain this is not incorrect because all the elements appear to be present in the constructed graph) :
{ "nodes" : [{ "data" : { "id" : "12293"} }...], "edges" : [{ "data" : { "id" : "24607-26336", "source" : "24607", "target" : "26336" } }...] }
My Problem is that all the nodes appear stacked in the top left corner when the graph finishes initializing. I believe this is because the instance is using the null layout. A quick peek in the inspector console shows that cy.layout.name = 'layout'.
I just can't get it to initialize with the grid layout like I want it to. I've tried taking out the layout in the constructor, and using cy.layout({name : 'grid',}).run();. I've tried using both, one after the other. I've even tried putting cy.layout({name : 'grid',}).run(); in a while loop until cy.layout.name == 'grid' - that just caused the page to freeze. I've tried changing basically every option in both the Cytoscape initializer and the Layout initializer - no dice. What I find weird is that when I execute cy.layout({name : 'grid',}).run(); in the inspector console, the layout sets up properly...
If anyone has any ideas I'd greatly appreciate it!
Ok, OP here. I think my problem is with Bootstrap.
I started by noticing that I call $('#cyModal').modal('show'); after I've finished initializing the Cytoscape instance. I realized that before I call $('#cyModal').modal('show'); the div container for my Cytoscape instance has no size and is invisible. So I tried calling $('#cyModal').modal('show'); before initializing Cytoscape, but that still didn't work. Furthermore, I noticed that the modal wasn't actually showing until after Cytoscape initialized.
Apparently, for this sort of thing, I need to wait for the Bootstrap modal to fire the 'shown' event before setting my Cytoscape layout to be sure that the div is visible and has a size. So I put my layout setter in a listener for shown.bs.modal:
$('#cyModal').on('shown.bs.modal', function (e) {
cy.layout({
name: 'grid',
fit: true, // whether to fit the viewport to the graph
padding: 0, // padding used on fit
avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space
avoidOverlapPadding: 20, // extra spacing around nodes when avoidOverlap: true
nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm
condense: false, // uses all available space on false, uses minimal space on true
sort: function(a,b) { // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
return a.degree() - b.degree();
},
animate: false, // whether to transition the node positions
transform: function (node, position ){ return position; } // transform a given node position. Useful for changing flow direction in discrete layouts
}).run();
});
This works for me, but it feels a bit like a hack - why can't I just do it like I originally wanted to? If that's just the way the cookie crumbles with Cytoscape.js and Bootstrap - c'est la vie; but I'll take any other suggestions if anyone has a more 'natural feeling' solution.
I'm doing a project using Highcharts Gantt, and I'm having a little trouble mastering it at the moment, especially the management of its height and Y axis.
Here Sandbox : https://codesandbox.io/s/nice-microservice-fuv76?file=/src/GanttMain.jsx:334-352
Let me explain:
What I want to do is set a maximum height and use scrollablePlotArea to make the y-axis scroll, while keeping the X-axis header.
The problem is: if I define a minHeight in scrollablePlotArea, the Y axis cuts the events until the minimum height defined (see sandbox), if I increase minHeight, it will cut less events, but the number is dynamic, so impossible to put a fixed value...
My question is: How to define a maximum height, while keeping a Y scroll that displays all events, While not changing the line height?
I tried several possibilities with the documentation, but nothing works...
I hope I made myself understood...
Thank you very much for your help.
This problem is a bug in Highcharts Gantt and it is similar to this issue: https://github.com/highcharts/highcharts/issues/13884
As a workaround you can dynamically set minHeight for scrollable plot area, example:
let allowChartUpdate = true;
Highcharts.ganttChart('container', {
chart: {
animation: false,
scrollablePlotArea: {
minHeight: 450
},
events: {
render: function() {
if (allowChartUpdate) {
allowChartUpdate = false;
this.update({
chart: {
scrollablePlotArea: {
minHeight: ...
}
}
});
allowChartUpdate = true;
}
}
}
},
...
});
Live demo: http://jsfiddle.net/BlackLabel/ywt2cmkn/
API Reference: https://api.highcharts.com/gantt/chart.events.render
I'm trying to create a simple line chart using Vega chart library. My problem is that I can't make it to be responsive. I've started from the example that they provided, but I can't make the chart to dimension relative to the window size. Probably this is not an out of the box functionality of Vega chart library.
Could you help me to achieve this? Or do you have any suggestions on how can I create a Vega chart that is auto-resize based on the screen size?
Thanks in advance!
UPDATE:
Here is the changes that I did so far:
window.onresize = function (event) {
view.signal('width', event.target.innerWidth - 50)
.signal('height', event.target.innerHeight - 50)
.run('enter');
}
Link to fiddle: https://jsfiddle.net/TheoAbiel/ehtu6xkj/18/
This isn't quite ok and I need to improve it. Do you have other suggestions?
So long as there aren't certain interactions that depend on the math from the original view dimensions, or there aren't multiple nested charts this example should help.
Note: autosize:{type:'fit',resize:true} in the spec helps with multiple nested graphs, but causes some jumpy behavior on hover as seen in this example.
This snippet supports SVG & Canvas. There are also two specs to choose from.
Run the demo below and resize your browser! The responsiveness is based on the parent element, and in this case, the parent element is 50% of the body. Note that the DOM will be cleared after 10 seconds!
// const spec={$schema:'https://vega.github.io/schema/vega/v4.json',width:200,height:200,autosize:{type:'fit',resize:true,},signals:[{name:'startAngle',value:0,bind:{input:'range',min:0,max:6.29,step:0.01},},{name:'endAngle',value:6.29,bind:{input:'range',min:0,max:6.29,step:0.01},},{name:'padAngle',value:0,bind:{input:'range',min:0,max:0.1},},{name:'innerRadius',value:0,bind:{input:'range',min:0,max:90,step:1},},{name:'cornerRadius',value:0,bind:{input:'range',min:0,max:10,step:0.5},},{name:'sort',value:false,bind:{input:'checkbox'},},],data:[{name:'table',values:[{id:1,field:4},{id:2,field:6},{id:3,field:10},{id:4,field:3},{id:5,field:7},{id:6,field:8},],transform:[{type:'pie',field:'field',startAngle:{signal:'startAngle'},endAngle:{signal:'endAngle'},sort:{signal:'sort'},},],},],scales:[{name:'color',type:'ordinal',range:{scheme:'category20'}},],marks:[{type:'arc',from:{data:'table'},encode:{enter:{fill:{scale:'color',field:'id'},x:{signal:'width/2'},y:{signal:'height/2'},},update:{startAngle:{field:'startAngle'},endAngle:{field:'endAngle'},padAngle:{signal:'padAngle'},innerRadius:{signal:'innerRadius'},outerRadius:{signal:'width/2'},cornerRadius:{signal:'cornerRadius'},},},},],};
const spec={$schema:'https://vega.github.io/schema/vega/v4.json',width:400,height:200,autosize:{type:'fit',resize:true,},data:[{name:'table',values:[{category:'A',amount:28},{category:'B',amount:55},{category:'C',amount:43},{category:'D',amount:91},{category:'E',amount:81},{category:'F',amount:53},{category:'G',amount:19},{category:'H',amount:87},],},],signals:[{name:'tooltip',value:{},on:[{events:'rect:mouseover',update:'datum'},{events:'rect:mouseout',update:'{}'},],},],scales:[{name:'xscale',type:'band',domain:{data:'table',field:'category'},range:'width',padding:0.05,round:true,},{name:'yscale',domain:{data:'table',field:'amount'},nice:true,range:'height',},],axes:[{orient:'bottom',scale:'xscale'},{orient:'left',scale:'yscale'},],marks:[{type:'rect',from:{data:'table'},encode:{enter:{x:{scale:'xscale',field:'category'},width:{scale:'xscale',band:1},y:{scale:'yscale',field:'amount'},y2:{scale:'yscale',value:0},},update:{fill:{value:'steelblue'},},hover:{fill:{value:'red'},},},},{type:'text',encode:{enter:{align:{value:'center'},baseline:{value:'bottom'},fill:{value:'#333'},},update:{x:{scale:'xscale',signal:'tooltip.category',band:0.5},y:{scale:'yscale',signal:'tooltip.amount',offset:-2},text:{signal:'tooltip.amount'},fillOpacity:[{test:'datum===tooltip',value:0},{value:1},],},},},],};
// Vega object
const vegaDemo = {
renderType: 'svg', // 'canvas',
responsive: true,
widthHeightRatio: spec.width / spec.height,
wrapperElem: document.querySelector('#vega-view'),
};
// Create the view
vegaDemo.view = new vega.View(vega.parse(spec))
.renderer(vegaDemo.renderType)
.resize()
.width(vegaDemo.wrapperElem.offsetWidth)
.height(vegaDemo.wrapperElem.offsetWidth / vegaDemo.widthHeightRatio)
.initialize(vegaDemo.wrapperElem)
.run();
// If responsive
if (vegaDemo.responsive) {
if (vegaDemo.renderType === 'canvas') {
// For Canvas views
vegaDemo.resize = () => {
const width = vegaDemo.wrapperElem.offsetWidth;
vegaDemo.view
.width(width)
.height(width / vegaDemo.widthHeightRatio)
.run();
};
window.addEventListener('resize', vegaDemo.resize);
} else {
// For SVG views
function responsive() {
// Remove width/height attributes of Vega SVG and let viewBox handle the responsive behavior
const viewDom = vegaDemo.wrapperElem.firstElementChild;
viewDom.removeAttribute('width');
viewDom.removeAttribute('height');
viewDom.style.width = '100%';
}
responsive();
}
}
// Destroy before changing your view on a SPA app
vegaDemo.destroy = () => {
vegaDemo.view.finalize();
if (vegaDemo.resize) {
window.removeEventListener('resize', vegaDemo.resize);
} else if (vegaDemo.responsive instanceof MutationObserver) {
vegaDemo.responsive.disconnect();
}
vegaDemo.wrapperElem.innerHTML = ''; // Empty the wrapper if you aren't navigating away and simply want to remove the Vega view
};
// Destroy everything after 10 seconds
window.setTimeout(vegaDemo.destroy, 10000);
*,
*:before,
*:after {
box-sizing: border-box;
}
article {
width: 50%;
}
<!doctype html>
<html>
<head>
<title>Vega Responsive</title>
<script src="https://cdn.jsdelivr.net/npm/vega#4.0.0-rc.3"></script>
</head>
<body>
<p>Resize the browser!</p>
<ul>
<li>Specs taken from https://vega.github.io/vega/examples/.</li>
<li>This supports SVG & Canvas (See the vegaDemo.renderType prop).</li>
<li>This demo uses the wrapper's width...in this case, 50% of the body.</li>
<li>The demo will be destroyed and removed after 10 seconds.</li>
</ul>
<article>
<div id="vega-view"></div>
</article>
</body>
</html>
FYI: containerSize tied to an event in signals can help too. An example from Banu Prakash on https://groups.google.com/forum/#!topic/vega-js/YovF3RvlnRg may help.
http://jsfiddle.net/jc3rj681/2/
Using the plugin jQuery UI Layout, I have several different panes. When the user resizes the window to a small size, I would like to call a function that changes the minsize and size of the pane so that I can make it smaller with the window.
I can do this, but for the changes to apply, I must toggle closed and then toggle open the pane. This creates a lot of flickering and ends up being pretty messy looking. I only need this one pane to resize in this fashion.
QUESTION: Is there a way I can apply these layout changes without having to toggle the pane twice for them to apply?
Check out this Fiddle that I made: http://jsfiddle.net/jc3rj681/2/
In here, the changes to the "width" of the show/hide button don't get applied until you toggle the pane. If you can make this width change work without toggling, I'm sure it would solve my problem as well.
$("#eastToggle").click(function () {
testLayout.toggle('east');
});
$("#attempt").click(function () {
testLayout.options.east.spacing_closed = 20;
testLayout.options.east.spacing_open = 20;
});
I'm not sure if there is a callback function or any special utility method that'll do the trick.
But you can try something like the following (probably with a resize function that'll resize the panes manually) -
var testLayout = $('body').layout({
applyDefaultStyles: true,
east: {
spacing_closed: 100, //toggler width
spacing_open: 100,
togglerLength_closed: 200, //toggler height (length)
togglerLength_open: 200
}
});
$("#eastToggle").click(function() {
testLayout.toggle('east');
});
$("#attempt").click(function() {
resize('east', 20);
});
// A function to resize the tab
function resize(region, space) {
// Width of the new center pane
var newCenterWidth = parseInt($('body .ui-layout-center').css('width').split('px')[0]) + testLayout.options.east.spacing_closed - space;
// Change the options so they don't affect the layout when you expand / collapse again
testLayout.options.east.spacing_closed = space;
testLayout.options.east.spacing_open = space;
// Manually resize the panes
$('body .ui-layout-resizer-' + region).css('width', space);
$('body .ui-layout-center').css('width', newCenterWidth);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://layout.jquery-dev.net/lib/js/jquery.layout-latest.js"></script>
<div class="ui-layout-center">Center</div>
<div class="ui-layout-north">North</div>
<div class="ui-layout-south">South</div>
<div class="ui-layout-east">East</div>
<div class="ui-layout-west">West
<br/>
<br/>
<button id="eastToggle">Toggle East Panel</button>
<br/>
<br/>
<button id="attempt">Change Width?</button>
</div>
I'm trying to create a pie chart through the use of flot charts. I have successfully managed to create one with the following code:
HTML:
<div class="row">
<div class="col-md-6">
<div id="pie-chart"></div>
</div>
</div>
Javascript:
var data = [];
data[0] = { label: "Vertification successful", data: 9 };
data[1] = { label: "Vertification failed", data: 2 };
var series = Math.floor(Math.random() * 10) + 1;
$.plot($("#pie-chart"), data,
{
series: {
pie: {
show: true,
}
},
grid: { hoverable: true },
});
And it displays just fine.
The thing is, if I change the ID of the div element to "pie-chart1" (rather than "pie-chart")
and update the javascript accordingly:
$.plot($("#pie-chart1"), data,
I get the following error:
Uncaught Invalid dimensions for plot, width = 501, height = 0
What on earth could be causing this? I simply wanna rename the ID which apparently for some reason is impossible.
It's very likely that there is some CSS or possibly JS elsewhere on your site that expects the div to be called pie-chart. You need to ensure that it still applies to the new div. For example, if you had:
#pie-chart {
width: 400px;
height: 300px;
}
When you change the ID of the div, you need to update that reference too, or else the placeholder's height and width become undefined, which Flot cannot handle.
If your goal in adding that number is to create several charts, then you should use a class to apply the styles rather than an ID.