var graph = Vue.component('graph', {
template: '#graph-template',
props: {
data:Array,
},
attached: function(){
this.genGraph();
},
methods:{
genGraph:function(){
this.data.forEach(function(obj) {
root = obj;
root.x0 = h / 2;
root.y0 = 0;
this.updateGraph(root);
})
},
updateGraph: function(source) {
var i = 0;
var duration = d3.event && d3.event.altKey ? 5000 : 500;
var nodes = this._tree.nodes(root).reverse();
nodes.forEach(function(d) { d.y = d.depth * 180; });
var node = this._svg.selectAll("g.node")
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on("click", function(d) {
this.updateGraph(d);
});
nodeEnter.append("svg:circle")
.attr("r", 0)
.style("fill", function( d ) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeEnter.append('svg:foreignObject')
.attr("x", -3)
.attr("y", -15)
.append("xhtml:body")
.html(function(d){
return d.children || d._children ? "<i class='fa fa-server fa-2x' style='color:#e29e3d;'></i>" : "<i class='fa fa-server fa-2x' style='color:#03a9f4;'></i>";
});
nodeEnter.append("svg:text")
.attr("x", function(d) { return d.children || d._children ? - 15 : 25; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
nodeEnter.append("a")
.attr("xlink:href", function (d) {
return "/" + d.id;
})
.append("rect")
.attr("class", "clickable")
.attr("y", -10)
.attr("x", function (d) { return d.children || d._children ? -70 : 10; })
.attr("width", 60)
.attr("height", 16)
.style("fill","rgba(3,169,244,0)")
.style("fill-opacity", .3);
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function( d ) {
return "translate(" + d.y + "," + d.x + ")";
});
nodeUpdate.select("circle")
.attr("r", 0)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1)
.text(function(d){
return d.name.split("/").pop();
});
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
var link = this._svg.selectAll("path.link")
.data(this._tree.links(nodes), function(d) { return d.target.id; });
link.enter().insert("svg:path", "g")
.attr("class", function (d) { return (d.source != root) ? "link_dashed" : "link_continuous" ; })
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
})
.transition()
.duration(duration)
.attr("d", diagonal)
.attr("marker-end", "url(#end)");
link.transition()
.duration(duration)
.attr("d", diagonal)
.attr("marker-end", "url(#end)");
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
}
}
When the updateGraph() method is called, an error occurs:
Uncaught TypeError: this.updateGraph is not a function.
The reason you'll be getting this issue (based on your code above) is because this.updateGraph(root); is not in the same scope as your component.
Above the line this.data.forEach(function(obj) { add the following:
var self = this;
and then change:
this.updateGraph(root);
to:
self.updateGraph(root);
Hope this helps!
Related
I am working on a project which needs a label for each of the levels of the collapsible tree in D3.js(https://bl.ocks.org/mbostock/4339083). I am facing tough time in adding the same. The label I require is added in the attached screenshot. The label should populate for each level as soon as click on each level and disappear as it the tree rolls back. Could anyone help me out with this.
Modify the update method to track the levels from the node items. Maintain a unique sorted hash of all the depth values from the node items used to plot the chart. Once you have the sorted depthHash array, plot the text on the top of your chart. Below is a fiddle i have modified for your reference.
http://jsfiddle.net/deepakpster/vomxqxov/3/
var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = 960 - margin.right - margin.left,
height = 800 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
flare = "http://rawgit.com/mbostock/1093025/raw/a05a94858375bd0ae023f6950a2b13fac5127637/flare.json"
d3.json(flare, function(error, flare) {
root = flare;
root.x0 = height / 2;
root.y0 = 0;
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
update(root);
});
d3.select(self.frameElement).style("height", "800px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 180; });
// Showing the labels for the level of depths.
// Using underscore.js to do the pluck, uniq.
var depthHash = _.uniq(_.pluck(nodes, "depth")).sort();
svg.selectAll("g.levels-svg").remove();
var levelSVG = svg.append("g").attr("class", "levels-svg");
var levels = levelSVG.selectAll("g.level");
levels.data(depthHash)
.enter().append("g")
.attr("class", "level")
.attr("transform", function(d) { return "translate(" + d*180 + "," + 10 + ")"; })
.append("text")
.text(function(d){
return "level-" + d;
});
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", click);
/*
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
*/
var ww = 30, hh = 20
nodeEnter.append("rect")
.attr("height", 1e-6)
.attr("width", 1e-6)
.attr("x", -ww/2)
.attr("y", -hh/2)
.attr("rx", 3)
.attr("ry", 3)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("rect")
.attr("width", ww)
.attr("height", hh)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("circle")
.attr("r", 4.5)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.attr("dx", -10)
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
Cannot figure out how to start my tree with data collapsed. Tried all solutions i could find here already. Have included the snipped of javascript.
People have mentioned changing the JSON file readme.json which i don't have.
Have also tried to swap _children and children with no success.
Any help much appreciated.
<script src="d3/d3.min.js"></script>
<script>
var treeData = [
<?php echo $contents ?>
];
// ************** Generate the tree diagram *****************
var margin = {top: 20, right: 0, bottom: 0, left: 250},
width = 1800 - margin.right - margin.left,
height = 900 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
root = treeData[0];
root.x0 = height / 2;
root.y0 = 0;
update(root);
d3.select(self.frameElement).style("height", "400px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(), links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 360; });
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
;
nodeEnter.on("click", function (d) {
click(d);
var x = d.jobdescription;
x = x.replace('\\','');
var g = d3.select(this); // The node
d3.select("body").select('div.tooltip').remove();
var div = d3.select("body").append("div")
.attr('pointer-events', 'none')
.attr("class", "tooltip")
.style("opacity", 1)
.html(x)
.style("left", (d.x + 50 + "px"))
.style("top", (d.y +"px"));
});
/*nodeEnter.on("mouseout", function (d) {
d3.select("body").select('div.tooltip').remove();
});*/
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; })
;
nodeEnter.append("text")
.attr("x", function(d) { var i = ((d.the_length) * 7) + 25; var i2 = -(d.the_length + 10); return d.children || d._children ? i2 : i; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "end"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("circle")
.attr("r", 10)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
</script>
I'm using the d3.js library to paint some nodes and link them, but they don't link them correctly. I have tried too many things but none of them has given me a solution.
Like you can see in the image the path are linking only in the enter position of the svg:rect.
nodeEnter.append("svg:rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 1)
.attr("height", 1)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("svg:text")
.attr("x", 30)
.attr("dy", ".35em")
.attr("text-anchor", "")
.text(function(d) { return d.name + " " + (d.occurences || ""); })
.style("fill-opacity", 1e-6);
nodeEnter.append("svg:image")
.attr('width', 0)
.attr('height', 0)
.style("fill-opacity", 1e-6)
.attr("xlink:href", function(d) {return d.icon; });
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("rect")
.attr("rx", 6)
.attr("ry", 6)
.attr("x", 0)
.attr("y", -12.5)
.attr("width", function(d){ return (getWidthOfText(d.name, 'Helvetica Neue', 14)+getWidthOfText(d.ocurrences, 'Helvetica Neue', 14))+30;})
.attr("height", 25)
//.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
.style("filter", "url(#drop-shadow)");
nodeUpdate.select("text")
.style("fill-opacity", 1);
nodeUpdate.select("image")
.attr("x", 6)
.attr("y", -7)
.attr('width', 20)
.attr('height', 15)
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 1)
.attr("height", 1)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeExit.select("text")
.style("fill-opacity", 1e-6);
vis.selectAll("image")
.append("svg:title")
.text(function(d) { return "Descripción: "+d.description+"\n\nComentarios: "+d.comment});
// Update the links…
var link = vis.selectAll("path.link")
.data(tree.links(nodes), function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("svg:path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
})
.transition()
.duration(duration)
.attr("d", diagonal);
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
Is there any way to move the paths to get the nodes correctly linked?
I have the following code:
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", function(d) { toggle(d); update(d); });
nodeEnter.append("svg:circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("svg:text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) {
if (d.size != undefined) {
return d.name + " :: " + d.size + " :: " + d.diff_day;
} else {
return d.name;
}
})
.style("fill-opacity", 1e-6)
.style("font-size", "15px");
nodeEnter.append("svg:path").attr("d", d3.svg.symbol.type("triangle-up").style("fill", "black"));
Now, the problem is that the svg:circle and svg:text are coming fine. But the "d3.svg.symbol.type("triangle-up").style("fill", "black"));" doesn't seem to work. Any help would be appreciated...
You cannot set the attribute properties on the symbol you create itself like this:
nodeEnter.append("svg:path") // Wrong.
.attr("d", d3.svg.symbol.type("triangle-up").style("fill", "black"));
You have to first set the d attribute, and then set the style fill on the path:
nodeEnter.append("svg:path") // Fixed.
.attr("d", d3.svg.symbol().type("triangle-up"))
.style("fill", "black");
I'm new to the d3.js library.
I'm trying to make a tree like this one, but with a link that goes to an external page on each node.
Is it possible?
I tried to add a "svg:a" to each node but in makes all the tree to disappear.
Update:
I'm taking this code from the html of the page linked above.
The libraries linked are:
d3.js
d3.layout.js
This is all the code:
<style type="text/css">
.node circle {
cursor: pointer;
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font-size: 11px;
}
path.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
</style>
<script>
var m = [20, 120, 20, 120],
w = 1280 - m[1] - m[3],
h = 800 - m[0] - m[2],
i = 0,
root;
var tree = d3.layout.tree()
.size([h, w]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var vis = d3.select("#body").append("svg:svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.append("svg:g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
d3.json("flare.json", function(json) {
root = json;
root.x0 = h / 2;
root.y0 = 0;
function toggleAll(d) {
if (d.children) {
d.children.forEach(toggleAll);
toggle(d);
}
}
// Initialize the display to show a few nodes.
root.children.forEach(toggleAll);
toggle(root.children[1]);
toggle(root.children[1].children[2]);
toggle(root.children[9]);
toggle(root.children[9].children[0]);
update(root);
});
function update(source) {
var duration = d3.event && d3.event.altKey ? 5000 : 500;
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse();
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 180; });
// Update the nodes…
var node = vis.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", function(d) { toggle(d); update(d); });
nodeEnter.append("svg:circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("svg:text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("circle")
.attr("r", 4.5)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = vis.selectAll("path.link")
.data(tree.links(nodes), function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("svg:path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
})
.transition()
.duration(duration)
.attr("d", diagonal);
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children.
function toggle(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
}
</script>
Basically what I have tried was to add this piece of code just before the nodeEnter.append("svg:text")
nodeEnter.append("svg:a")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text("http://example.com")
.style("fill-opacity", 1e-6);
Try adding the action on the node itself, like below, and change the cursor to pointer to give a hint to the user.
var node = vis.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); })
.style("cursor", "pointer")
.on("click", function() {
window.open("http://www.stackoverflow.com", '_blank').focus();
});
You should be able to add an HTML link to any of the objects that you want by adding an a tag as follows (this example is for the text that is associated with each node);
nodeEnter.append("a")
.attr("xlink:href", "http://example.com")
.append("svg:text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
If you had a seperate URL associated with each node (let's imagine that it's called link) it would just be a matter of retrieving the url with a function call similar to this;
nodeEnter.append("a")
.attr("xlink:href", function(d) { return d.link; })
.append("svg:text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
For a fuller description of adding a link see here: https://leanpub.com/D3-Tips-and-Tricks/read#leanpub-auto-adding-web-links-to-d3js-objects
For information on tree diagrams in general see here: https://leanpub.com/D3-Tips-and-Tricks/read#leanpub-auto-tree-diagrams