You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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>
关键注意点
  1. 内存泄漏防护:一定要在组件销毁时调用dispose()方法销毁amCharts实例,避免内存泄漏。
  2. 数据不可变性:更新数据源时要创建新数组(比如用[...updatedData]),这样BehaviorSubject才能正确触发订阅更新。
  3. 跨组件联动:如果两个图表在不同组件里,只要在各自组件中注入DataService,graph-1所在组件处理点击更新数据,graph-2所在组件订阅currentData$更新图表即可。

这样操作下来,点击graph-1的任意年份柱子,该年份数据会立刻从graph-1中隐藏,同时graph-2的总数会自动重新计算并刷新,完美实现你要的联动效果!

内容的提问来源于stack exchange,提问作者Geethapriya G H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 12:34:07