Created
December 17, 2014 00:38
-
-
Save zachguo/7037141c11a233327e34 to your computer and use it in GitHub Desktop.
Meteor JS: Reactive D3 Force Layout Graph (minimum example)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<head> | |
<title>d3test</title> | |
</head> | |
<body> | |
<h3>Meteor JS: Reactive D3 Force Layout Graph (minimum example)</h3> | |
<h4>Add few nodes and links first, or visualization won't show up.</h4> | |
<span>New Node</span> | |
<form id="newnode"> | |
<input type="text" name="title" placeholder="Node title here..." /> | |
<select name="nodetype" id="nodetype"> | |
<option value="type1">type1</option> | |
<option value="type2">type2</option> | |
</select> | |
<button type="submit">Create node!</button> | |
</form> | |
<span>New Link</span> | |
<form id="newlink"> | |
<select name="sourceid" id="sourceid"> | |
{{#each nodes}} {{> nodeoption}} {{/each}} | |
</select> | |
<select name="targetid" id="targetid"> | |
{{#each nodes}} {{> nodeoption}} {{/each}} | |
</select> | |
<select name="linktype" id="linktype"> | |
<option value="type1">type1</option> | |
<option value="type2">type2</option> | |
</select> | |
<button type="submit">Create link!</button> | |
</form> | |
<span>Delete Link</span> | |
<form id="dellink"> | |
<select name="linkitem" id="linkitem"> | |
{{#each links}} {{> linkoption}} {{/each}} | |
</select> | |
<button type="submit">Delete link!</button> | |
</form> | |
<span>Delete Node</span> | |
<form id="delnode"> | |
<select name="nodeitem" id="nodeitem"> | |
{{#each nodes}} {{> nodeoption}} {{/each}} | |
</select> | |
<button type="submit">Delete node!</button> | |
</form> | |
{{> graphvis}} | |
</body> | |
<template name="nodeoption"> | |
<option value="{{_id}}">{{_id}} - {{title}} - {{type}}</option> | |
</template> | |
<template name="linkoption"> | |
<option value="{{_id}}">{{_id}} - {{source}} - {{target}} - {{type}}</option> | |
</template> | |
<template name="graphvis"> | |
<svg id="graphvis"></svg> | |
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Nodes = new Mongo.Collection("nodes"); | |
Links = new Mongo.Collection("links"); | |
var LinksToD3Array = function(linksCol, nodesCol) { | |
var nodes = {}; | |
nodesCol.forEach(function(node) { | |
nodes[node._id] = node; | |
}); | |
var result = []; | |
linksCol.forEach(function(link) { | |
var tmp = { | |
source: nodes[link.source], | |
target: nodes[link.target], | |
type: link.type, | |
_id: link._id | |
}; | |
result.push(tmp); | |
}); | |
return result; | |
}; | |
if (Meteor.isClient) { | |
Template.body.helpers({ | |
nodes: function() { | |
return Nodes.find({}); | |
}, | |
links: function() { | |
return Links.find({}); | |
} | |
}); | |
Template.body.events({ | |
"submit #newnode": function(event) { | |
Nodes.insert({ | |
title: event.target.title.value, | |
type: event.target.nodetype.value | |
}); | |
}, | |
"submit #newlink": function(event) { | |
Links.insert({ | |
source: event.target.sourceid.value, | |
target: event.target.targetid.value, | |
type: event.target.linktype.value | |
}); | |
}, | |
"submit #delnode": function(event) { | |
Nodes.remove({ | |
_id: event.target.nodeitem.value | |
}); | |
}, | |
"submit #dellink": function(event) { | |
Links.remove({ | |
_id: event.target.linkitem.value | |
}); | |
} | |
}); | |
Template.graphvis.rendered = function() { | |
var width = window.innerWidth, | |
height = window.innerHeight; | |
var svg = d3.select("#graphvis") | |
.attr("width", width) | |
.attr("height", height); | |
var render = function(svg, nodesCol, linksCol) { | |
var nodes = nodesCol, | |
links = LinksToD3Array(linksCol, nodesCol); | |
var force = d3.layout.force() | |
.nodes(nodes) | |
.links(links) | |
.size([width, height]) | |
.linkDistance(100) | |
.charge(-600) | |
.start(); | |
// add links | |
var line = svg.selectAll("line") | |
.data(force.links()) | |
.enter().append("line") | |
// differentiate link color by type | |
.attr('stroke', function(d) { | |
if (d.type === 'type2') { | |
return 'gray'; | |
} else { | |
return 'black'; | |
} | |
}) | |
// dash link for 'type2' link type | |
.attr('stroke-dasharray', function(d) { | |
if (d.type === 'type2') { | |
return '0,2 2'; | |
} | |
}); | |
// add nodes | |
var circle = svg.selectAll("circle") | |
.data(force.nodes()) | |
.enter().append("circle") | |
.attr("r", 6) | |
.attr('fill', 'black') | |
.call(force.drag); | |
// tick | |
force.on("tick", function() { | |
circle.attr('cx', function(d) { | |
return d.x; | |
}) | |
.attr('cy', function(d) { | |
return d.y; | |
}); | |
line.attr('x1', function(d) { | |
return d.source.x; | |
}) | |
.attr('y1', function(d) { | |
return d.source.y; | |
}) | |
.attr('x2', function(d) { | |
return d.target.x; | |
}) | |
.attr('y2', function(d) { | |
return d.target.y; | |
}); | |
}); | |
}; | |
Deps.autorun(function() { | |
// fetch() may cause memory overhead when collection is deep and large | |
var nodesCol = Nodes.find({}).fetch(); | |
var linksCol = Links.find({}).fetch(); | |
render(svg, nodesCol, linksCol); | |
}); | |
}; | |
} | |
if (Meteor.isServer) { | |
Meteor.startup(function() { | |
// code to run on server at startup | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment