【问题标题】:Angular - Assigning mutiple Observable arrays from a Service API callAngular - 从服务 API 调用分配多个 Observable 数组
【发布时间】:2019-05-21 23:20:12
【问题描述】:

我有一个调用 API 以返回 Observable Observable<MeasuringPoint[]> 的服务

我希望能够为该服务的结果订阅多个变量。每个变量 MeasuringPoints$、MeasuringPointsViewed$、LatestReadings$ 都需要相同的数据,但会以不同的方式解析它。他们还需要彼此不了解。

关于最佳方式的任何想法?

【问题讨论】:

  • 能分享一下 MeasurementPoint[] 接口的数据结构吗?

标签: angular rxjs


【解决方案1】:

所以我过去所做的是创建一个 RXJS 存储,任何组件都可以注入和使用数据结果。

some-data-store.service.ts

export class SomeDataStoreService {
  // Create a behavior subject that can be changed at will, always houses the "current" value.
  // Allows you to always get the latest emmited value.
  private _measuringPoint = new BehaviorSubject<MeasuringPoint[]>(null);

  // Expose the BeahviorSubject as an observable
  measuringPoint: Observable<MeasuringPoint[]> = this._measuringPoint.asObservable();

  // Inject your api service
  constructor(private measuringPointApi: MeasuringPointApiService) {}

  //Use Api to populate Behavior subject
  getMeasuringPoint() {
    this.measuringPointApi
      .getMeasuringPoint()
      .subscribe(data => this._measuringPoint.next(data));
  }
}

some-consumer.component.ts

export class SomeConsumerComponent implements OnInit {
  MeasuringPoints$: MeasuringPoint[];

  // Inject the DataStoreService
  constructor(private someDataStore: SomeDataStoreService) {}

  ngOnInit() {
    // Request the data initially
    this.someDataStore.getMeasuringPoint();

    // Subscribe to the public member measuringPoint of the dataStoreService.
    // NOTE: You can use this piece over and over in different components, they'll just fire off whenever data is changed.
    this.someDataStore
      .measuringPoint()
      .subscribe(data => (this.MeasuringPoints$ = data));
  }
}

这只是一个例子,你显然需要你的导入和什么不需要,但这应该会让你朝着正确的方向前进。肯定有其他方法可以做到这一点,NGRX 例如https://ngrx.io/,但它是一个完整的状态管理库。我建议你在深入研究之前做更多的研究,看看你是否需要像 NGRX 这样强大的东西。

【讨论】:

  • 感谢@Budhead2004 的出色回答一件事。我需要结果是一个可观察的列表,因为我使用它是异步的。我注意到 MeasurementPoints$: 是一个数组。
  • 数据存储 observable 根据您的原始问题返回一种测量点[]。我不确定你我明白你的意思,你能详细说明一下吗?
【解决方案2】:

我想到了这样的事情:

export class ConsumerComponent implements OnInit {
    mesuringPoint$: Observable<MesurePointModel>;
    mesuringPointView$: Observable<MesurePointModelView>;
    latestReadings$: Observable<number>;

    constructor(private service: DataStoreService) {
        this.mesuringPoint$ = this.service.getData().pipe(// rxjs operators);
        this.mesuringPointView$ = this.service.getData().pipe(// rxjs operators );
        this.latestReadings$ = this.service.getData().pipe( // rxjs operators );


    ngOnInit() {
       // Subscribe here or anywhere else to your observables
       this.latestReadings$.subscribe(latestReadings => // number of latest readings)
    }


}

重要的部分是使用 RXJS 来转换或组合运算符,这样您就可以创建新的 observable,将您的服务的 observable 作为输入,然后通过管道将其创建为您自己的。

RXJS Operators

【讨论】:

    【解决方案3】:

    我以前通过以下方式完成此操作。就像 Budhead2004 所说,您绝对应该做一些研究,并找到适合您需求的良好状态管理解决方案。这个方法是我自己稍微滚动的,但它可以为较小的应用程序或代码部分完成工作。

    这里是一个堆栈闪电战,展示了这一点:https://stackblitz.com/edit/angular-7gjfgt

    store.service.ts 管理数据的状态并将MeasuringPoints 的数组映射到每个点的各个可观察对象。

    store.service.ts

    import { Injectable } from '@angular/core';
    import { Subject, BehaviorSubject, Observable, from } from 'rxjs';
    import { mergeMap, tap } from 'rxjs/operators'
    import { MeasuringPoint } from './measuring-point';
    
    const STUB_DATA: MeasuringPoint[] = [
      { data: 'fake data 1' },
      { data: 'fake data 2' },
      { data: 'fake data 3' },
    ];
    
    @Injectable({ providedIn: 'root' })
    export class StoreService {
    
      // If you receive the data as an array, you will want to store it as an array
      private _subject: Subject<MeasuringPoint[]> = new BehaviorSubject([]);
    
      // Simple state management. Could switch this out for
      //  a caching library or NGRX
      private loadedData: boolean = false;
    
      constructor(/* inject needed services (ie. http, etc) */) { }
    
      // Return single MeasuringPoint objects
      public getAllMeasuringPoints$(): Observable<MeasuringPoint> {
        // If you haven't loaded the data yet, load it now
        if (!this.loadedData) {
          this._subject.next(STUB_DATA);
          this.loadedData = true;
        }
    
        return this._subject
          // return the observable and not the subject
          .asObservable()
          // Convert the array of MeasuringPoints to emit for each
          //  value in the array
          .pipe(
            // Log values for demonstration
            tap((values: MeasuringPoint[]) => console.log('Values before mapping: ', values)),
            // convert an observable of an array to individual observables
            mergeMap((points: MeasuringPoint[]) => from(points)),
            // Log values for demonstration
            tap((value: MeasuringPoint) => console.log('Values after mapping: ', value)),
          );
      }
    }
    

    然后您可以订阅公开的方法来获取您的数据。任何组件都可以订阅它。因为它是一个BehaviorSubject,所以订阅者总是会得到最近发出的值。关于BehaviorSubject 需要注意的另一件事是,由于 store.service 中的 observable 永远不会完成,因此您的组件在销毁时需要取消订阅。否则,您将在整个应用程序中出现内存泄漏。

    first-component.component.ts -- 注意:second-component.component.ts 几乎是这个的副本

    import { Component, OnInit, OnDestroy } from '@angular/core';
    import { Subject } from 'rxjs';
    import { StoreService } from './store.service';
    import { takeUntil } from 'rxjs/operators';
    import { MeasuringPoint } from './measuring-point';
    
    @Component({
      selector: 'app-first-component',
      template: `
        <h3>First Component</h3>
        <div *ngFor="let point of measuringPoints">
          {{point | json}}
        </div>
      `
    })
    export class FirstComponentComponent implements OnInit, OnDestroy {
    
      public measuringPoints: MeasuringPoint[] = [];
      // Keep track of subscriptions
      private _endSubscriptions: Subject<null> = new Subject();
    
      constructor(private _storeService: StoreService) { }
    
      ngOnInit() {
        this._storeService.getAllMeasuringPoints$()
        // This is to avoid memory leaks by unsubscribing when your component destroys
        .pipe(takeUntil(this._endSubscriptions))
        .subscribe((point: MeasuringPoint) => this.measuringPoints.push(point));
      }
    
      ngOnDestroy() {
        this._endSubscriptions.next();
        this._endSubscriptions.complete();
      }
    
    }
    

    这里是 RxJS 的文档from()https://www.learnrxjs.io/operators/creation/from.html

    这应该会给您想要的结果。如果没有,请告诉我。干杯!

    【讨论】:

      猜你喜欢
      • 2017-07-08
      • 2016-08-25
      • 1970-01-01
      • 2021-07-06
      • 2018-11-16
      • 2020-12-28
      • 2018-06-25
      • 2018-02-14
      • 1970-01-01
      相关资源
      最近更新 更多