【问题标题】:Filter an observable based on value comping from an API call根据来自 API 调用的值过滤可观察对象
【发布时间】:2022-12-11 02:12:08
【问题描述】:

我是 RxJS 的新手,任何帮助将不胜感激!

在我的组件 html 模板中,我想生成一个单选按钮列表。

该列表通过异步管道填充了来自可观察对象的值。

这是单选按钮列表的模板部分:

<div *ngIf="entities$ | async as entities">
    <mat-radio-group
        aria-labelledby="entities-radio-group-label"
        class="entities-radio-group"
        <mat-radio-button *ngFor="let entity of entities" [value]="entity">
          {{entity.description}}          
        </mat-radio-button>
      </mat-radio-group>
</div>

在组件类上,我有一个使用服务检索实体数据的可观察对象。 我还按实体类型过滤实体。

  entities$: Observable<Entity[]> = this.eventsService.entities$
  .pipe(
    map(items => items.filter(item => item.type.toLowerCase().indexOf("set") > -1)),
    )

检索到的实体对象具有以下结构:

[
{"id":"34534534643364",
 "type":"SET",
 "description":"some description",
 "link":"/conf/sets/34534534643364"},
{"id":"5474745744457547",
 "type":"SET",
 "description":"other description",
 "link":"/conf/sets/5474745744457547"}
 ]

这部分有效,我可以显示“SET”类型的实体。

但我还需要根据需要通过 API 调用检索的附加值来过滤实体列表。 对于此源可观察对象中的每个实体,我需要发出一个使用指定实体的请求关联

这是请求的样子(我正在使用服务)

 restService.call<any>(entityUrl)
 .pipe(finalize(()=>this.loading=false))
 .subscribe(
    apidata => console.log(`data: ${JSON.stringify(apidata)}`),
    error => this.alert.error('Failed to retrieve entity: ' + error.message)
    );

这将返回一个可观察对象,数据基本上是一个像这样的对象:

{
    "id": "34534534643364",
    "name": "some name",
    "description": null,
    "type": {
        "value": "LOGICAL",
        "desc": "Logical"
    },
    "content": {
        "value": "IEP",
        "desc": "This it the value I need"
    },
    "status": {
        "value": "ACTIVE",
        "desc": "Active"
    }
}

我需要使用“desc”的值来执行额外的过滤。

我试图使用一个函数来执行额外的过滤,并将其添加到源可观察对象中。

可观察的:

  entities$: Observable<Entity[]> = this.eventsService.entities$
  .pipe(
    tap((items) => console.log("started pipe", items)),
    map(items => items.filter(item => item.type.toLowerCase().indexOf("set") > -1)),
    tap((items) => console.log("after set filtered pipe", items)),
    map(items => items.filter(item => this.descFilter(item.link))),
    tap((items) => console.log("after descFilter: ", items)),
    tap(() => this.clear())
    );

功能:

 descFilter(link: string): boolean{
    let testedEnt:any = [];
    resObs$ = this.restService.call<any>(link)
    .pipe(finalize(()=>this.loading=false))
    .subscribe(
        next => {console.log(`checkTitleSet api call result:, ${JSON.stringify(next)}`);
                 testedEnt = next.content.desc;
                 console.log("desc from inside next: ",testedEnt);}, // this retrieved the value
       error => this.alert.error('Failed to retrieve entity: ' + error.message)
     );

    console.log("desc: ",testedEnt); // does not retrieve the value (the request did not complete) 

    if (testedEnt === "some string"){return true;} else {return false;}
    }

它没有用,因为 api 也需要时间来处理。

我还想到了一个额外的选择:

仅将 api 结果用于我的模板单选按钮组。我能够创建一个 observables 数组(所有 api 结果) 但我不知道在我的模板中使用这个数组。

任何建议将被认真考虑!

【问题讨论】:

    标签: javascript angular typescript asynchronous observable


    【解决方案1】:

    如果我理解正确的话,初始情况如下:

    1. 您从 API 中获取了多个项目
    2. 对于每个项目,您必须从 API 中获取一个过滤器对象,它决定是否过滤掉该项目

      您的代码的主要问题是:为了链接可观察对象,您不能使用 map 运算符。相反,我建议将 switchMapforkJoin 结合使用。

      我建议按如下方式修改您的异步管道(为了更好地理解,请在代码中查看我的 cmets):

      entities$: Observable<Entity[]> = this.eventsService.entities$
          .pipe(
              tap((items) => console.log("started pipe", items)),
              map(items => items.filter(item => item.type.toLowerCase().indexOf("set") > -1)),
              tap((items) => console.log("after set filtered pipe", items)),
              switchMap(items => {
      
                  // Create an array of filter observables that include an api-call
                  // and return the item itself if the condition is met, or NULL otherwise
                  const filterObs = items.map(item => this.descFilter(item));
                  
                  // Use forkJoin to execute the array with the http-requests
                  return filterObs.length ? forkJoin(filterObs) : of([]);
              }),
              // Filter out the NULL values:
              map(items => items.filter(i => i !== null)),
              tap((items) => console.log("after descFilter: ", items)),
              tap(() => this.clear())
          );
      

      descFilter() 方法也需要更改,否则无法将其集成到异步管道中:

      /* Based on whether the condition is met, the item itself or NULL will be returned */
      descFilter(item: Entity): Observable<Entity | null> {
          return this.restService.call<any>(item.link).pipe(
              map(res => res.content.desc === "some string" ? item : null)
          );
      }
      

      重要的:最终您可能必须订阅entities$才能运行代码。

    【讨论】:

      猜你喜欢
      • 2023-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多