replacing d3.js diagram instead of creating a new one every time - d3.js

I`m using d3.js combined with GWT and every time the javascript code for creating a pie chart is called it appends a new one rather than replacing the old one with new values.
How can I change this code so that the old one is removed before appending the new one?
var svg = d3.select(".body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

Rather then changing your code i would suggest creating it once, then build your diagram using your data and taking a look at the .enter() and .exit() methods of d3.js - so if your data changes the library would add/remove some elements.
Cheers

Just add
d3.select(".body").selectAll("svg").remove();
before your code to remove all SVG elements.

Related

Unable to repaint waterfall chart in D3js with Angular

I have created an Angular directive for waterfall chart in D3, and want to update the chart as soon as values get updated. I've put the scope.$watch and I can see the new data coming to the directive. But the paintChart() function which I am creating to re-render the chart with new data does not seem to work correctly.
Even if I am able to receive different arrays of data, I am not able to re-paint the chart with different data. I tried to put chart and bar variable definitions out of the paint method and re-use them again with different parameters, but no success. Like if I put the following code out of the paintChart() function:
var chart = d3.select(rawSvg[0])
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
The function stops working.
Here is the Fiddle.. The data is changing perfectly on selction, but I am struggling to get the chart re-painted properly on changing data.
UPDATE: I put the following code as first lines in paintChart() function, to remove previous SVG and it's associated elements before creating chart:
var svg = d3.select("svg");
svg.selectAll("*").remove();
Now the chart is successfully updating when different data is selected. But there are more issues:
When I select different data to update the chart, and re-select previously selected data, The last column of waterfall Chart disappears and shows NaN. The last column shows no error when loaded at first time. Only when data is toggled, this error occurs. You can reproduce this error by selecting different data from select-box and then re-selecting previously selected data. This seems a very strange issue.
It doesn't seem to me a correct way to remove SVG and it's associated groups/elements and render a totally new chart every time new data comes.

Graph not redrawing properly

I'm currently trying to combine two of my graphs into one view. My second graph, the bar chart will update the data if you use the slider. When you slide it forward, it works fine and dandy but when you slide it backwards it doesn't redraw the chart. When I was working on the bar chart separately it was fully working, it just seems that when I combine it with the line graph that it's only partially working.
I'm trying to find the cause of the problem and I think it's something wrong with this block of code. If I comment it out the bar graph redraws properly.
var chart1 = d3.select("body").append("svg")
.attr("width", chart1_width + chart1_margin.left + chart1_margin.right)
.attr("height", chart1_height + chart1_margin.top + chart1_margin.bottom)
.append("g")
.attr("transform", "translate(" + chart1_margin.left + "," + chart1_margin.top + ")");
Link to my fiddle: http://jsfiddle.net/flyingburrito/a8fym1ma/3/
Thanks!
I moved the bar chart on top and that made it work fine. An easy solution would be to keep it like that.
A better solution would be to analyse why that happens. With D3 when things happen like this it will often be in your selects. That is your problem here
d3.select("#sexYear").on("input", function () {
debugger;
d3.select("svg").selectAll("rect").remove();
update(this.value);
});
You are selecting 'svg' which selects the first svg which is the line chart, not the bar chart. This is because 'd3.select' selects the first of whatever is being called. What I did was add a class called 'barchart' to the barchart and then I select the barchart as such
var chart2 = d3.select("body").append("svg")
.attr('class','barchart')
and
d3.select("#sexYear").on("input", function () {
debugger;
d3.select(".barchart").selectAll("rect").remove();
update(this.value);
fiddle

DimpleJS barchart styling columns

I'm basically using a modified version of : http://dimplejs.org/advanced_examples_viewer.html?id=advanced_bar_labels .
I'd like to be able to add for each value a border on the left as high as the value (with a specific color for that border).
I'm not really sure where to start for adding that.
Any ideas?
Thanks.
More details : This is what I'd like to obtain : https://dl.dropboxusercontent.com/u/2227188/Image%202.png - the border on the left is the issue. (jsfiddle.net/mkzTk/5/ this what I currently have which is pretty much what's in the example - I don't know where to start really for adding a border)
You could append a rectangle after drawing for each element of the series as follows:
mySeries.afterDraw = function (s, d) {
var shape = d3.select(s);
svg.append("rect")
.attr("x", shape.attr("x"))
.attr("y", shape.attr("y"))
.attr("height", shape.attr("height"))
.attr("width", "10px")
.style("fill", shape.style("stroke"))
.style("pointer-events", "none");
};
The example you mention already uses the afterDraw function so just add the contents above to the existing method for labelling.
It looks nice, here's an example:
http://jsbin.com/lorin/9/edit?js,output#J:L20
I would set up each bar + edge pair as its own group based on a certain data point, and then append two rect elements to that group. Differences in color can be used to give them their distinctive colors.
Your code would look something like this:
var monthBars = d3.selectAll('.monthBar') //These will be for each chart
.data(allMyData, idFunction) //Assign and key your data
.enter()
.append('g')
.classed('monthBar', true);
.each(function(d){
var taskGroups = d3.select(this).selectAll('.taskGroup')
.data(d.dataForThisMonth, taskIdFn)
.enter()
.append('g')
.classed('.taskGroup', true);
.attr('transform', ...) //Define the x and y positioning for the group
taskGroups.append('rect')
//Make this the 'body' rect with the text in it
taskGroups.append('rect')
//Make this the edge rect
})

dc.js accessing a chart other that it's own in postRender

I have 2 charts on a page (A and B) and wish for some custom behavior to be added to chart B when a brush filter on chart A is performed.
I thought I could achieve this by doing something like;
charta.on('postRender', function(){
...
d3.selectAll('#chartb svg')
.data(nested, function(d){ return d.key})
.enter()
.append("g")
.attr("class", "aclass")... more code....
But this #chartb selector doesn't seem to work - when I inspect the DOM it has appended the <g> attributes to the <html> element and not the svg element I wanted to append to.
Is what I am trying to achieve possible?
If you are just adding stuff to the other chart, something like this should be possible. I don't think you will be able to select the generated items of the other chart and then apply a d3 join to it, because it is already joined.
I believe the problem with the code above is that d3.select is what you use to choose the context for a join, and d3.selectAll is what you use to actually make the data join. See
http://bost.ocks.org/mike/join/
So your code is trying to join to the chart and svg elements, which would have the effect you are describing. Instead you'll want to d3.select the svg and then d3.selectAll the elements you want to add - even though they don't exist yet! Yes, it's kind of a mind-bender; take a look at the above and the linked articles to get a better idea of it.
Note: there are dc convenience methods on the chart object which will execute the selects in the right context.
I got this working in the end by replacing the .enter() with repeated calls to datum() instead. A bit of a hack, but it works; If anyone can suggest a more d3ish way of acheiving this, I would be very grateful.
var svg = chart.svg();
nested.forEach(function(withValues) {
_(withValues.values).filter(function(d){return d.value < threshold}).forEach(function(timesMatchingThreshold){
svg.datum(timesMatchingThreshold)
.append("rect")
.style("opacity", 0.6)
.attr("transform", "translate(" + 30 + ", " + (-30) + ")")
.attr("class", "belowThreshold")
.attr("x", function(d) {return x(d.date)})
.attr("y", function(d) {return 200 - y(d.value)})
.attr("width", 3)
.attr("height", function(d) {return y(d.value)});
});

Can an SVG pattern be implemented in D3?

I tried:
var pattern = d3.select("#container").append("svg:pattern");
pattern.append("svg:defs") ... //and so on
but ran into errors on append("svg:pattern") so I assume that is not implemented in d3 yet.
My solution was to simply run the SVG code outside of d3 which worked fine since I did not need the power of d3 for this piece. (I realized I could have attached a .data(dataset) to an SVG object and looped through it to create it, but that would have been a lot of d3 effort for no reason.)
This is a sanity-check question: Did I miss something in trying to implement an svg pattern in d3, or was my solution the correct approach?
(Thank you Mike Bostock for such an incredible library.)
In this case all the magic it's in svg rather than d3. Take a look at the pocket guide to writing SVG to understand the basics of svg.
And here is my solution:
var svg = d3.select("body").append("svg");
svg.append("defs").append("pattern")
.attr('id','myPattern')
.attr("width", 40)
.attr("height", 25)
.attr('patternUnits',"userSpaceOnUse")
.append('path')
.attr('fill','none')
.attr('stroke','#335553')
.attr('stroke-width','3')
.attr('d','M0,0 Q10,20 20,10 T 40,0' );
svg.append('rect').attr('x',10)
.attr('y',0)
.attr('width',500)
.attr('height',200)
.attr('fill','url(#myPattern)');
View this example working on codepen
D3 is more or less agnostic to what you're dong with it -- you can generate SVG, HTML, whatever. There's no reason why SVG patterns wouldn't work. From the little code you've posted it looks like you might be trying to append the pattern to an HTML element. You'll have to create the SVG and the defs element before starting to define the pattern.
I confirm, you first have to add the defs element to your SVG and then add your pattern to it.
//first create you SVG or select it
var svg = d3.select("#container").append("svg");
//then append the defs and the pattern
svg.append("defs").append("pattern")
.attr("width", 5)
.attr("height", 5);

Resources