图形无法使用角度4正确渲染系列

时间:2018-03-23 21:58:08

标签: angular highcharts

我使用角度4实现了样条曲线图。我试图通过点击按钮向图表添加系列。当用户单击“应用”按钮时,应在图表中添加一系列。我正在使用图表的问题不会刷新以显示添加的系列。用户必须清除浏览器历史记录并重新加载页面才能看到新添加的系列。

我注意到代码的一个问题是,添加系列的代码是用OnInit方法编写的,这意味着它只触发一次。每次页面加载时我都会将其更改为调用。父组件将结果对象传递给承载图形的子组件。结果组件包含数据和系列对象。

我对新代码的问题是,当用户单击“应用”按钮时,它会尝试添加之前添加的相同系列,并将这些系列绑定到图表。结果,重复的系列被添加到图表中。有些人可以告诉我问题是什么吗?

父组件

import { Component, OnInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { RunService, NavBarService } from '@wtw/platform/services';
import { Base } from '@wtw/toolkit';
import { NpvAnalysis, EvAnalysis } from '../../../shared/models/results';
import { Dto } from '@wtw/platform/api';
import { Strategy, StressTestAnalysis, CaptivesRun, EndingSurplus } from '../../../api/dtos';
import { RunModel } from '@wtw/platform/api/dtos';

@Component({
  selector: 'app-results',
  templateUrl: './results.component.html'
})
export class ResultsComponent extends Base.ReactiveComponent implements OnInit {
  run: CaptivesRun;
  stressTestResults: Array<StressTestAnalysis> = [];
  forceRedraw: { value: number };
  private _baseRun: Dto.RunModel;

  constructor(
    private _runService: RunService,
    private _navBarService: NavBarService,
    private _translate: TranslateService,
  ) {
    super();
  }

  ngOnInit() {
    this._subscriptions = [
      this._runService.activeRun.subscribe((r: any) => {
        this._processRun(r);
      }),
      this._runService.currencyConverted.subscribe(r => {
        this._processRun(r);
        this.save();
        this.forceRedraw = { value: Math.random() * 10000 };
      }),
      this._navBarService.downloadReportEvent.subscribe(x => {
        this.downloadReport();
      })
    ];
  }

  downloadReport() {
    console.log('download report');
  }

  applyChange(event: any) {
    this.run.strategies.splice(event.index, 1, event.strategy);
    this._baseRun.data = this.run;
    this._runTrigger2(this._baseRun, event.index);
  }

  save() {
    this._baseRun.data = this.run;
    this._runService.persist(this._baseRun.runId, this.run, this._baseRun.currencyInfo).uiSignal('save').subscribe(x => {
      this._processResults(this.run.strategies);
    });
  }

  private _runTrigger2(r: Dto.RunModel, strategyIndex: number) {
    this._runService.executeTrigger(r.runId, this.run, { number: 2, param: strategyIndex.toString() }, r.currencyInfo)
      .uiSignal('trigger 2')
      .subscribe(x => {
        this.run = x.data;
        this._processResults(x.data.strategies);
      });
  }

  private _processRun(r: RunModel) {
    this._baseRun = r;
    this.run = r.data as CaptivesRun;

    // Initialising the data
    if (this.run.strategies) {
      if (!this.run.strategies[0].results) {
        this._runTrigger2(this._baseRun, 0);
      } else {
        this._processResults(this.run.strategies);
      }
    }
  }

  private _processResults(strategies: Array<Strategy>) {

    this.stressTestResults = new Array();
    const strategyTranslation = this._translate.instant('CAPTIVES.RESULTS.COMMON.STRATEGY');

    const getStrategyName = (strategy: Strategy, index: number) => {
      let name = this._translate.instant('CAPTIVES.RESULTS.COMMON.BASE_STRATEGY');
      if (index > 0) {
        name = strategyTranslation + ' ' + index;
      }
      return name;
    };

    strategies.forEach((strategy, index) => {
      const strategyName = getStrategyName(strategy, index);
      const results = strategy.results;
      this.stressTestResults.push(results.stressResult);
    });
  }
}

子组件

import { Component, OnInit, Input } from '@angular/core';
import { StressTestAnalysis } from '../../../../api/dtos';
import { ReactiveComponent } from '@wtw/toolkit/src/utils/base.component';

export interface ChartSeries {
  data: number[];
  name: string;
  color: string;
}

export interface YAxisSeries {
  yaxis: number[];
}

@Component({
  selector: 'app-stress-test-analysis',
  templateUrl: './stress-test-analysis.component.html'
})

export class StressTestAnalysisComponent extends ReactiveComponent implements OnInit {
  isExpanded = false;
  showTable = true;
  public chartSeries: Array<ChartSeries> = [];
  yAxisSeries: Array<YAxisSeries> = [];
  yAxisData: number[] = [];
  minYAxis: number;
  maxYAxis: number;
  seriesName: string;
  public results: Array<StressTestAnalysis> = [];
  private _stressResults: Array<StressTestAnalysis> = [];

  @Input() set stressResults(value: Array<StressTestAnalysis>) {
    this.results = value;
    this.addSeries();
    let minY = Math.min(...this.yAxisSeries.map(el => Math.min(...el.yaxis)));
    let maxY = Math.max(...this.yAxisSeries.map(el => Math.max(...el.yaxis)));
    this.generateYAxisArray(minY, maxY);
  }

  constructor( ) { super(); }

  ngOnInit() {
  }

  private addSeries() {
    if (this.results === null) {
      return;
    }


    this.results.forEach(element => {
      if (element.data !== null)
        this.chartSeries.push({ data: element.data, name: element.seriesName, color: element.color });
       if (element.yaxis !== null)
        this.yAxisSeries.push({ yaxis: element.yaxis });
    });
  }

  //Function that generates the array based on the min and max derived from the previous method
  private generateYAxisArray(min: any, max: any) {
    let count = min;
    for (count = min; count <= max; count = count + 500000) {
      this.yAxisData.push(count);
    }
  }


}

子组件html

<div class="card">
  <!-- Stress Test Analysis -->
  <div class="card-header" role="tab" id="sta_heading">
    <a data-toggle="collapse" (click)="isExpanded = !isExpanded" href="javascript:void(0);" role="button" [attr.aria-expanded]="isExpanded"
      aria-controls="accordion3" [ngClass]="{'collapsed': !isExpanded}">
      <h5 class="mb-0">{{'CAPTIVES.RESULTS.STA.TITLE'|translate}}</h5>
    </a>
  </div>
  <div [ngClass]="{'show': isExpanded}" id="accordion3" class="collapse" role="tabpanel" aria-labelledby="accordion3_heading"
    data-parent="#accordion" [attr.aria-expanded]="isExpanded">
    <div class="card-body">
      <ul class="nav nav-pills mb-3" id="pills-tab" role="tablist">
        <li class="nav-item">
          <a href="javascript:void(0);" [ngClass]="!showTable ? '' : 'active' " class="nav-link " id="table-tab" data-toggle="pill"
                        role="tab" aria-controls="table" (click)="showTable = !showTable" aria-selected="true">{{'CAPTIVES.RESULTS.COMMON.TABLE'|translate}}</a>
        </li>
        <li class="nav-item">
         <a href="javascript:void(0);" [ngClass]="showTable ? '' : 'active'  " class="nav-link" id="chart-tab" data-toggle="pill"
            role="tab" aria-controls="chart" (click)="showTable = !showTable" aria-selected="false">{{'CAPTIVES.RESULTS.COMMON.CHART'|translate}}</a>
        </li>
      </ul>
      <div class="tab-content" id="pills-tabContent">
        <!-- sta table -->
        <div *ngIf="showTable" class="tab-pane fade show active" id="base-strategy-table--sta" role="tabpanel" aria-labelledby="table-tab">
          <div class="tb-container">
            <div class="tb-row d-flex flex-row">
              <div class="tb-cell col-sm-6 col-md-3 col-lg-2 col-6"></div>
              <div *ngFor="let result of results;" class="tb-cell col-sm-6 col-md-3 col-lg-2 col-6">
                <h6>{{'CAPTIVES.RESULTS.STA.AVERAGE_SURPLUS'|translate}}</h6>
              </div>
            </div>
            <div class="tb-row d-flex flex-row">
              <div class="tb-cell col-sm-6 col-md-3 col-lg-2 col-6">{{'CAPTIVES.RESULTS.STA.CURRENT_LEVEL_CAPTILIZATION'|translate}}</div>
              <div *ngFor="let result of results;" class="tb-cell col-sm-6 col-md-3 col-lg-2 col-6">{{result?.curlevel|percent:'.1-2'}}</div>
            </div>
            <div class="tb-row d-flex flex-row">
              <div class="tb-cell col-sm-6 col-md-3 col-lg-2 col-6">{{'CAPTIVES.RESULTS.STA.TEN_PERCENT_LESS_CURRENT_LEVEL_CAPTILIZATION'|translate}}</div>
              <div *ngFor="let result of results;" class="tb-cell col-sm-6 col-md-3 col-lg-2 col-6">{{result?.decrease|percent:'.1-2'}}</div>
            </div>
            <div class="tb-row d-flex flex-row">
              <div class="tb-cell col-sm-6 col-md-3 col-lg-2 col-6">{{'CAPTIVES.RESULTS.STA.TEN_PERCENT_MORE_CURRENT_LEVEL_CAPTILIZATION'|translate}}</div>
              <div *ngFor="let result of results;" class="tb-cell col-sm-6 col-md-3 col-lg-2 col-6">{{result?.increase|percent:'.1-2'}}</div>
            </div>
          </div>
        </div>
        <!-- sta table End -->
        <!-- sta Chart -->
        <div *ngIf="!showTable" class="tab-pane base-strategy-chart fade show active" id="base-strategy-chart--nva" role="tabpanel"
          aria-labelledby="chart-tab">
          <div class="tb-container">

            <div class="tb-row d-flex flex-row">
              <div class="tb-cell col-12 pt-5">
               <splinechart [series]="chartSeries" [yaxisdata]="yAxisData">
               </splinechart>
                </div>

            </div>
          </div>

          <!-- sta Chart End -->
        </div>
      </div>
    </div>
  </div>
  <!-- Card + Stress Test Analysis End-->

样条图组件

import { Component, Input, OnChanges } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';

@Component({
    selector: 'splinechart',
    template: '<chart [options]="options" (load)="getInstance($event.context)"></chart>',
    styles: [`
    chart {
        display: block;
        width: 100% !important;
         padding:0;
      }`]
})

export class SplineChartComponent implements OnChanges {
    public options: any;
    chart: any;

    @Input() public series: any;
    @Input() public yaxisdata: any;
    @Input() public selectedRating: string = '';

    constructor(private _translate: TranslateService) {
        this.options = {
            credits: {
                enabled: false
            },
            chart: {
                type: 'spline'
            },
            title: {
                text: ''
            },
            subtitle: {
                text: ''
            },
            legend: {
                layout: 'horizontal',
                margin: 25,
                itemMarginTop: 0,
                symbolRadius: 0,
                symbolHeight: 20,
                symbolWidth: 20,
                useHTML: true,
                    title: {
                    text: this._translate.instant('CAPTIVES.RESULTS.COMMON.GRAPH_LEGEND_TITLE'),
                    margin: 50,
                    style: {
                        fontStyle: 'italic',
                        fontWeight: 'normal'
                    }
                },
                align: 'right',
                verticalAlign: 'bottom',
            },
            xAxis: {
                title: {
                    text: this._translate.instant('CAPTIVES.RESULTS.STA.GRAPH_XAXIS')
                }
            },
            yAxis: {
                title: {
                    text: this._translate.instant('CAPTIVES.RESULTS.STA.GRAPH_YAXIS')
                }
            },

            tooltip: {

            },
            plotOptions: {
                series: {
                    cursor: 'pointer',
                    events: {

                        legendItemClick: function() {
                            const elements = document.querySelectorAll('.highcharts-legend-item path');
                            for (let i = 0; i < elements.length; i++) {
                                elements[i].setAttribute('stroke-width', '20');
                                elements[i].setAttribute('stroke-height', '20');
                            }
                            this.chart.redraw();
                        }

                    },

                    allowPointSelect: true,

                },
                spline: {
                    lineWidth: 2,
                    states: {
                        hover: {
                            lineWidth: 3
                        }
                    },
                    marker: {
                        enabled: true,
                        symbol: 'circle'

                    },
                }
            },
            series: [
                {
                    showInLegend: false
                }
            ]
        };
    }

    getInstance(chartInstance): void {
        this.chart = chartInstance;
        this.redraw();
    }

    ngOnChanges(data: any) {
        if (!data.series.currentValue || !this.chart) return;
        data.series.currentValue.map(s => {
            this.chart.addSeries(s);
        });
        this.chart.reflow();
    }

    redraw() {
        if (!this.chart) return;
        this.chart.yAxis[0].categories = this.yaxisdata;

        this.series.map(s => {
            if (s !== null)
                this.chart.addSeries(s);
        });

        const elements = document.querySelectorAll('.highcharts-legend-item path');
        for (let i = 0; i < elements.length; i++) {
            elements[i].setAttribute('stroke-width', '20');
            elements[i].setAttribute('stroke-height', '20');
        }
        this.chart.redraw();

    }

}

1 个答案:

答案 0 :(得分:1)

  1. 您是否在Highcharts中使用任何Angular包装器?有一位官员:https://github.com/highcharts/highcharts-angular并且有一些非官方的:https://www.npmjs.com/package/angular2-highcharts这些将有助于动态事件,例如添加新系列和Angular中的一般Highcharts用法。

  2. 代码this.chart.yAxis[0].categories = this.yaxisdata;并不好。如果要安全更新轴的类别,请使用Axis.update()

  3. legendItemClick中的图例项目更改可能无效 - 项目状态包含API文档: