2
getBudgetMap(budgetMonthID){
        this.rows = [];
        this.categoryService.getCategory(budgetMonthID).subscribe(category =>{
            this.categoryList = category;
            this.categoryList.forEach(category => {

                this.transactionService.getTransaction(budgetMonthID, category.id).subscribe(transaction => {
                    this.rows = this.rows.concat(transaction);
                })

            })
        )
    }

To make it easy, I have a nested ajax service (http.get) call which the first one (getCategory) returning a list of category base on budgetMonthID. The number of categories varies. Then each category will make a second call getTransaction to retrieve all transactions belong to each category. The code above works fine, but I been reading about map, flapMap, pipe for Angular, I just can't figure it out how to change that nasty code. Thanks

Yashwardhan Pauranik
  • 5,370
  • 5
  • 42
  • 65
Raymond Tey
  • 464
  • 3
  • 11
  • Which DBMS are you using? – Exterminator Oct 22 '18 at 06:36
  • I'm sure you'll get good answers, but I'd argue that this code you have now is far from "nasty" (other than the huge indents :)), and is probably easier to read/maintain than using RxJS operators at all costs. Just my 2 cents tho. – Jeto Oct 22 '18 at 06:46

2 Answers2

1

You can use rxjs operators which deals with Asynchronous call. To be preciouse you should use switchmap or flatmap or mergemap. Code can be given to however you will encounter with same problem in future again. So I would encourage you to spare sometimes to understand how rxjs operator works.

Please walk through this - https://www.learnrxjs.io/operators/transformation/switchmap.html

Sunil Singh
  • 11,001
  • 2
  • 27
  • 48
  • better to learn to use mergeMap as default, instead of switchMap, because the latter can cause ['dataloss'](https://blog.angularindepth.com/switchmap-bugs-b6de69155524) if improperly used – Poul Kruijt Oct 22 '18 at 06:44
1

This requires some rxjs knowledge indeed.

this.categoryService.getCategory(budgetMonthID).pipe(
  tap((categories) => this.categoryList = categories),
  mergeMap((categories) => zip(
    ...categories.map(
      (category) => this.transactionService.getTransaction(budgetMonthID, category.id)
    )
  )
).subscribe((rows) => {
  this.rows = rows;
});

To start we are using tap to save the categories into the categoryList. This is an operator which just executes the function but has no effect on the pipe flow.

After that, we use mergeMap which picks up the categories, and returns another Observable. In this case the zip, which waits for all the parameters to complete before emitting as an array, which will be picked up by the subscribe.

You can also try to have a look at the built-in async pipe from angular. This removes the need of unsubscribing inside your component, and keeps it clean as well. This will change your code to this:

this.categoryList$ = this.categoryService.getCategory(budgetMonthID).pipe(
  shareReplay(1)
);

this.rows$ = this.categoryList$.pipe(
  mergeMap((categories) => zip(
    ...categories.map(
      (category) => this.transactionService.getTransaction(budgetMonthID, category.id)
    )
  )
);

In your template you can then access this like so:

<div *ngFor="let category of categoryList$ | async"></div>
<div *ngFor="let row of rows$ | async"></div>
Poul Kruijt
  • 69,713
  • 12
  • 145
  • 149
  • Should be `this.categoryList = categories`. Also wouldn't `switchMap` be more fitting here? Still not entirely familiar with RxJS but it seems like you no longer need the values from the initial observable after tapping into it. – Jeto Oct 22 '18 at 06:44
  • @Jeto untested code indeed :) changed the categories thing. And no, you should try to only use switchMap in cases that it makes sense. See my comment on SunilSingh's answer – Poul Kruijt Oct 22 '18 at 06:45
  • Use switchMap, only use mergeMap if you have a good reason for it! – Bo Vandersteene Oct 22 '18 at 06:51
  • @Jeto you're welcome. You are right nevertheless though, I can only assume these are ajax requests which complete immediately, which makes concatMap/mergeMap/switchMap all have the same outcome, but personally I even prefer concatMap here, because you really wait for it to complete and describes the code more thoroughly, but opinions differ – Poul Kruijt Oct 22 '18 at 06:52