1
votes

I am writing an Angular 6 app. I expect it to show a loading animation when route changes as well as there is also any pending http requests. So I have 2 Observables look like this. For httpPendingRequests, I am implementing with a counter written in Angular's HttpInterceptor; and for locationChanged, I have subscribed to Router's NavigationStart/NavigationEnd events.

httpPendingRequests: BehaviorSubject<number>;
locationChanged: BehaviorSubject<boolean>;

And I am using concatMap to subscribe to these 2 Observables, code looks like this:

this.locationChanged.pipe(
  concatMap(changed => {
    console.log('location change:' + changed);
    if (changed) {
      return this.httpPendingRequests;
    } else {
      return of(0);
    }
  }),      
  map(count => count > 0)
).subscribe(val => { 
  console.log('isloading: ' + val);
});

So I expect this to log 'isloading' to console only when location has been changed, plus if there is any pending requests. It does work in this scenario. However I found it also logs 'isloading' message when there is only pending http requests but location does not change. It made me confused, I thought the operator is making sure the Observables are subscribed in order? If the first one(location change) does not emit, then the second one(pending request) should not be triggered? Am I understanding this concept wrong?

Besides.. I have also tried other methods to combine Observables, zip, forkJoin, combineLatest - they all only triggered the subscription once in my case so I am also not very sure what was wrong.

I am glad to provide more informations if needed. Thanks in advance

2
is it that the 'isloading: ' logs for one time every time the component loads, even without locationChange?CozyAzure

2 Answers

0
votes

Can be solve by using combineLatest observable and map operator. Here is the demo: https://stackblitz.com/edit/so-rxjs-concat.

Code is in app.component.ts. Take a look into console.log value to understand more.

0
votes

You may want to take a look at the fact that you are using BehaviorSubject.

BehaviorSubject requires always a value to emit at creation time. This means that locationChanged always emits at least one notification.

If you rather use Subject you can control when the first notification is emitted.

In the following example, taken directly from you code, you do not see anything logged since locationChanged never emits.

const httpPendingRequests = new BehaviorSubject<number>(0);
const locationChanged = new Subject<boolean>();

locationChanged.pipe(
    concatMap(changed => {
      console.log('location change:' + changed);
      if (changed) {
        return httpPendingRequests;
      } else {
        return of(0);
      }
    }),      
    map(count => count > 0)
  ).subscribe(val => { 
    console.log('isloading: ' + val);
});

setTimeout(() => {
    httpPendingRequests.next(1);
}, 100);
setTimeout(() => {
    httpPendingRequests.next(2);
}, 200);
setTimeout(() => {
    httpPendingRequests.next(3);
}, 300);