Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing down values from multiple Observables

In my Angular service i have the following methods:

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

In my template i'm showing the items in a list.

I would like to be able to create a new item and then re-load the list (to include the newly created item), so i've implemented the following:

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

This seemingly works fine, but in the end i would like to do some processing with the createdId as well:

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

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

So i've came up with the following:

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

But having to use combineLatest inside the switchMap and explicitly make createdId an Observable makes me wonder if this is a good solution.

So basically i would like to create and item, update the list (when the item creation is finished) and use the ID of the created item when the update is finished.

Is there a better way to do this?

I'd really appreciate any advice.

like image 458
justanoob Avatar asked Feb 23 '19 11:02

justanoob


People also ask

How do I combine multiple Observables?

combine multiple Observables into one by merging their emissions. You can combine the output of multiple Observables so that they act like a single Observable, by using the Merge operator.

Can Observable return multiple values?

In functions, you can't return multiple times. The fundamental difference is that observable is able to return multiple values. When you . call on a function, you are basically saying — 'give me a value immediately and just one value'.


3 Answers

After some more digging into RxJS operators i figured the cleanest solution might be to simply combine concat with 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[]
    });
like image 53
justanoob Avatar answered Oct 24 '22 16:10

justanoob


You can use the concatMap operator. Try to first create the item, then wait for the execution using the concatMap operator. Take the output of the execution in the allItems observable and then merge the results using a map operator. In the subscribe, you will have an object of createdItem and allItems

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

createdItem
  .pipe(
    concatMap(item =>
              allItems.pipe(
                map(items => ({
                  createdItem: item,
                  items
                })
              )
    )
  )
like image 37
profanis Avatar answered Oct 24 '22 17:10

profanis


You need to pipe the result of the inner observable in order to further pass down the value emited by the source. You can do this as follows:

// 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
    });
like image 33
Jota.Toledo Avatar answered Oct 24 '22 16:10

Jota.Toledo