web-dev-qa-db-ja.com

RXJS Observableの配列の単純なフィルター

私はAngular2でプロジェクトを開始していますが、開発者はPromiseではなくRXJS Observableを推奨しているようです。

サーバーから要素のリスト(エピックス)を取得することに成功しました。しかし、たとえばidを使用して要素をフィルタリングするにはどうすればよいですか?

次のコードは私のアプリからの抜粋であり、最終的な作業ソリューションを示しています。それが誰かを助けることを望みましょう。

@Injectable()
export class EpicService {

  private url = CONFIG.SERVER + '/app/';  // URL to web API

  constructor(private http:Http) {}

  private extractData(res:Response) {
    let body = res.json();
    return body;
  }

  getEpics():Observable<Epic[]> {
    return this.http.get(this.url + "getEpics")
      .map(this.extractData)
      .catch(this.handleError);
  }

  getEpic(id:string): Observable<Epic> {
    return this.getEpics()
      .map(epics => epics.filter(epic => epic.id === id)[0]);
  }
}

export class EpicComponent {

  errorMessage:string;
  epics:Epic[];
  epic:Epic;

  constructor(
    private requirementService:EpicService) {
  }

  getEpics() {
    this.requirementService.getEpics()
      .subscribe(
        epics => this.epics = epics,
        error => this.errorMessage = <any>error);
  }

  // actually this should be eventually in another component
  getEpic(id:string) {
    this.requirementService.getEpic(id)
        .subscribe(
        epic => this.epic = epic,
        error => this.errorMessage = <any>error);
  }
}

export class Epic {
  id: string;
  name: string;
}

よろしくお願いします。

34
Johannes

実際の配列をフィルタリングし、その周囲にあるオブザーバブルをフィルタリングする必要があります。したがって、Observableのコンテンツ(Epic[])をフィルター処理されたEpicにマップします。

getEpic(id: string): Observable<Epic> {
  return this.getEpics()
     .map(epics => epics.filter(epic => epic.id === id)[0]);
}

その後、subscribeからgetEpicに変更し、必要な操作を実行できます。

61
Luka Jacobowitz

これを行うには、flatMapのJS配列フィルターメソッドの代わりに、filterObservableおよびmapメソッドを使用します。何かのようなもの:

this.getEpics() 
    .flatMap((data) => data.epics) // [{id: 1}, {id: 4}, {id: 3}, ..., {id: N}]
    .filter((epic) => epic.id === id) // checks {id: 1}, then {id: 2}, etc
    .subscribe((result) => ...); // do something epic!!!

flatMapは、フィルタリングのための特異なインデックスを提供します。次に、結果で次に何が起こるかを把握できます。

Angularのドキュメントに従って、フィルター内の==の使用に関係なく文字列と数値を比較できないことを示すTypeScriptがエラーをスローする場合、フィルター内の+の前にepic.idを追加します。

    .flatMap(...)
    .filter((epic) => +epic.id === id) // checks {id: 1}, then {id: 2}, etc
    .subscribe(...)

例:

https://stackblitz.com/edit/angular-9ehje5?file=src%2Fapp%2Fapp.component.ts

27
mtpultz

修正を含む元の回答:Observablesは遅延しています。 subscribeを呼び出して、observableにリクエストを送信するように指示する必要があります。

  getEpic(id:number) {
    return this.getEpics()
           .filter(epic => epic.id === id)
           .subscribe(x=>...);
  }

Rxjs 6への更新:

import {filter} from 'rxjs/operators';

getEpic(id:number) {
        return this.getEpics()
               .pipe(filter(epic => epic.id === id))
               .subscribe(x=>...);
      }
1

JavaScriptではhttp呼び出しは非同期であるため、データを取得するにはObservablesをサブスクライブする必要があります。

getEpic(id: number, callback: (epic: Epic) => void) {
    this.getEpics().subscribe(
        epics: Array<Epic> => {
            let epic: Epic = epics.filter(epic => epic.id === id)[0];
            callback(epic);
        }
    );
}

このメソッドを次のように呼び出すことができます:

this.someService.getEpic(epicId, (epic: Epic) => {
    // do something with it
});
0
rinukkusu