Reputation: 17485
I have an Angular CLI application that displays a number of D3.v5 visuals. Some of the tooltips I would like to add on those D3 displays are fairly non-trivial, and I would like to make use of the power of Angular's templating and data binding.
The only remotely connected resource I could find is https://github.com/andyperlitch/ngx-d3-tooltip. I checked out the code. It is fairly outdated, relying on D3 v4 and Angular v5.
How would you go about using Angular components as data-driven tooltips from a D3 context ?
Upvotes: 1
Views: 2068
Reputation: 29335
I'm not a d3 expert, so I started with this: http://bl.ocks.org/williaster/af5b855651ffe29bdca1
and worked my way from there.
Here is the working blitz: https://stackblitz.com/edit/angular-ivy-vrq5p6?file=src%2Fapp%2Fchart.component.ts
So I wrote a basic component that take in some data and renders a scatter plot with d3.
The component has a tooltip container above the chart container, that looks like this:
<div class="tooltip" *ngIf="toolTipTmp && hovered" [ngStyle]="ttPos">
<ng-container *ngTemplateOutlet="toolTipTmp.tmp; context: {$implicit: hovered}"></ng-container>
</div>
the component has corresponding properties:
@ContentChild(ChartTooltipDirective)
toolTipTmp: ChartTooltipDirective
hovered?: any
ttPos = {
"left.px": 0,
"top.px": 0
}
the content child here is a simple directive that exposes a template:
@Directive({
selector: "[chart-tooltip]"
})
export class ChartTooltipDirective {
constructor(public tmp: TemplateRef<any>) {}
}
then the render function links into these on the mouse enter and mouse leave functions:
var tipMouseover = (d) => {
this.hovered = d;
this.ttPos["left.px"] = d3.event.pageX + 15;
this.ttPos["top.px"] = d3.event.pageY - 28;
};
// tooltip mouseout event handler
var tipMouseout = (d) => {
this.hovered = undefined;
};
....
.on("mouseover", tipMouseover)
.on("mouseout", tipMouseout);
basically it just sets the hovered element and the position of the tooltip.
The concept here is that you set whatever element is hovered and set the tooltip's position on the component, and use that info to reveal and position the tooltip, then you feed the hovered element into the the template context, and use some template defined as a content child of your chart component, so you can define whatever template you like.
you use it in your template pretty easily:
<my-chart [data]="data" xLabel="Sugar" yLabel="Calories">
<ng-template chart-tooltip let-d>
{{d.cereal}}
<br>
<span>{{d.manufacturer}}</span>
<br>
<b>{{d.sugar}}</b> sugar, <b>{{d.calories}}</b> calories
</ng-template>
</my-chart>
just apply your directive and write up your tooltip template. This way, you have pretty much the full power of angular templating and components for your tooltips.
I tried to make this as generic as possible, but again, not a d3 expert.
EDIT:
here it is adapted to the chart you provided:
Upvotes: 2