![見出し画像](https://assets.st-note.com/production/uploads/images/124530166/rectangle_large_type_2_1a079fbce13345043c89a65cfa0c2d8d.png?width=1200)
Angular_Highchartsのtooltipを自作する #435
今回の記事は、仕事で参考にした偉大な先人のコードを紹介させていただきます。
Highchartsでtooltipを使う際、そのカスタマイズに苦労されたことはないでしょうか?
Highchartsはグラフ描画の素晴らしいライブラリである一方、要件による微調整はやりにくい時があります。私もどうしても満たしたいTooltipの仕様が満たせずに苦労していました。
Highcharts-tooltipのソースコードです↓
偉大な先人は、このソースコードを読み解いて以下のように実装することで、自作tooltipをHighchartsのtooltipとして使うことを実現していました。
以下のようにHTMLでHighchartsを呼び出す方式で、「options」に渡す「ChartData」でTooltipの設定を行います。
<highcharts-chart
[Highcharts]="Highcharts"
[options]="chartData"
>
</highcharts-chart>
まずchartDataから見ていきます。ここではベン図を作成していて、ベン図にマウスをホバーした際のTooltipを設定します。
constしているtooltipComponentが重要なポイントです。ここでComponentへのアクセスを作成していて、それをHighchartsのtooltip欄に設定することで、自作Componentをtooltipとして使うことができます。下記コードのformatterで設定しています。
また、Highchartsの設定内では「this」が使いにくいので、refThisComponentで自身のComponentへアクセスできるようにしています。
[sample-venn-graph-component.ts]
import {Compiler, Injector} from '@angular/core';
import venn from 'highcharts/modules/venn';
venn(Highcharts);
import { ComponentFactoryClass } from '@shared/utils/component-factory.util';
import { SampleTooltipComponent } from './your-pass';
import { SampleTooltipModule } from './your-pass';
@Component({
selector: 'app-sample-app',
templateUrl: './sample-app.component.html',
styleUrls: ['./sample-app.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class SampleVennGraphComponent implements OnInit, OnChanges {
constructor(private injector: Injector, private compiler: Compiler) {}
private _buildVennChartData(): void {
const refThisComponent: SampleVennGraphComponent = this;
const tooltipComponent: ComponentRef<SampleTooltipComponent> = new ComponentFactoryClass<
SampleTooltipModule,
SampleTooltipComponent
>(this.injector, this.compiler).createComponent(SampleTooltipModule, SampleTooltipComponent);
this.chartData = {
chart: {
type: 'venn',
width: 700,
spacingRight: 50
},
credits: {
enabled: false // クレジットを非表示にする
},
exporting: {
enabled: false // エクスポートメニューを無効にする
},
title: {
text: null
},
tooltip: {
useHTML: true, // HTMLを使用する設定を有効にする
outside: true,
backgroundColor: null,
borderWidth: 0,
borderRadius: 0,
style: {
fontFamily: 'Noto Sans JP', // フォントファミリーの設定
color: '#545454', // フォントの色
textOutline: 'none',
textAlign: 'center'
},
formatter: function () {
tooltipComponent.instance.nameSets = this.point.sets;
tooltipComponent.instance.sampleValue = this.point.value.toFixed(1);
tooltipComponent.instance.setsLine = refThisComponent.buildTooltipTitle(this.point.sets);
tooltipComponent.changeDetectorRef.detectChanges(); // 変化を検知して再描画する
return tooltipComponent.location.nativeElement.outerHTML;
}
},
plotOptions: {
venn: {
states: {
hover: {
borderColor: 'transparent', // ホバー時の枠線を透明にする
borderWidth: 0 // 枠線の太さを0にする
}
}
}
},
series: [
{
data: this.convertToVennData(this.totalUniqueReachSet, 0)
}
]
};
}
private buildTooltipTitle(sets: string[]): string {
switch (sets.length) {
case 3:
return '全て重複';
case 2:
return `${sets[1]}と${sets[0]}`;
default:
return `${sets[0]}全体`;
}
}
次は、Componentを作成するクラスです。先人が作ってくれていたもので、神掛かっていると感じました。
正確には、ComponentRefとあるように、Componentへのアクセスを作成してreturnします。これによってtooltip部分でSampleTooltipComponentにアクセスできるわけです。
[component-factory.util.ts]
import { Compiler, ComponentRef, Injector, Type } from '@angular/core';
export class ComponentFactoryClass<M, C> {
constructor(private injector: Injector, private compiler: Compiler) {}
public createComponent = (module: Type<M>, component: Type<C>): ComponentRef<C> => {
const compiledModule = this.compiler.compileModuleAndAllComponentsSync(module);
const factory = compiledModule.ngModuleFactory
.create(this.injector)
.componentFactoryResolver.resolveComponentFactory(component);
return factory.create(this.injector);
};
}
後はSampleTooltipComponentを定義するだけです。親Componentから必要な値を@Inputして、HTMLとCSSを調整すれば好きなようにカスタムしたtooltipを使うことができます(ここではtsファイルのみ記載)。
[sample-tooltip.component.ts]
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'app-sample-tooltip',
templateUrl: './sample-tooltip.component.html',
styleUrls: ['./sample-tooltip.component.scss']
})
export class SampleTooltipComponent implements OnInit {
@Input()
public nameSets: string[] = [];
@Input()
public sampleValue = 0;
@Input()
public setsLine = '';
constructor() {}
ngOnInit(): void {}
}
[sample-tooltip.module.ts]
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { SharedModule } from '@shared/shared.module';
import { SampleTooltipComponent } from './sample-tooltip.component';
@NgModule({
imports: [CommonModule, SharedModule],
declarations: [SampleTooltipComponent]
})
export class SampleTooltipModule {}
以上になります。
自分でソースコードを読み解いて、こういう応用を効かせられるようになりたいですが、まだまだ道のりは長そうです。
ここまでお読みいただきありがとうございました!