7
votes

Transmettre les valeurs de plusieurs observables

Dans mon service Angular, j'ai les méthodes suivantes:

this.itemService.createItem(item)
    .pipe(
      switchMap(createdId =>
         combineLatest(this.itemService.getAllItems(), of(createdId)))
    ).subscribe(result => {
      this.items = result[0];

      // doing some stuff with result[1], which is the value of createdId
    });

Dans mon modèle, j'affiche les éléments dans une liste.

Je voudrais pouvoir pour créer un nouvel élément, puis recharger la liste (pour inclure l'élément nouvellement créé), j'ai donc implémenté ce qui suit:

this.itemService.createItem(item)
    .pipe(
      switchMap(createdId => this.itemService.getAllItems())
    ).subscribe(result => {
      this.items = result;

      // i would like to use createdId here as well
    });

Cela fonctionne apparemment bien, mais à la fin, je voudrais également effectuer un traitement avec createdId :

this.itemService.createItem(item)
    .pipe(
      switchMap(createdId => this.itemService.getAllItems())
    ).subscribe(result => {
      this.items = result;
    });

Donc, j'ai trouvé ce qui suit: p>

// creates an Item and returns the ID of the created Item
createItem(item: Item): Observable<ItemId> {
    return this.http.post<ItemId>('some_api_url', item);
}

// returns all Items
getAllItems(): Observable<Item[]> {
    return this.http.get<Item[]>('some_api_url');
}

Mais avoir à utiliser combineLatest dans le switchMap et à faire explicitement de createdId un Observable me fait me demander si c'est une bonne solution.

Donc, fondamentalement, je voudrais créer un élément, mettre à jour la liste (lorsque la création de l'élément est terminée) et utiliser l'ID de l'élément créé élément lorsque la mise à jour est terminée.

Y a-t-il une meilleure façon de faire cela?

J'apprécierais vraiment tout conseil.


1 commentaires

Je recommande de changer un peu cela et de retourner un sujet, ou mieux encore un sujet de comportement. De cette façon, vous appelez la valeur suivante du service observable et renvoyez le sujet comme observable. Tous les abonnés recevront la nouvelle variable.


5 Réponses :


1
votes

Vous pouvez utiliser l'opérateur concatMap . Essayez d'abord de créer l'élément, puis attendez l'exécution à l'aide de l'opérateur concatMap . Prenez la sortie de l'exécution dans l'observable allItems puis fusionnez les résultats à l'aide d'un opérateur map . Dans l'abonnement, vous aurez un objet de createdItem et allItems

const createdItem = this.itemService.createItem(item);
const allItems = this.itemService.getAllItems();

createdItem
  .pipe(
    concatMap(item =>
              allItems.pipe(
                map(items => ({
                  createdItem: item,
                  items
                })
              )
    )
  )


0 commentaires

1
votes

Vous devez canaliser le résultat de l'observable interne afin de transmettre davantage la valeur émise par la source. Vous pouvez le faire comme suit:

// I use a function here for the purpose of making the code more readable
const itemsWithNew = (newItemId) => this.itemService.getAllItems()
                                        .pipe(map(items => ({items,newItemId})));
this.itemService.createItem(item)
    .pipe(
      switchMap(itemsWithNew)
    ).subscribe(({items, newItemId})=> {
      this.items = items;

      // newItemId is the new value
    });


0 commentaires

-1
votes

Alternativement, switchmap accepte également un deuxième rappel de résultat pour traiter à la fois la valeur externe et la valeur interne ensemble.

this.itemService.createItem(item).pipe(
      switchMap(
        () => this.itemService.getAllItems(),
        (createdId, items) => {
           // can do some stuff here with both outer and inner values
           return { createdId, items };
        },
      ),
    ).subscribe({ createdId, allItems }) => {
      this.items = allItems;
    });


1 commentaires

Le paramètre de fonction de sélection est obsolète



2
votes

Après quelques recherches supplémentaires sur les opérateurs RxJS, j'ai pensé que la solution la plus propre pourrait être de simplement combiner concat avec toArray:

// import { concat } from 'rxjs';
// import { toArray } from 'rxjs/operators';

concat(
  this.itemService.createItem(item),
  this.itemService.getAllItems())
    .pipe(toArray())
    .subscribe((result: [ItemId, Item[]]) => {
      // result[0] is the ItemId
      // result[1] is the Item[]
    });


3 commentaires

Je suis presque sûr que toArray n'est pas obligatoire


@ Jota.Toledo Si je comprends bien, sans le toArray , cela entraînerait l'émission de deux valeurs distinctes, d'abord le ItemId , puis le Item [] . J'ai besoin des deux et seulement quand les deux sont terminés, donc c'est nécessaire je pense.


En effet, a supervisé que



-1
votes
      // in your service 

        items: Item[]=[];
        $$items=new BehaviorSubject(this.items);


           // creates an Item and returns the ID of the created Item
            createItem(item: Item): Observable<ItemId> {
             return this.http.post<ItemId>('some_api_url', item)
           }

            // returns all Items
      getAllItems(): Observable<Item[]> {
     return this.http.get<Item[]>('some_api_url').pipe(map(response=>{
      return {//format response here//}

}).subscribe(result=>{
this.items=result;
     this.$$items.next(this.items);
        })
       }

       returnItemsAsObs(){
        return this.$$items.asObservable();

        }

         //in your component
          items:Item[]
            $$items: Subscription

         ngOninit(){
         this.service.getAllItems()
         this.$items=this.service.returnItemsAsObs().subscribe(items=>{
         this.items=items;
         return this.items;

      })


    onCreateItem(item){
    return this.service.createItem(item).subscribe(item=>{
    if(item.length<0){
    //call other service for itemid manipulation
    this.service.getAllItems();


     }

    })


    }


    }

0 commentaires