Storybook:更改控件的值不会重新呈现 Chart.js 画布

Posted

技术标签:

【中文标题】Storybook:更改控件的值不会重新呈现 Chart.js 画布【英文标题】:Storybook: Changing the value of the control doesnot rerender the Chart.js canvas 【发布时间】:2021-06-21 10:22:43 【问题描述】:

我正在使用基于StorybookAngular。我想做的就是根据 Storybook 控件中给出的值重新渲染图表。但是即使在更改控件的值之后,图表仍然保持不变。我尝试了很多解决方法,但仍然处于第一阶段。我想显示的图表是一个等值线。我使用Chartjschartjs-chart-geo 库来显示图表。

我在 Storybook 中的组件:

import  Component, Input, Output, EventEmitter, OnInit  from '@angular/core';
import * as Chart from 'chart.js';
import * as ChartGeo from 'chartjs-chart-geo';
import  HttpClient  from '@angular/common/http';
@Component(
    selector: 'storybook-choropleth',
    template: `<div>
    <canvas id="mapCanvas"></canvas>
  </div>
  `,
    styleUrls: ['./choropleth.css'],
)
export default class ChoroplethComponent implements OnInit 
    @Input()
    url = 'https://unpkg.com/world-atlas/countries-50m.json';
    /**
     * Type of projecttion
     */
    // @Input()
    chartProjection: 'azimuthalEqualArea' | 'azimuthalEquidistant' | 'gnomonic' | 'orthographic' | 'stereographic'
        | 'equalEarth' | 'albers' | 'albersUsa' | 'conicConformal' | 'conicEqualArea' | 'conicEquidistant' | 'equirectangular' | 'mercator'
        | 'transverseMercator' | 'naturalEarth1' = 'mercator';
    chart: any;
    geoData: any;
    countries: any;
    constructor(
        private http: HttpClient
    ) 
    
    ngOnInit() 
        this.getGeoData();
    
    getGeoData() 
        this.http.get(this.url).subscribe((data) => 
            this.countries = ChartGeo.topojson.feature(data, data['objects']['countries']).features;
            let t = <htmlCanvasElement>document.getElementById('mapCanvas');
            if (this.chart !== undefined) 
                this.chart.destroy();
            
            // exclude antartica
            this.countries.splice(239, 1);
            console.log(this.countries);
            let dts = 
                labels: this.countries.map((d) => d.properties.name),
                datasets: [
                    label: 'Countries',
                    data: this.countries.map((d) => ( feature: d, value: Math.random() )),
                ]
            ;
            console.log(this.countries);
            let configOptions = 
                maintainAspectRatio: true,
                responsive: true,
                showOutline: false,
                showGraticule: false,
                scale: 
                    projection: this.chartProjection
                 as any,
                geo: 
                    colorScale: 
                        display: true,
                        interpolate: 'blues',
                        missing: 'white',
                        legend: 
                            display: 'true',
                            position: 'bottom-right'
                        
                    
                
            ;
            
            this.chart = new Chart(t.getContext('2d'),
                
                    type: 'choropleth',
                    data: dts,
                    options: configOptions
                
            );
        );
    
    getDts() 
        this.getGeoData();
        let dts = 
            labels: this.geoData.map((i) => i.properties.name),
            datasets: [
                
                    outline: this.geoData,
                    data: this.geoData.map((i) => (
                        feature: i,
                        value: i.properties.confirmed
                    ))
                
            ]
        ;
        return dts;
    
    getConfigOptions() 
        let configOptions = 
            maintainAspectRatio: true,
            responsive: true,
            showOutline: false,
            showGraticule: false,
            scale: 
                projection: 'mercator'
             as any,
            geo: 
                colorScale: 
                    display: true,
                    interpolate: 'reds',
                    missing: 'white',
                    legend: 
                        display: 'true',
                        position: 'bottom-right'
                    
                
            
        ;
        return configOptions;
    

我的故事.ts:

import  Story, Meta, moduleMetadata  from '@storybook/angular';
import Choropleth from './choropleth.component';
import  HttpClientModule  from '@angular/common/http';
import  withKnobs  from '@storybook/addon-knobs';
import  NO_ERRORS_SCHEMA  from '@angular/core';
export default 
    title: 'Choropleth',
    component: Choropleth,
    decorators: [
        withKnobs,
        moduleMetadata(
            //:point_down: Imports both components to allow component composition with storybook
            imports: [HttpClientModule],
            schemas: [NO_ERRORS_SCHEMA],
        )
    ],
    argTypes: 
        backgroundColor:  control: 'color' 
    ,
 as Meta;
const Template: Story<Choropleth> = (args: Choropleth) => (
    component: Choropleth,
    props: args
);
export const Primary = Template.bind();
Primary.args = 
    url: 'https://unpkg.com/world-atlas/countries-50m.json'
;

在此,我想保持 URL 动态。由于故事书控件中的 URL 已更改,因此我想相应地重新渲染地图。任何帮助将不胜感激。

【问题讨论】:

我没有编写故事书,但如果您要更改 @Input 的值,则需要在 ngOnChanges 上调用 getGeoData()。或者,我认为您获取新数据并更新它(chartjs.org/docs/latest/developers/api.html#updateconfig),而不是再次创建图表实例。无论哪种方式,您都需要在 ngOnChanges 中调用它们。 @GowthamRajJ 哦,是的! ngOnChanges 成功了!谢谢! 很高兴它有帮助:) 我已经添加了这个答案。 【参考方案1】:

设置图表的getGeoData 方法仅在组件初始化期间调用,并且在@Input 值更改时不会运行。对于这些场景,Angular 提供了ngOnChanges 生命周期钩子。这就是我们需要告诉 Angular 当@Input 值发生变化时需要做什么的地方。

ngOnChanges(changes: SimpleChanges) 
  for (const propName in changes) 
    switch(propName) 
      case 'url':
      // action that needs to happen when url changes
      break;
      case 'chartProjection':
      // action that needs to happen when chartProjection changes
      break;
    
  

【讨论】:

以上是关于Storybook:更改控件的值不会重新呈现 Chart.js 画布的主要内容,如果未能解决你的问题,请参考以下文章

React Context 和 Storybook:在组件故事中更改 React Context 的值

使用 ContextAPI 不会重新呈现其使用者,但会更改内容。为啥?

上下文更改时组件不会重新呈现

React 页面不会在 url 查询更改时重新呈现

如果没有在 URL 更改上重新加载页面,React 不会呈现

反应输入值不会在按钮更改时重新呈现