如何在Angular项目中基于amCharts实现联动依赖图表
我来帮你搞定这个图表联动的需求!其实核心思路就是让两个图表共享同一数据源+监听graph-1的交互事件修改数据+同步更新两个图表,下面一步步给你拆解实现步骤:
核心思路概述
因为两个图表的数据存在依赖关系,所以必须让它们基于同一套可响应的数据源。当用户点击graph-1的年份时,我们修改这套数据源(隐藏对应年份),然后让两个图表自动感知数据变化并刷新——这在Angular里用BehaviorSubject做数据共享是最方便的方案。
具体实现步骤
第一步:创建数据共享服务
先写一个Angular服务,用来存储和同步两个图表的数据源,确保数据变化时所有订阅者都能收到通知:
// data.service.ts import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class DataService { // 用BehaviorSubject存储可响应的人口数据 private populationData$ = new BehaviorSubject<any[]>([]); // 对外暴露可订阅的数据流 public currentData$ = this.populationData$.asObservable(); // 更新数据源的方法 updatePopulationData(updatedData: any[]) { this.populationData$.next([...updatedData]); } // 获取当前最新数据 getCurrentData() { return this.populationData$.value; } }
第二步:创建graph-1并监听点击事件
在组件里初始化graph-1,给它绑定数据源,同时监听图表的点击事件——当用户点击某年份的柱子/数据点时,过滤掉该年份的数据并更新共享数据源:
// 在你的图表组件中 import { Component, OnInit, OnDestroy } from '@angular/core'; import { DataService } from './data.service'; import * as am4core from '@amcharts/amcharts4/core'; import * as am4charts from '@amcharts/amcharts4/charts'; @Component({ selector: 'app-charts', templateUrl: './charts.component.html' }) export class ChartsComponent implements OnInit, OnDestroy { private chart1!: am4charts.XYChart; private chart2!: am4charts.XYChart; // 模拟原始人口数据 private originalData = [ { year: '2020', male: 500, female: 480 }, { year: '2021', male: 520, female: 490 }, { year: '2022', male: 530, female: 510 }, { year: '2023', male: 540, female: 520 } ]; constructor(private dataService: DataService) {} ngOnInit(): void { // 初始化共享数据源 this.dataService.updatePopulationData([...this.originalData]); // 创建两个图表 this.initGraph1(); this.initGraph2(); // 监听数据变化,同步更新graph-2 this.dataService.currentData$.subscribe(data => { this.updateGraph2(data); }); } ngOnDestroy(): void { // 销毁图表避免内存泄漏 this.chart1?.dispose(); this.chart2?.dispose(); } private initGraph1(): void { // 创建"人口vs年份"的柱状图 this.chart1 = am4core.create('graph-1-container', am4charts.XYChart); this.chart1.data = this.dataService.getCurrentData(); // 配置X轴(年份) const xAxis = this.chart1.xAxes.push(new am4charts.CategoryAxis()); xAxis.dataFields.category = 'year'; xAxis.title.text = '年份'; // 配置Y轴(人口数) const yAxis = this.chart1.yAxes.push(new am4charts.ValueAxis()); yAxis.title.text = '人口数'; // 添加男性人口系列 const maleSeries = this.chart1.series.push(new am4charts.ColumnSeries()); maleSeries.dataFields.valueY = 'male'; maleSeries.dataFields.categoryX = 'year'; maleSeries.name = '男性'; maleSeries.columns.template.fill = am4core.color('#2196F3'); // 添加女性人口系列 const femaleSeries = this.chart1.series.push(new am4charts.ColumnSeries()); femaleSeries.dataFields.valueY = 'female'; femaleSeries.dataFields.categoryX = 'year'; femaleSeries.name = '女性'; femaleSeries.columns.template.fill = am4core.color('#F44336'); // 添加图例 this.chart1.legend = new am4charts.Legend(); // 监听柱子点击事件 this.chart1.series.forEach(series => { series.columns.template.events.on('hit', (event) => { const clickedYear = event.target.dataItem.dataContext.year; // 获取当前数据,过滤掉点击的年份 const updatedData = this.dataService.getCurrentData().filter(item => item.year !== clickedYear); // 更新共享数据源 this.dataService.updatePopulationData(updatedData); // 同步更新graph-1的数据 this.chart1.data = updatedData; }); }); }
第三步:创建graph-2并同步数据更新
graph-2的职责是基于当前数据源计算男女人口总数,当共享数据源变化时自动刷新:
// 接上面的组件代码 private initGraph2(): void { // 创建"男女人口总数"的饼图 this.chart2 = am4core.create('graph-2-container', am4charts.PieChart); this.chart2.data = this.calculateTotalPopulation(this.dataService.getCurrentData()); // 配置饼图系列 const pieSeries = this.chart2.series.push(new am4charts.PieSeries()); pieSeries.dataFields.value = 'total'; pieSeries.dataFields.category = 'gender'; pieSeries.labels.template.text = '{gender}: {total}'; // 添加图例 this.chart2.legend = new am4charts.Legend(); } // 计算男女人口总数的工具方法 private calculateTotalPopulation(data: any[]): any[] { let totalMale = 0; let totalFemale = 0; data.forEach(item => { totalMale += item.male; totalFemale += item.female; }); return [ { gender: '男性总数', total: totalMale }, { gender: '女性总数', total: totalFemale } ]; } // 更新graph-2的方法 private updateGraph2(data: any[]): void { this.chart2.data = this.calculateTotalPopulation(data); }
组件模板文件
最后在HTML里给两个图表准备容器:
<!-- charts.component.html --> <div class="chart-container"> <h3>Graph-1: 人口 vs 年份</h3> <div id="graph-1-container" style="width: 100%; height: 400px;"></div> </div> <div class="chart-container" style="margin-top: 50px;"> <h3>Graph-2: 男女人口总数</h3> <div id="graph-2-container" style="width: 100%; height: 400px;"></div> </div>
关键注意点
- 内存泄漏防护:一定要在组件销毁时调用
dispose()方法销毁amCharts实例,避免内存泄漏。 - 数据不可变性:更新数据源时要创建新数组(比如用
[...updatedData]),这样BehaviorSubject才能正确触发订阅更新。 - 跨组件联动:如果两个图表在不同组件里,只要在各自组件中注入
DataService,graph-1所在组件处理点击更新数据,graph-2所在组件订阅currentData$更新图表即可。
这样操作下来,点击graph-1的任意年份柱子,该年份数据会立刻从graph-1中隐藏,同时graph-2的总数会自动重新计算并刷新,完美实现你要的联动效果!
内容的提问来源于stack exchange,提问作者Geethapriya G H
相关产品推荐
相关产品推荐

