0

I am using a route guard (or resolver, I have tried to use either but got the same error) where I want to get Observable as a return value:

canActivate(): Observable<boolean> {
    return this.store.pipe(
      select(fromUserProfileState.getUserProfiles),
      tap((loaded: UserProfile[]) => {
        if (!loaded || loaded.length == 0) {
          this.store.dispatch(new fromUserProfileActions.LoadUPs());
        } else {
          return of(true);
        }
      }),
      filter((loaded: UserProfile[]) => loaded.length > 0),
      first()
    );
  }

However, this doesn't return Observable, it returns Observable which is not acceptable. How can I tweak the rxjs (v 6.5.5) operators to return Observable only?

enter image description here

Efron A.
  • 353
  • 3
  • 10
  • 19

3 Answers3

1

Try this:

canActivate(): Observable<boolean> {
  return this.store.pipe(
    select(fromUserProfileState.getUserProfiles),
    // Convert the information from UserProfiles to a boolean
    // Thils will also be used to authorize the navigation
    map((loaded: UserProfile[]) => !!(loaded && loaded.length)),
    // dispatch an action is no user profile has been loaded
    tap((isLoaded: Boolean) => {
      if (!isLoaded) {
        this.store.dispatch(new fromUserProfileActions.LoadUPs());
      }
    }),
    // complete the observable
    take(1)
  );
}
julianobrasil
  • 8,954
  • 2
  • 33
  • 55
  • Thanks for your response. new fromUserProfileActions.LoadUPs() will trigger an effect to fetch UserProfiles[] from a backend API. Will it wait for that to happen? The effect fires off another action (via mergeMap) : new userProfileActions.LoadSuccessUPs(userProfiles). I mean will take(1) checks whether LoadSuccessUPs has updated the store? I think we need some more bits to add.. – Efron A. Apr 29 '20 at 01:24
  • I just added filter((loaded)=>loaded) before take(1) so it waits for that LoadSuccessUPs action to take place and then takes one and completes. It worked for me this way. – Efron A. Apr 29 '20 at 01:34
1

Thanks to @julianobrasil this has worked for my case:

canActivate(): Observable<boolean> {
    return this.store.pipe(
      select(fromUserProfileState.getUserProfiles),
      // Convert the information from UserProfiles to a boolean
      // Thils will also be used to authorize the navigation
      map((loaded: UserProfile[]) => !!(loaded && loaded.length)),
      // dispatch an action is no user profile has been loaded
      tap((isLoaded: boolean) => {
        if (!isLoaded) {
          this.store.dispatch(new fromUserProfileActions.LoadUPs());
        }
      }),
      // wait till the array is populated
      filter((loaded) => loaded),
      // complete the observable
      take(1)
    );
  }

Basically, it waits for UserProfile[] array to be populated.

Efron A.
  • 353
  • 3
  • 10
  • 19
0

tap is used for side effects but returns an observable identical to the one from the source.

first() is no need because canActivate() will unsubscribe

Maybe you can break navigation unless LoadUPs() prepare all data and retry navigation as a new side effect

canActivate(): Observable<boolean> {
    return this.store.pipe(
      select(fromUserProfileState.getUserProfiles),
      tap((loaded: UserProfile[]) => {
        if (!loaded || loaded.length == 0) {
          this.store.dispatch(new fromUserProfileActions.LoadUPs());
        }
      }),
      map((loaded: UserProfile[]) => {
        if (!loaded || loaded.length == 0) {
          return false;
        } else {
          return true;
        }
      })
    );
  }
Radik
  • 2,715
  • 1
  • 19
  • 24
  • Thanks for your response. I need that filter operator to wait until LoadSuccessUPs take place successfully and the array is populated. So, as I commented above, I just added filter((loaded)=>loaded) before take(1) for that matter. Refer to @julianobrasil answer. – Efron A. Apr 29 '20 at 01:37