Search code examples
angulartypescriptrxjsngrx

Re-using an RxJS Observable to repeat an asynchronous call?


How can an RxJS Observable emit a new value, if the underlying event stream doesn't emit a new value?

Given an Angular component which uses an async pipe to display a user's name;

export class MyComponent implements OnInit {

  public user$!: Observable<User>;

  constructor(private readonly ms: MyService)

  ngOnInit() {
    this.user$ = this.ms.getUser();
  }
}
<ng-container *ngIf="user$ | async as user; else empty">
  {{ user.name }}
</ng-container>

<ng-template #empty>
  No user here.
</ng-template>

...imagine some time passes and I want to try getting the user again by calling the asynchronous function this.ms.getUser(), how can I do this?

AFAIK, I cannot simply overwrite the Observable $captcha;

export class MyComponent implements OnInit {

  public user$!: Observable<User>;

  constructor(private readonly ms: MyService)

  ngOnInit() {
    this.user$ = this.ms.getUser();
  }

  getUserAgain() {
    // Wrong. This would be a new/different Observable.
    // The UI would not update.
    this.user$ = this.ms.getUser();
  }
}

The service MyService returns an Observable because it's just a thin wrapper around Angular's HttpClient.


Solution

  • I'd recommend to use observables as much as possible and stay in a "reactive mind":

    export class MyComponent {
      public refreshUser$: Subject<void> = new Subject();
    
      public user$!: Observable<User> = this.refreshUser$.pipe(
        startWith(undefined),
        switchMap(() => this.ms.getUser())
      );
    
      constructor(private readonly ms: MyService) {}
    
      public getUserAgain() {
        this.refreshUser$.next();
      }
    }