-2

I am making a couple of similar http calls in my angular 5 application. I also need to apply similar data transformation logic and add the results to an array to display it on the page.

The 2nd call is not mandatory and is conditional.

On http subscribe, am doing the data transformation and pushing it to an array. I tried doing array concat to add the results from the 2nd call, but am not getting any results on the page.

What's the best way to approach this?

if(oneReq) {
     this.http.get(url).subscribe ((message:       any[]) => {
     results.push({});
     }
   return results;

} else {
    forkJoin([this.http.get(url),        this.http.get(url)]) . subscribe ((message:    any[]) => {
     results.push({});
 }
  return results;
  }
l a s
  • 3,836
  • 10
  • 42
  • 61

2 Answers2

0

Use

  • forkJoin to make all calls and wait for completion (equivalent to Promise.all)
  • defaultIfEmpty for the conditionnal second call
  • map and the spread operator to concatenate these values
  • map again to transform these iteams

    Observable
        .forkJoin(this.first$, this.second$.defaultIfEmpty([]))
        .map(([current, added]) => [...current, ...added])
        .map(item => transform(item))
        .subscribe(array=>{
           //do something with array
        });
    
Safiyya
  • 1,383
  • 1
  • 10
  • 16
  • Code-only posts tend to get flagged as low quality and deleted. Could you add some commentary to this so that others can understand how this fixes the problem? – Graham Feb 27 '18 at 02:14
0

A couple of points I can see base on the code provided:

  1. Observables are asynchronous
  2. You can subscribe after the conditional block

Asynchronous

The observable you get back from http.get does not emit immediately. It waits for the http response first, then executes the code inside subscribe. It also does not block execution. So while it's waiting for the response, it carries on and runs return results. When the response finally comes, then the subscribe block runs.

Here's an example. I've simulated Angular's http.get here since it's not so simple to include Angular into the snippet:

// Angular's `http.get` returns an RxJS Observable
// which `emits` and `completes` when the http response comes through
// So this simulates the `get` 
// using an Observable which emits and completes after 500 milliseconds
const http = { get: () =>  Rx.Observable.of('messages').delay(500) }

const results = []
const url = ''

function getMessages() {
  http.get(url).subscribe(messages => {
    // this block is run after 500ms
    results.push({});
    console.log('C: ', results) // [{}]
  })
  return results; // this code does not wait 500ms, it runs immediately
}

const returned = getMessages()
console.log('A: ', returned) // []
console.log('B: ', results)  // []

// logs will show:
// A: []
// B: []
// C: [{}]
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>

Subscribing after the conditional block

const forkJoin = Rx.Observable.forkJoin

// Let's pretend our endpoint just response to `get` with the url as its "messages"
const http = {
  get(url) {
    return Rx.Observable.of([url]).delay(500)
  }
}

const url1 = 'one.com', url2 = 'two.com', url3 = 'three.com'

let results = []
let oneReq

function getMessages() {
  let response;
  if (oneReq) {
    response =  http.get(url1) // emits [one.com]
  } else {
    response = forkJoin([
      http.get(url2), http.get(url3)
    ]) //emits [[two.com], [three.com]]
  }
  
  // we return the Observable
  return response.do(handleMessages)
}

function handleMessages(messages) {
  // because of the forkJoin,
  // you need to check for the nested array
  // [[two.com], [one.com]]
  // that forkJoin makes before you concat
  results = results.concat(messages)
}

oneReq = true
getMessages().subscribe(messages => {
  console.log(results)
})

oneReq = false
getMessages().subscribe(messages => {
  console.log(results)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>

Note that the Observable is returned from getMessages().

To re-iterate on the asynchronous aspect, returning results from getMessages() will not produce what you might expect because code inside subscribe() executes later.

Angular components can detect the changes for you though, so you have two choices inside a component if you don't like having to subscribe to getMessages():

Option 1:

// change the `do` to subscribe
function getMessages(): void {
  // if/else...
  response.subscribe(handleMessage)
}

Note that you cannot return the messages now. Returning response.subscribe(handleMessage) will return the Subscription, not the Observable. Take a look at this about subscriptions

Option 2:

Use the async pipe in your template:

<ul>
  <li *ngFor="let result of results | async">{{result}}</li>
<ul>

Here's the documentation and an article about the async pipe

Clarence Lee
  • 141
  • 6
  • So the second request depends on the response of the first response? – Clarence Lee Feb 27 '18 at 08:23
  • No they are independent – l a s Feb 27 '18 at 11:17
  • It would really help to see some of the code you've written that isn't working. – Clarence Lee Feb 27 '18 at 14:05
  • But I couldn't help having another stab at an answer before I left work :P Updated answer to show a condition outside of the two streams – Clarence Lee Feb 27 '18 at 18:11
  • For the time being, I am using an if condition - forkjoin of 2 calls / subscribe and in else with just http/subscribe. For some reason am not able to use a function to format the responses - I had to duplicate the logic. – l a s Feb 28 '18 at 04:21
  • I really can't visualise what you're describing. Add snippets of the if and forkjoin structure you're trying out onto your question and we can take it from there. – Clarence Lee Feb 28 '18 at 09:08
  • I have updated what I have currently which is not very clean but works. I am trying to use a function to reduce duplicate code and also want to avoid if else as well. – l a s Feb 28 '18 at 18:28
  • Aha, thanks for the code example, answer updated. Let's see if this better answers you questions – Clarence Lee Mar 01 '18 at 08:29