如何从使用 rjxs 从 API 中获取值的服务 return 映射字符串?

How to return a mapped string from a Service that fetches values from an API with rjxs?

我在 Angular 中有一项名为 MappingService 的服务 7. 该服务从 API:

中获取姓名列表
{
  'guid1': 'Item 1',
  'guid2': 'Item 2',
  'guide': 'Item 3',
}

这些值稍后应该由它们的 ID 映射。当然我可以在 componentn 中订阅 items$ 然后做类似的事情:

{{ mapping[id] }}

但我认为这会很好,如果我不必订阅该服务或其列表,那么我就不必一直传递它,因为映射一旦加载就固定了并且是多个组件需要。

所以我想我添加了一个方法 map 到执行此操作的服务:

喜欢:

{{ mappingSrervice.map(id) }}

但这就是我卡住的地方:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable, of } from 'rxjs';
import { mergeMap, tap, catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})

export class MappingService {
  items$ = new BehaviorSubject<any>(null);

  constructor(private http: HttpClient) { }

  getAll(): Observable<any> {
    return this.items$.pipe(mergeMap(items => {
      if (items) {
        return of(items);
      } else {
        return this.http.get<any>(
          'http://api'
        ).pipe(
          tap((response: any) => response),
          catchError(error => {
            return of([]);
          })
        );
      }
    }));
  }

  map(id: number): string {
    return this.items$[id] || null;
  }
}

如何正确实施 map()?我应该在构造函数中 运行 getAll() 吗?问题当然还是在getAll()完成之前items$不会被填满

我可以让它像我想的那样工作吗? string 是正确的 return 类型吗?

不知这种做法是否可行?还是我需要 return promises 来代替?但这会产生一个新的抽象层次,使它不再那么简单,而不是简单的方法(只需订阅 items$.

Resolver可能对你有帮助。

In summary, you want to delay rendering the routed component until all necessary data have been fetched.

如果您在解析器的 resolve() 方法中 return ObservablePromise,则在 Observable 完成或Promise 已完成。 在您的组件中,您可以使用 route.snapshot.data.xxx.

访问解析器提供的数据

这种方法的缺点是它会延迟组件的呈现。 实际上,我认为在这种情况下,简单地订阅 items$ 会更好。

先说说缓存。您已经尝试使用 BehaviorSubject,这是个好主意。与异步操作一样,数据在一段时间内不可用,因为它仍在加载。 这不是问题,我们可以在我们的组件中处理这个问题。 在应用程序加载之前没有真正酷的方法来执行请求(有一些方法,但我不想推荐其中的任何一种)。 说到这里,a BehaviorSubject 并不是最好的选择,因为我们总是需要用一个值来初始化它。 更合适的是ReplaySubject:它将最后的元素保留在其缓冲区中并将它们重播给每个新订阅者。

虽然按照您的方式做这一切很酷,但我们可以做得更简单:使用 shareReplay() 运算符。 这使得一个冷的 Observable 成为热的,这意味着:它将普通的 HTTP Observable(为每个订阅者发出 HTTP 请求)转换为一个共享的 Observable,它只执行一次任务并与所有订阅者共享它的值。

代码可能如下所示:

export class MappingService {

  items$ = this.getAll().pipe(shareReplay(1));


  getAll(): Observable<any>() {
    return this.http.get('http://api'); // also add catchError here as you already did
  }

}

对于第一个订阅者,将调用 getAll() 并执行 HTTP 请求。对于所有后续订阅者,将使用缓存的值。 请注意,您仍然需要订阅 items$ Observable,但在组件中订阅 Observable 不是问题或挑战。

对于映射,我建议使用 RxJS map() 运算符。 你应该提供一个服务方法,它接受一个 ID 和 returns 一个带有结果的 Observable。它可能看起来像这样:

getItem(id: number): Observable<string> {
  return this.items$.pipe(
    map(items => items[id])
  );
}

这样——在任何地方都使用 Observables——你也可以通过异步操作避免竞争条件。您的每个 Observables 都会 return 数据到达后立即。