web-dev-qa-db-fra.com

Filtre simple sur le tableau de RXJS Observable

Je commence mon projet avec Angular2 et les développeurs semblent recommander RXJS Observable au lieu de Promises.

J'ai réussi à récupérer une liste d'éléments (epics) du serveur. Mais comment puis-je filtrer les éléments en utilisant par exemple un identifiant?

Le code suivant est une extraction de mon application et montre maintenant la solution de travail finale. Espérons que cela aide quelqu'un.

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

Merci d'avance pour votre aide.

34
Johannes

Vous voudrez filtrer le tableau réel et non l'observable qui l'entoure. Vous allez donc cartographier le contenu de l’Observable (qui est un Epic[]) sur un Epic filtré.

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

Ensuite, vous pouvez subscribe à getEpic et en faire ce que vous voulez.

61
Luka Jacobowitz

Vous pouvez le faire en utilisant les méthodes flatMap et filter de Observable à la place de la méthode de filtrage du tableau JS dans map. Quelque chose comme:

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 fournira des index singuliers pour le filtrage et vous pourrez continuer avec ce qui se passera ensuite avec les résultats.

Si TypeScript génère une erreur indiquant que vous ne pouvez pas comparer une chaîne et un nombre indépendamment de votre utilisation de == dans le filtre, ajoutez simplement un + avant epic.id dans le filtre, selon le Angular docs:

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

Exemple:

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

27
mtpultz

réponse originale avec un correctif: Observables sont paresseux. Vous devez appeler subscribe pour demander à un observable d'envoyer sa demande.

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

Mise à jour de Rxjs 6:

import {filter} from 'rxjs/operators';

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

Vous devez vous abonner sur Observables pour obtenir les données, car les appels http sont asynchrones en JavaScript.

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

Vous pouvez appeler cette méthode alors comme ceci:

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