angularrxjsrxjs-observablesasync-pipe

rxjs observable doesn't subscribe with async pipe


I have the following pipeline and html template, but the app always displays #noMatch and doesn't logs "in the pipe" in the reduce function. I feel it's a matter of timing but I'm not able to fix it.

ts

user$: Observable<User> = new Observable<User>();
matches$: Observable<User[]> = this.user$.pipe(
  switchMap((user: User) => {
    this.type= user.type;
    return of([user, user, user]);
  }),
  distinctUntilChanged(),
);
filteredMatches$: Observable<{ type1: User[]; type2: User[] }>;

ngOnInit(): void {
  this.user$ = this.authService.user$;

  this.filteredMatches$ = this.matches$.pipe(
    reduce((acc: any, curr: User[]) => {
      if (!acc.type1) acc.type1= [];
      if (!acc.type2) acc.type2= [];
      console.log('in the pipe');

      this.networkingService.decideType(this.type, curr.type)
        ? acc.type1.push(curr)
        : acc.type2.push(curr);
      return acc;
    }),
  );
}

html

<ng-template *ngIf="(filteredMatches$ | async) as matches; else noMatch">
  <app-child *ngFor="let user of matches.type1" [user]="user">
  <app-child *ngFor="let user of matches.type2" [user]="user">
</ng-template>

<ng-template #noMatch>
    <app-no-match></app-no-match>
</ng-template>

Solution

  • Ah found it. You do this:

    user$: Observable<User> = new Observable<User>();
    matches$: Observable<User[]> = this.user$.pipe(
      switchMap((user: User) => {
        this.type= user.type;
        return of([user, user, user]);
      }),
      distinctUntilChanged(),
    );
    

    mathes$ listens to user$ which is a new Observable.

    Then, on ngInit you do this:

    this.user$ = this.authService.user$;
    

    You replace the value of user$ with the authService's user$. But mathes$ is still subscribed to the first Observable, which is not doing anything.

    I suggest you replace

     user$: Observable<User> = new Observable<User>();
     //by
     user$: Observable<User> = this.authService.user$;
    

    Or better yet, directly use the authService's user$ instead of creating a property for it.

    EDIT: In my opinion, there is no reason to use the ngOnInit here, you could simply have the observable set at the beggining...

    matches$: Observable<User[]> = this.authService.user$.pipe(
      switchMap((user: User) => {
        this.type= user.type;
        return of([user, user, user]);
      }),
      distinctUntilChanged(),
    );
    filteredMatches$: Observable<{ type1: User[]; type2: User[] }> = this.matches$.pipe(
        reduce((acc: any, curr: User[]) => {
          if (!acc.type1) acc.type1= [];
          if (!acc.type2) acc.type2= [];
          console.log('in the pipe');
    
          this.networkingService.decideType(this.type, curr.type)
            ? acc.type1.push(curr)
            : acc.type2.push(curr);
          return acc;
        }),
      );