I have a stackblitz here - https://stackblitz.com/edit/chart-update-1-34uvdo?file=src/app/bar-chart.ts
It's a d3 chart in an angular app.
The bars have arrows to show the start and finish position of the bar.
I'd like to have each bar arrow compo in one g element - at the moment they are all in the same g element.
How can I craete a g element for each bar and arrow.
bar.enter()
.append("line")
.attr("x1", d => this.x(d.phase) + this.x.bandwidth() / 2)
.attr("y1", d => this.y(d.start) + ((d.start < d.finish) ? -5 : 5))
.attr("x2", d => this.x(d.phase) + this.x.bandwidth() / 2)
.attr("y2", d => this.y(d.finish) + ((d.start < d.finish) ? 5 : -5))
.attr("stroke", "black")
.attr("stroke-width", 3)
.attr("marker-end", "url(#arrow)")
.style('pointer-events', 'none');
Have a look again at the d3 general update pattern. It is one of those d3 concepts that will always be a little confusing, especially when applying it to anything except the simplest examples.
So by looking at the above pattern we can break it down as:
- Data Join
Join new data with old elements, if any.
- Update
Update old elements as needed.
- Enter
Create new elements as needed.
- Enter + Update
After merging the entered elements with the update selection, apply operations to both.
- Exit
Remove old elements as needed.
private drawUpdate(data) {
this.x.domain(data.map((d: any) => {
return d.phase
}));
this.getExtent(data);
this.y.domain(d3.extent(this.values));
this.createAxis();
// Data Join
const bar = this.chart.selectAll(".bargroup")
.data(data);
// Updates
// Update rect dimensions
bar.select('rect').attr("x", (d, i) => {
return this.x(d.phase)
})
.attr("width", (d, i) => {
return this.x.bandwidth()
})
.attr("y", (d, i) => {
if (d.start < d.finish) {
return this.y(d.finish);
} else {
return this.y(d.start);
}
})
.attr("height", (d, i) => {
if (d.start < d.finish) {
return this.y(d.start) - this.y(d.finish);
} else {
return this.y(d.finish) - this.y(d.start);
}
})
// Update line dimensions
bar.select('line')
.attr("x1", d => this.x(d.phase) + this.x.bandwidth() / 2)
.attr("y1", d => this.y(d.start) + ((d.start < d.finish) ? -5 : 5))
.attr("x2", d => this.x(d.phase) + this.x.bandwidth() / 2)
.attr("y2", d => this.y(d.finish) + ((d.start < d.finish) ? 5 : -5))
// Enter
const barEnter = bar.enter()
.append("g")
.classed('bargroup', true)
// Enter & update - append new rects, update dimensions
barEnter
.append('rect')
.attr("x", (d, i) => {
return this.x(d.phase)
})
.attr("width", (d, i) => {
return this.x.bandwidth()
})
.attr("y", (d, i) => {
if (d.start < d.finish) {
return this.y(d.finish);
} else {
return this.y(d.start);
}
})
.attr("height", (d, i) => {
if (d.start < d.finish) {
return this.y(d.start) - this.y(d.finish);
} else {
return this.y(d.finish) - this.y(d.start);
}
})
// Enter & update - append new lines, update dimensions
barEnter
.append("line")
.attr("stroke", "white")
.attr("stroke-width", 3)
.attr("marker-end", "url(#arrow)")
.style('pointer-events', 'none')
.attr("x1", d => this.x(d.phase) + this.x.bandwidth() / 2)
.attr("y1", d => this.y(d.start) + ((d.start < d.finish) ? -5 : 5))
.attr("x2", d => this.x(d.phase) + this.x.bandwidth() / 2)
.attr("y2", d => this.y(d.finish) + ((d.start < d.finish) ? 5 : -5))
//Exit
bar.exit()
.remove();
d3.select('.y-axis').call(this.y_axis)
d3.select('.x-axis').call(this.x_axis)
.attr("transform", "translate(0," + this.height + ")");
}
Updated stackblitz