Forum Discussion
d3 v4 and selectionManager
- 8 years ago
It seems d3v4 handles selections in a different they in comparison to d3v3.
We'd recommend to take a look at this fixed version.
Ignat Vilesov,
Software Engineer
Microsoft Power BI Custom Visuals
It seems d3v4 handles selections in a different they in comparison to d3v3.
We'd recommend to take a look at this fixed version.
Ignat Vilesov,
Software Engineer
Microsoft Power BI Custom Visuals
Hi Ignat,
works fine for me.
Thank you very much!
For everyone who has the same issue follow these steps:
1. STEP: Change the selection datatype to the new d3 v4 format to something like this:
private chartSelection: d3.Selection<d3.BaseType, ChartDataPoint, d3.BaseType, any>; // Needed to display datapoints
private svg: d3.Selection<SVGElement, {}, HTMLElement, any>; // SVG-Element (Container)
private g: d3.Selection<SVGElement, {}, HTMLElement, any>; // SVG-Element (group 'g')
2. STEP: Add <SVGElement> when calling the constructor:
constructor(options: VisualConstructorOptions) {
this.host = options.host;
this.selectionManager = options.host.createSelectionManager();
this.selectionManager.registerOnSelectCallback(() => {
this.syncSelectionState(this.chartSelection, this.selectionManager.getSelectionIds() as ISelectionId[]);
});
this.svg = d3.select(options.element).append<SVGElement>('svg');
this.g = this.svg.append<SVGElement>('g');
}3. STEP: When creating the datapoints, you have to add the d3 event directly to it. That means if your code look something similar like this:
// Create a SVG-Element (datapoints)
this.chartSelection = this.g
.append('g')
.selectAll('.dot')
.data(viewModel.dataPoints);
this.chartSelection
.enter()
.append('circle')
.attr('r', 5)
.merge(this.chartSelection)
.classed('dot', true)
.attr('cx', d => x(<number>d.gesamtKosten))
.attr('cy', d => y(<number>d.anzahlKostenstellen))
.attr('fill', d => d.color);
this.syncSelectionState(
this.chartSelection,
this.selectionManager.getSelectionIds() as ISelectionId[]
);
this.chartSelection.on('click', (d) => {
// Allow selection only if the visual is rendered in a view that supports interactivity (e.g. Report)
if (this.host.allowInteractions) {
const isCrtlPressed: boolean = (d3.event as MouseEvent).ctrlKey;
this.selectionManager
.select(d.selectionId, isCrtlPressed)
.then((ids: ISelectionId[]) => {
this.syncSelectionState(this.chartSelection, ids);
});
(<Event>d3.event).stopPropagation();
}
});
this.chartSelection
.exit()
.remove();
// Clear selection when clicking outside a dot
this.svg.on('click', (d) => {
if (this.host.allowInteractions) {
this.selectionManager
.clear()
.then(() => {
this.syncSelectionState(this.chartSelection, []);
});
}
});Change it to this:
// Create a SVG-Element (datapoints)
this.chartSelection = this.g
.append('g')
.selectAll('.dot')
.data(viewModel.dataPoints);
this.chartSelection
.enter()
.append('circle')
.on('click', (d) => {
// Allow selection only if the visual is rendered in a view that supports interactivity (e.g. Report)
if (this.host.allowInteractions) {
const isCrtlPressed: boolean = (d3.event as MouseEvent).ctrlKey;
this.selectionManager
.select(d.selectionId, isCrtlPressed)
.then((ids: ISelectionId[]) => {
this.syncSelectionState(this.g.selectAll(".dot"), ids);
});
(<Event>d3.event).stopPropagation();
}
})
.attr('r', 5)
.merge(this.chartSelection)
.classed('dot', true)
.attr('cx', d => x(<number>d.gesamtKosten))
.attr('cy', d => y(<number>d.anzahlKostenstellen))
.attr('fill', d => d.color);
this.syncSelectionState(
this.g.selectAll(".dot"),
this.selectionManager.getSelectionIds() as ISelectionId[]
);
this.chartSelection
.exit()
.remove();
// Clear selection when clicking outside a dot
this.svg.on('click', (d) => {
if (this.host.allowInteractions) {
this.selectionManager
.clear()
.then(() => {
this.syncSelectionState(this.chartSelection, []);
});
}
});4. STEP: Edit the syncSelectionState():
private syncSelectionState(
selection: d3.Selection<d3.BaseType, any, d3.BaseType, any>,
selectionIds: ISelectionId[]
): void { ... }I hope this helps!