在Angular2

时间:2017-04-08 17:55:50

标签: angular typescript observable

我有来自Firebase的可观察流的数据流。即

items: FirebaseListObservable<BucketlistItem[]> = [{
  room: 'kitchen', 
  price: 200
}, {
  room: 'kitchen', 
  price: 400
}, {
  room: 'bedroom', 
  price: 400
}]

我想把它变成一个聚合列表,这样可以增加类似房间的价格。

[{
  room: 'kitchen', 
  price: 600
}, {
  room: 'bedroom', 
  price: 400
}]

通过订阅可观察的项目并在运行中聚合它以在条形图中显示它,我已经实现了这一点。但是,这似乎不是最佳解决方案,尤其是因为items数组经常在视图中更改。是否有一种聪明的方法可以使用observable来获得更强大的解决方案?

import {Component, Input, OnInit, Output, EventEmitter} from '@angular/core';
import {BucketlistItem} from "../../bucketlist/bucketlist-item";
import {FirebaseListObservable} from "angularfire2";

@Component({
  selector: 'app-inspiration-header',
  templateUrl: './inspiration-header.component.html',
  styleUrls: ['./inspiration-header.component.css']
})
export class InspirationHeaderComponent implements OnInit, Input {
  @Input() items: FirebaseListObservable<BucketlistItem[]>;

  @Input() budget: string;
  @Output() onBudget = new EventEmitter<string>();
  public percentOfBudget: number;
  public totalCost: number;

  public barChartLabels: string[] = [];

  public barChartData: any =
    [{data: []}]
  ;

  constructor() { }

  ngOnInit() {
    this.items
      .subscribe(items => {
        const aggregate = {};
        let totalCost = 0;
        for (const item of items) {
          if (item.bucketed) {
            totalCost += Number(item.price);
            if (item.room in aggregate) {
              aggregate[item.room] += Number(item.price);
            } else {
              aggregate[item.room] = Number(item.price);
            }
          }
        }
        this.setPercentageOfBudget(totalCost);
        this.totalCost =  totalCost;
        for (const room in aggregate) {
          this.barChartLabels.push(room);
          this.barChartData[0].data.push(aggregate[room]);
        }
        this.barChartLabels = Object.keys(aggregate);
      });
  }

  private setPercentageOfBudget(totalCost: number) {
    this.percentOfBudget = Math.round(totalCost / Number(this.budget) * 100);
  }

}

1 个答案:

答案 0 :(得分:2)

根据您所描述的内容,您可能希望使用扫描运算符。

扫描操作符将在其源可观察值发出值时发出。但是,对扫描运算符的回调将同时接收源可观察量发出的当前值,以及过去提交的accumulated value(概念与Array.reduce的工作方式类似)。

这听起来很复杂,但实际上这意味着每次您的源可观察值发出一个值时,您的扫描操作符会将该新值与“旧值”组合,然后发出新的组合值。

你仍然需要编写逻辑来将新值与旧的累积值相结合,但我认为最终的observable会让你更接近你想要的。

// don't forget to import your operator
import 'rxjs/add/operator/scan';
.....


  @Input() items: FirebaseListObservable<BucketlistItem[]>;
  combinedItems: Observable<BucketlistItem[]>;

  ngOnInit() {
    this.combinedItems = items.scan((accumulated, current) => {
      return this.combine(accumulated, current);
    });
  }

  combine(total: BucketListItem[], newItem: BucketListItem[]) {
    // implement your similar logic of combining 
    // [{room: 'kitchen', price: 200}]  <-- accumulated 
    // with [{room: 'kitchen', price: 200}, {room: 'kitchen', price: 400}]  <--- current
    // to give your final output
  }

无法实现组合逻辑(尽管您可以创造性地使用Array.reduce来简化代码)。