Reputation: 1992
I basically have some nested data like so:
const shapeGroups = [
{
title: 'shapeGroup_01',
color: 'blue',
shapes: [
{
shape:'rect',
width: 30,
height: 100,
x: 250,
y: 450
}
]
},
{
title: 'shapeGroup_01',
color: 'blue',
shapes: [
{
shape:'rect',
width: 10,
height: 40,
x: 350,
y:50
}
]
}
]
What I'm interested in doing is setting the color in each rect
as it's defined in the shapeGroup's color
property.
const shapeGroups = [{
title: 'shapeGroup_01',
color: 'blue',
shapes: [{
shape: 'rect',
width: 30,
height: 100,
x: 250,
y: 450
}]
},
{
title: 'shapeGroup_01',
color: 'blue',
shapes: [{
shape: 'rect',
width: 10,
height: 40,
x: 350,
y: 50
}]
}
]
const stage = d3.select('#stageContainer')
.append('svg')
.attr('id', '#stage')
.attr('width', 1000)
.attr('height', 1000)
const groups = stage
.selectAll('.group')
.data(shapeGroups)
const groupEnter = groups
.enter()
.append('g')
.attr('class', 'group');
const getGroup = group => group.shapes;
const createShape = shape => document.createElementNS(d3.namespaces.svg, shape.shape)
groupEnter
.selectAll('.shape')
.data(getGroup)
.enter()
.append(createShape)
.attr('fill', 'red') // I want the color as defined in the current group
.attr('width', d => d.width)
.attr('height', d => d.height)
.attr('x', d => d.x)
.attr('y', d => d.y)
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="stageContainer"></div>
In other words, I need a way to either set color
at the group level, or somehow access the parent datum
object when I'm appending individual .shape
s.
Upvotes: 2
Views: 186
Reputation: 102194
As you can read in my answer here, you cannot access the parent's datum or its index from inside a selection in D3 v4/v5.
A solution here is getting the datum of the parentNode
:
.attr('fill', (_, i, n) => d3.select(n[i].parentNode).datum().color)
Here is your code with that change (I'm making the two rectangles with different colours and the SVG smaller, for better visualising it):
const shapeGroups = [{
title: 'shapeGroup_01',
color: 'blue',
shapes: [{
shape: 'rect',
width: 30,
height: 100,
x: 250,
y: 50
}]
},
{
title: 'shapeGroup_01',
color: 'green',
shapes: [{
shape: 'rect',
width: 10,
height: 40,
x: 350,
y: 20
}]
}
];
const stage = d3.select('body')
.append('svg')
.attr('width', 400)
.attr('height', 400)
const groups = stage
.selectAll('.group')
.data(shapeGroups)
const groupEnter = groups
.enter()
.append('g')
.attr('class', 'group');
const getGroup = group => group.shapes;
const createShape = shape => document.createElementNS(d3.namespaces.svg, shape.shape)
groupEnter
.selectAll('.shape')
.data(getGroup)
.enter()
.append(createShape)
.attr('fill', (_, i, n) => d3.select(n[i].parentNode).datum().color)
.attr('width', d => d.width)
.attr('height', d => d.height)
.attr('x', d => d.x)
.attr('y', d => d.y)
<script src="https://d3js.org/d3.v5.min.js"></script>
However, the simplest solution by far is just setting the fill
to the parent selection itself:
const groupEnter = groups
.enter()
.append('g')
.attr('class', 'group')
.attr('fill', d => d.color);
Here is the demo:
const shapeGroups = [{
title: 'shapeGroup_01',
color: 'blue',
shapes: [{
shape: 'rect',
width: 30,
height: 100,
x: 250,
y: 50
}]
},
{
title: 'shapeGroup_01',
color: 'green',
shapes: [{
shape: 'rect',
width: 10,
height: 40,
x: 350,
y: 20
}]
}
];
const stage = d3.select('body')
.append('svg')
.attr('width', 400)
.attr('height', 400)
const groups = stage
.selectAll('.group')
.data(shapeGroups)
const groupEnter = groups
.enter()
.append('g')
.attr('class', 'group')
.attr('fill', d=>d.color);
const getGroup = group => group.shapes;
const createShape = shape => document.createElementNS(d3.namespaces.svg, shape.shape)
groupEnter
.selectAll('.shape')
.data(getGroup)
.enter()
.append(createShape)
.attr('width', d => d.width)
.attr('height', d => d.height)
.attr('x', d => d.x)
.attr('y', d => d.y)
<script src="https://d3js.org/d3.v5.min.js"></script>
Upvotes: 3