web-dev-qa-db-de.com

Einfacher Filter auf Array von RXJS Observable

Ich beginne mein Projekt mit Angular2 und die Entwickler scheinen RXJS Observable anstelle von Promises zu empfehlen.

Ich habe es geschafft, eine Liste von Elementen (Epics) vom Server abzurufen. Aber wie kann ich die Elemente filtern, indem ich zum Beispiel eine ID verwende?

Der folgende Code ist ein Auszug aus meiner App und zeigt nun die endgültige funktionierende Lösung. Hoffen wir, dass es jemandem hilft.

@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;
}

Vielen Dank im Voraus für deine Hilfe.

34
Johannes

Sie möchten das tatsächliche Array filtern und nicht das um es gewickelte Observable. So ordnen Sie den Inhalt des Observable (das ist ein Epic[]) zu einem gefilterten Epic.

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

Danach können Sie subscribe bis getEpic und damit machen, was Sie wollen.

61
Luka Jacobowitz

Sie können dazu die Methoden flatMap und filter von Observable anstelle der JS-Array-Filtermethode in map verwenden. So etwas wie:

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 liefert singuläre Indizes zum Filtern und dann können Sie mit den Ergebnissen weitermachen, was auch immer als nächstes passiert.

Wenn TypeScript einen Fehler ausgibt, der anzeigt, dass Sie eine Zeichenfolge und eine Zahl unabhängig von der Verwendung von == Im Filter nicht vergleichen können, fügen Sie einfach + Vor epic.id Im Filter ein. gemäß den Angular docs:

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

Beispiel:

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

27
mtpultz

ursprüngliche Antwort mit einem Fix: Observables sind faul. Sie müssen subscribe aufrufen, um einem observable mitzuteilen, dass er seine Anfrage senden soll.

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

Update auf Rxjs 6:

import {filter} from 'rxjs/operators';

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

Sie müssen Observables abonnieren, um die Daten abzurufen, da http-Aufrufe in JavaScript asynchron sind.

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);
        }
    );
}

Sie können diese Methode dann folgendermaßen aufrufen:

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