0
votes

Angular / Rxjs comment s'abonner à une fonction dans une boucle avec différents paramètres

Comment appeler une fonction qui retourne un observable (sujet) dans une boucle (pipe -> map)? Les paramètres de la fonction sont différents à chaque fois.

J'essaye de l'expliquer avec un exemple:

Client 1; Keyfigure A; 23
Client 1; Keyfigure B, 22

A la fin, je veux une liste comme:

export class MyClass implements OnInit {

  private keyFigureSubject = new Subject();
  keyFigures$ = this.keyFigureSubject.asObservable();

  load() {
    const clients$ = of([{ id: 1, name: 'Mustermann' }, { id: 2, name: 'Hans Dampf' }, { id: 3, name: 'Pleite AG' }]);
      const result = clients$.pipe(
      flatMap((clients)=> clients),
      map(client => this.loadKeyfigures(client.id)) // this isnt working 
    );

    result.subscribe(x=>console.log(x));
  }

  loadKeyfigures(clientId: number) {

    // EDIT: This is a http call in "real life"

    const keyFigures = [];
    keyFigures.push({clientId: 1, name: 'A', value: 23});
    keyFigures.push({clientId: 1, name: 'B', value: 22});
    keyFigures.push({clientId: 2, name: 'A', value: 12});
    keyFigures.push({clientId: 3, name: 'A', value: 312});
    keyFigures.push({clientId: 3, name: 'B', value: 12333});
    keyFigures.push({clientId: 3, name: 'C', value: 4567});
    keyFigures.push({clientId: 3, name: 'D', value: 54});
    keyFigures.push({clientId: 4, name: 'A', value: 5672});
    keyFigures.push({clientId: 5, name: 'A', value: 243});
    keyFigures.push({clientId: 6, name: 'A', value: 334});

    keyFigures.forEach(keyFigure =>{
      if(keyFigure.clientId === clientId){
        this.keyFigureSubject.next(keyFigure);
      }
    });
    return this.keyFigureSubject.asObservable();

  }

}

Le problème semble que subject.next() est appelé avant que le retour subject.asObservable() soit appelé. Et si je souscris à la propriété, je ne peux pas passer de paramètres.


0 commentaires

3 Réponses :


0
votes

Vous pouvez utiliser la combinaison de forkJoin et switchMap :

loadKeyfigures(clientId: number) {
    const keyFigures = [];
    keyFigures.push({clientId: 1, name: 'A', value: 23});
    keyFigures.push({clientId: 1, name: 'B', value: 22});
    keyFigures.push({clientId: 2, name: 'A', value: 12});
    keyFigures.push({clientId: 3, name: 'A', value: 312});
    keyFigures.push({clientId: 3, name: 'B', value: 12333});
    keyFigures.push({clientId: 3, name: 'C', value: 4567});
    keyFigures.push({clientId: 3, name: 'D', value: 54});
    keyFigures.push({clientId: 4, name: 'A', value: 5672});
    keyFigures.push({clientId: 5, name: 'A', value: 243});
    keyFigures.push({clientId: 6, name: 'A', value: 334});

    // Just filter out the invalid keyFigures by comparing the ids.
    return this.keyFigures.filter(({clientId: id}) => id === clientId);
  }

Dans la méthode d'abonnement, vous recevrez alors la liste des valeurs émises de chaque observable.

Pour votre cas spécifique, cependant, je ne vois pas pourquoi vous devez renvoyer des Observables à partir de votre méthode loadKeyfigures . Vous pouvez également simplement renvoyer la valeur de manière synchrone.

import { forkJoin } from "rxjs";
import { switchMap } from "rxjs/operators";

...
switchMap(client => forkJoin(this.loadKeyfigures(client.id)))

Ensuite, vous pouvez simplement l'opérateur de la carte comme dans votre exemple.


1 commentaires

Merci pour votre réponse. Désolé je l'ai mal décrit. La fonction effectue un appel http get. Je voulais simuler l'appel http avec le sujet.



0
votes

Vous n'avez pas besoin BehaviorSubject à des fins de simulation, il suffit d' utiliser of

switchMap(client => this.loadKeyfigures(client.id))

Veuillez également noter que vous aurez besoin d'un opérateur d'ordre supérieur tel que switchMap pour passer à une ou plusieurs autres valeurs observables.

keyFigures.forEach(keyFigure =>{
      if(keyFigure.clientId === clientId){
        return of(keyFigure);
      }
    });


0 commentaires

0
votes

Il semble que tout ce dont vous avez besoin est de vous abonner à une observable interne pour chaque nouvel appel à loadKeyfigures . Pour ce faire, tout ce que vous avez à faire est de changer la map en mergeMap . Il n'y a pas vraiment de boucle ici. Vous transformez simplement chaque valeur de votre flux en un appel http.

loadKeyfigures(clientId: number) {

  // EDIT: This is a http call in "real life"

  const keyFigures = [];
  keyFigures.push({clientId: 1, name: 'A', value: 23});
  keyFigures.push({clientId: 1, name: 'B', value: 22});
  keyFigures.push({clientId: 2, name: 'A', value: 12});
  keyFigures.push({clientId: 3, name: 'A', value: 312});
  keyFigures.push({clientId: 3, name: 'B', value: 12333});
  keyFigures.push({clientId: 3, name: 'C', value: 4567});
  keyFigures.push({clientId: 3, name: 'D', value: 54});
  keyFigures.push({clientId: 4, name: 'A', value: 5672});
  keyFigures.push({clientId: 5, name: 'A', value: 243});
  keyFigures.push({clientId: 6, name: 'A', value: 334});

  // Use from to convert array to a stream
  return from(keyFigures.filter(fig => 
    fig.clientId === clientId
  ));
}

En outre, il convient de noter que votre appel http virtuel ne fonctionnera pas. Les sujets sont des observables "chauds", donc au moment où vous le renvoyez comme observable, il n'émettra plus de valeurs.

Essayez plutôt ceci:

load() {
  from([
    { id: 1, name: 'Mustermann' }, 
    { id: 2, name: 'Hans Dampf' }, 
    { id: 3, name: 'Pleite AG' }
  ]).pipe(
    mergeMap(client => this.loadKeyfigures(client.id))
  ).subscribe(console.log);
}


0 commentaires