In vis.js, i can add easily an edge (ex function test) but i can't remove an existing edge (ex function test 1). Why ?
Unfortunately, i can't access now to documentation : "There isn't a GitHub Pages site here" for visjs.org. Thanks !
<input onclick=test() type="button" value="add edge 1 to 4 : ok">
<input onclick=test1() type="button" value="remove edge 1 to 2: not ok">
<div id="mynetwork"></div>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.21.0/vis.js"></script>
<script type="text/javascript">
function test(){
data.edges.add([{from: 1, to: 4}]);
}
function test1(){
data.edges.remove([{from: 1, to: 2}]);
}
// GENERAL TEST
var nodes = [
{id: 1, label: 'Node 1'},
{id: 2, label: 'Node 2'},
{id: 3, label: 'Node 3'},
{id: 4, label: 'Node 4'}
];
var edges = [
{from: 1, to: 2},
{from: 1, to: 3},
{from: 2, to: 4}
];
nodesd = new vis.DataSet(nodes);
edgesd = new vis.DataSet(edges);
var container = document.getElementById('mynetwork');
var data = {
nodes: nodesd,
edges: edgesd
};
var options = {
nodes: {
size:15
},
edges: {
color: {
inherit: "both"
}
}
};
network = new vis.Network(container, data, options);
</script>
It's ok now !
I can do it with "id" for edges :
var edges = [
{id:1, from: 1, to: 2},
{id:2, from: 1, to: 3},
{id:3, from: 2, to: 4},
{id:4, from: 2, to: 5}
];
function test1(){
data.edges.remove([1]);
}
Here's an updated solution for anyone in the future that's stuck.
Let's say you initialized the network like so:
let network = new vis.Network(...);
let NODE_ID_1 = ...;
let NODE_ID_2 = ...;
Add a node connection:
network.body.data.edges.add({
'from': NODE_ID_1,
'to': NODE_ID_2
});
Remove a node connection:
let edges = network.getConnectedEdges(NODE_ID_1);
for(let edge of edges) {
if(NODE_ID_2 == network.body.edges[edge].to.id) {
network.body.data.edges.remove(edge);
break;
}
}
Helpful link: https://github.com/visjs/vis-network/blob/master/lib/network/modules/EdgesHandler.js
Related
I could not understand why this simple network configuration keeps spinning around node 2, except after some nudges around 30s mark in this screen cast, after which it restarts spinning. The setup uses visjs network module with forceatlas2 resolver.
My options param for the Network constructor is as follows:
get options(): Options {
return (
this.optionS || {
nodes: {
shape: 'dot',
size: 30,
font: {
size: 32
},
borderWidth: 2,
shadow: true
},
edges: {
width: 2,
shadow: true,
smooth: {
enabled: true,
roundness: 0.5,
type: 'cubicBezier',
forceDirection: 'vertical'
}
},
physics: {
forceAtlas2Based: {
avoidOverlap: 0.25,
gravitationalConstant: -95,
centralGravity: 0.01,
springLength: 100,
springConstant: 0.19,
nodeDistance: 175,
damping: 0.11
},
minVelocity: 0.75,
solver: 'forceAtlas2Based'
}
}
);
}
The host angular component provides these 5 nodes:
const nodes = new DataSet([
{ id: 1, label: 'Node 1' },
{ id: 2, label: 'Node 2' },
{ id: 3, label: 'Node 3' },
{ id: 4, label: 'Node 4' },
{ id: 5, label: 'Node 5' }
]);
const edges = new DataSet([
{ from: 1, to: 3 },
{ from: 1, to: 2 },
{ from: 2, to: 4 },
{ from: 2, to: 5 }
]);
this.graphData = { nodes, edges };
The network directive simply instantiates the Network as follows:
this.network = new Network(
this.el.nativeElement,
this.graphData,
this.options
);
Any insight into why this sort of perennial motion happens would be appreciated. I need to understand what factors to keep in mind for generating 'stable' nodes so that users do not have to keep chasing nodes/edges to click/interact with.
Increase minVelocity or damping to stop this.
The way you configured it the physics actually never stops moving the nodes around. Nodes 1, 2, 4 and 5 are kept in star arrangement by central gravity. Node 3 then pushes node 1 away but since everything's connected it ends up moving all of the nodes. Thanks to the edge between node 1 and 3 the force is stronger in one direction than the other. This then applies never ending force on the whole arrangement which ends up slowly spinning around node 2.
Faster spinner:
Is it possible to draw line graph on timeline, using visjs?
I want to get something like here http://almende.github.io/chap-links-library/js/timeline/examples/example19_bar_graph.html but instead of bar graph, I need line graph.
Essentially the question is, is it possible to get timeline and graph2d on the same canvas like in example below?
With the help of the demo of syncing to timelines here and some experimenting of my own, the following seems to do something similar. You could probably tweak it a bit more to get what you want.
The console warnings of "WARNING: infinite loop in redraw?" when zooming on the graph2d part appear to be a bug in vis.js in this scenario, as it doesn't occur when zooming on the timeline - in any case, it doesn't affect the functionality
// create a couple of HTML items in various ways
var item1 = document.createElement('div');
item1.appendChild(document.createTextNode('item 1'));
var item2 = document.createElement('div');
item2.innerHTML = '<span>item 2</span>';
var item3 = document.createElement('div');
var span3 = document.createElement('span');
span3.className = 'large';
span3.appendChild(document.createTextNode('item 3'));
item3.appendChild(span3);
var item4 = 'item <span class="large">4</span>';
var item5 = document.createElement('div');
item5.appendChild(document.createTextNode('item 5'));
item5.appendChild(document.createElement('br'));
var img5 = document.createElement('img');
img5.src = 'https://d30y9cdsu7xlg0.cloudfront.net/png/511-200.png';
img5.style.width = '48px';
img5.style.height = '48px';
item5.appendChild(img5);
var item6 = 'item6<br><img src="https://lasindias.com/wp-content/uploads/2013/11/Dominio-Publico.png" style="width: 48px; height: 48px;">';
var item7 = 'item7<br>click here';
// create data and a Timeline
var graph_container = document.getElementById('visualization-top-row');
var event_container = document.getElementById('visualization-bottom-row');
var items_graph = [
{x: '2013-04-20', y: 10},
{x: '2013-04-14', y: 25},
{x: '2013-04-18', y: 30},
{x: '2013-04-16', y: 10},
{x: '2013-04-25', y: 15},
{x: '2013-04-27', y: 30},
{x: '2013-04-21', y: 30}
];
var items_bottom_row = new vis.DataSet([
{id: 1, content: item1, start: '2013-04-20', group: 0},
{id: 2, content: item2, start: '2013-04-14', group: 0},
{id: 3, content: item3, start: '2013-04-18', group: 0},
{id: 4, content: item4, start: '2013-04-16', end: '2013-04-19', group: 0},
{id: 5, content: item5, start: '2013-04-25', group: 0},
{id: 6, content: item6, start: '2013-04-27', group: 0},
{id: 7, content: item7, start: '2013-04-21', group: 0}
]);
var groupsBottomRow = new vis.DataSet();
groupsBottomRow.add({id: 0, content: "Cool and ze Gang"});
var dataset_graph = new vis.DataSet(items_graph);
var options2 = {
start: '2013-04-12',
end: '2013-04-22',
height: '100%',
};
var graph2d = new vis.Graph2d(graph_container, dataset_graph, options2);
var timeline = new vis.Timeline(event_container);
timeline.setGroups(groupsBottomRow);
timeline.setOptions(options2);
timeline.setItems(items_bottom_row);
function onChangeGraph(range) {
if (!range.byUser) {
return;
}
timeline.setOptions({
start: range.start,
end: range.end,
height: '100%',
});
}
function onChangeTimeline(range) {
if (!range.byUser) {
return;
}
graph2d.setOptions({
start: range.start,
end: range.end,
height: '100%'
});
}
// graph2d.on('rangechanged', onChangeGraph);
// timeline.on('rangechanged', onChangeTimeline);
graph2d.on('rangechange', onChangeGraph);
timeline.on('rangechange', onChangeTimeline);
graph2d.on('_change', function() {
visLabelSameWidth();
});
$(window).resize(function(){
visLabelSameWidth();
});
// Vis same width label.
function visLabelSameWidth() {
var ylabel_width = $("#visualization-bottom-row .vis-labelset .vis-label").width() + "px";
//$("#visualization-top-row")[0].childNodes[0].childNodes[2].style.left = ylabel_width;
var w1 = $("#visualization-top-row .vis-content .vis-data-axis").width();
var w2 = $("#visualization-bottom-row .vis-labelset .vis-label").width();
$("#visualization-top-row")[0].childNodes[0].childNodes[2].style.display = 'none';
if (w2 > w1) {
$("#visualization-top-row .vis-content")[1].style.width = ylabel_width;
}
else {
$("#visualization-bottom-row .vis-labelset .vis-label").width(w1+"px");
}
}
body, html {
font-family: arial, sans-serif;
font-size: 11pt;
}
span {
color: red;
}
span.large {
font-size: 200%;
}
#visualization-bottom-row, #visualization-top-row {
height: 100%;
}
.outer-top-row {
height: 200px;
}
.outer-bottom-row {
height: 300px;
}
#visualization-top-row .vis-panel.vis-bottom {
display: none;
}
#visualization-top-row .vis-timeline{
border-bottom: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.17.0/vis-timeline-graph2d.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vis/4.17.0/vis-timeline-graph2d.min.css" rel="stylesheet"/>
<div class="outer-top-row">
<div id="visualization-top-row"></div>
</div>
<div class="outer-bottom-row">
<div id="visualization-bottom-row"></div>
</div>
Sadly what you have described is not possible. The closest solution I have found is to have a both a graph2d and timeline on the same page and listen to the drag events on each and update the viewport on the other such that they are both showing the same timespan and zoom level.
This is the structure of nested array:
$scope.History = [
{
isCustomer:false,
userText:"some text",
options:[]
}
The array with data:
//The text in DisplayLabel will be the same for all the objects.
$scope.Categories = [
{ "QuestionId": 1, "QuestionName": "Complaint", "DisplayLabel": "what is it?" },
{ "QuestionId": 2, "QuestionName": "Registration", "DisplayLabel": "what is it?" }
];
Desired Array:
$scope.History = [
{
isCustomer:false,
userText:"what is it?",
options:["Complaint","Registration"]
}
Normally I would do this for a 1D array
angular.forEach($scope.Categories, function (value, key) {
$scope.History.push(false,value.DisplayLabel);
Now, how to add items to 'options' for an object.
EDIT:
I tried this but no luck. Doesn't display any data
var optionsData = [];
var userText = "";
angular.forEach($scope.Categories, function (value, key) {
optionsData.push(value.QuestionName);
userText = value.DisplayLabel;
})
$scope.History.push(false,userText,optionsData);
var app = angular.module('app', []);
app.controller('homeCtrl', function($scope) {
$scope.History = [{
isCustomer: false,
userText: "some text",
options: []
}]
$scope.Categories = [{
"QuestionId": 1,
"QuestionName": "Complaint",
"DisplayLabel": "what is it?"
}, {
"QuestionId": 2,
"QuestionName": "Registration",
"DisplayLabel": "what is it?"
}];
var optionsData = []
angular.forEach($scope.Categories, function(value, key) {
$scope.History[0].options.push(value.QuestionName)
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<div ng-app="app">
<div ng-controller="homeCtrl">
<pre>{{History | json}}</pre>
</div>
</div>
i just want to change the default background color of a grid row after it had been selected and when i press a button. I'm using sencha extjs 4.2.
Anyone could help me please?
Thank you in advance
Task can be solved using column renderer: http://jsfiddle.net/bLgSA/10/
var button = Ext.create('Ext.button.Button', {
text: 'btn',
listeners: {
click: function() {
var sm = grid.getSelectionModel();
if (sm.hasSelection()) {
var rec = sm.getSelection()[0];
if (rec.get("color") == "black")
rec.set("color", "red");
else
rec.set("color", "black");
}
}
}
});
var renderer = function(value, metaData, record) {
metaData.style = 'color:'+record.get("color");
return value;
}
var grid = Ext.create('Ext.grid.Panel', {
height: 300,
width: 400,
title: 'hello',
columns: [
{text: 'c1', dataIndex: 'id', renderer: renderer},
{text: 'c2', dataIndex: 'label', renderer: renderer}
],
store: Ext.create('Ext.data.Store', {
fields: ['id', 'label', 'color'],
data: [
{id: 0, label: 'a', color: 'black'},
{id: 1, label: 'b', color: 'red'}
]
})
});
grid.render(Ext.getBody());
button.render(Ext.getBody());
This is the way to select a one row at a time and highlight as selected row to add css class
function rowHighlight()
{
$('#facCodes tr').click(function(event){
$(this).addClass("click").siblings().removeClass("click");
});
}
I need to display items as couples. Like this for example:
Template.container.couples = function() {
var items = Items.find({}, {sort: {sort_field: 1}}).fetch();
var couples = [];
for (var i = 0; i < items.length; i++) {
couples.push({
itemA: items[i],
itemB: items[i + 1]
});
i++;
}
return couples;
};
<template name="container">
<ul>
{{#each couples}}
<li>
<p class="item-a">{{>item itemA}}</p>
<span>|</span>
<p class="item-b">{{>item itemB}}</p>
</li>
{{/each}}
<ul>
</template>
<template name="item">
<strong>{{title}}</strong>
</template>
Items look like:
{
sort_field: 1,
title: 'Item 1',
type: 'A'
},
{
sort_field: 2,
title: 'Item 2',
type: 'B'
},
{
sort_field: 3,
title: 'Item 3',
type: 'A'
},
{
sort_field: 4,
title: 'Item 4',
type: 'B'
},
{
sort_field: 5,
title: 'Item 5',
type: 'A'
}
This code works good, but when I update title for one of items then all items rerender.
How to fix it? How to create this kind of layout with reactivity?
As of Meteor 0.8.0, Meteor will automagically only re-render content that changes.