【问题标题】:How to update an array of observables using interval?如何使用间隔更新可观察的数组?
【发布时间】:2017-03-06 16:48:45
【问题描述】:

我正在尝试使用 angular 2 和 chart.js(通过 ngcharts)为图表构建仪表板。我想要一组图表,每个图表都通过自定义时间间隔的 http 请求进行更新。

现在我有三个单独的图表调用,它们将数据推送到数组中。当涉及到下一次迭代时,我遇到了麻烦——如果我再次推送到数组,我最终会得到 3 个图表。我希望数组中的订阅者在间隔发出新数据时更新它。

对于如何为我的用例正确构建组件/服务/http 关系,我有点困惑。我觉得我很接近,但我肯定错过了一些东西。如何获取间隔/订阅者关系以映射到视图并按间隔更新现有图表? 任何帮助都会很棒!

现在:

服务:

我在这里实现间隔:

getSingleChartObsinterval(id: number, interval: number) : Observable<Graph> {

return Observable.interval(interval).flatMap(() => this.getSingleChartobs(id));        
}


getSingleChartobs(id: number) : Observable<Graph> {

    return this.jsonp.get(“api location”)
            .map(response => this.extractJsonData(response, id) as Graph)
}

extractJsonData 只是获取响应并对其进行操作以与图表 JS 一起使用。它返回一个 Graph 对象,该对象具有易于使用的属性。我无法控制 API,因此无法重新配置响应以包含多个图表。

组件:

import { Component } from '@angular/core';
import { ChartsModule } from 'ng2-charts/ng2-charts';
import { ChartService } from './chart.service';
import { Graph } from './graph';
import { OnInit } from '@angular/core';
import { Observable } from 'rxjs/Rx';


@Component({
  selector: 'ab-chart',
  styles: [`
    .chart {
      display: block;
    }
  `],
  templateUrl: 'app/chart.component.html'
})
export class ChartComponent implements OnInit {

    ngOnInit(): void {
      console.log("Chart component init");

      this.getSingleChart(3, 5000);
      this.getSingleChart(5, 4000);
      this.getSingleChart(6, 5000);
    }

    graph: Graph;
    graphs: Graph[] = [];


    constructor(
        private chartService: ChartService
    ) {}


  getSingleChart(id: number, interval: number): void {
     this.chartService.getSingleChartObsinterval(id, interval)
      .subscribe(x => 
       this.graphs.push(x)
      );
}
}

观点:

<div *ngFor="let graph of graphs" class="chart-container">
    <base-chart
        class="chart"
        [datasets]="graph.datasets"
        [labels]="graph.labels"
        [options]="graph.options"
        [chartType]="graph.type">
      </base-chart>
  </div>

【问题讨论】:

  • 不是在你的数组中推送新的图表,你不能用新的数据替换现有的图表吗?我对chart.js一无所知,所以也许无法替换他的数据而只是等待角度来呈现新图表
  • @NoémiSalaün 我想这就是我要问的。每个图表都在不同的间隔上,所以我希望我可以让数组中的每个项目订阅更改并更新自身,而不必吹走数组。我尝试将每个对象作为订阅者推送到数组,但它不起作用。我希望有一种方法可以做到这一点,而无需在数组中搜索 ID 并自己更新数据。

标签: javascript angular rxjs rxjs5


【解决方案1】:

您的图表数量有限吗?如果你总是有三个,你可以利用combineLatest 运算符(如果你有更多,我猜你将不得不使用某种形式的递归)。

在您的组件中,您可以执行以下操作:

 this.graphs = this.getSingleChart(3, 5000).combineLatest(
        this.getSingleChart(5, 4000),
        this.getSingleChart(6, 5000),
        (val1, val2, val3) => return [val1, val2, val3])
        //.subscribe((arrayOfVal) => console.log(arrayOfVal);

每次更新其中一个图表时,这将返回一个新数组。如果图表 2 有新值,函数(combineLatest 的第三个参数)将被调用,旧值 1、新值 2 和旧值 3。

在您的模板中,您可以使用这个:

 <div *ngFor="let graph of graphs | async" ...>

结合最新:https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/combinelatest.md

【讨论】:

  • 实际上这不会像这样工作,因为combineLatest() 返回一个 Observable,而不是回调返回的值。这也意味着您必须一次刷新所有图表。
  • 它确实会返回一个 observable。但是,这就是重点,在这种情况下,可观察对象可以在模板中与异步管道一起使用。并且 combineLatest 不会强制重新更新每个图表。在幕后,这将保存所有三个调用的最新值。例如,A1、B1 和 C1。如果 B1 更新为 B2,则下一个值将是 A1、B2、C1。看到 A 和 C 引用没有改变。因此,它与您的答案相同。但更优雅的是 AFAIAC,因为您之后不需要清理订阅。
  • 您对async 的看法是正确的,我没有注意到这一点。但是,它必须立即更新所有图表,而不是像您描述的那样,因为每次更改 graphs | async 都会收到一个新数组,然后它将再次破坏并重新创建所有三个 &lt;base-chart&gt;s 元素。但你的解决方案肯定比我的简单。
  • 我希望在下面,基本图表组件会检查参考:)。或者它会遍历数组并让子组件将 changeDetection 策略设置为 onPush。
  • 我希望我可以将两者都标记为答案,因为它们都以不同的方法解决问题。另一个答案更接近我现有的方法,也是我采用的方法,但这个答案也帮助我使用了 combineLatest 函数并进一步理解了异步管道。感谢您的回答。
【解决方案2】:

由于每个图都有自己的id(我假设它是唯一的),所以我只需更改getSingleChart() 方法以更新特定键处的graphs 对象。注意我将graphs 属性从数组更改为对象:

graphs: {[key: number]: Graph} = {};

getSingleChart(id: number, interval: number): void {
  this.chartService.getSingleChartObsinterval(id, interval)
    .subscribe(x => this.graphs[id] = x);
}

get graphIds() {
    return Object.keys(this.graphs);
}

然后在模板中你需要迭代键的数组(你可以迭代graphs对象:

<div *ngFor="let id of graphIds" class="chart-container">
    <base-chart
        class="chart"
        [datasets]="graphs[id].datasets"
        [labels]="graphs[id].labels"
        [options]="graphs[id].options"
        [chartType]="graphs[id].type">
    </base-chart>
</div>

【讨论】:

  • 感谢您的回答。这两个答案都有效,但这更接近我一直在尝试的方法,并且对我现有的方法造成的改变最少。
猜你喜欢
  • 2016-11-26
  • 1970-01-01
  • 1970-01-01
  • 2013-12-16
  • 1970-01-01
  • 1970-01-01
  • 2013-11-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多