Home > Enterprise >  rxjs observable doesn't subscribe with async pipe
rxjs observable doesn't subscribe with async pipe

Time:12-03

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>

CodePudding user response:

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;
    }),
  );

CodePudding user response:

@Salketer's answer is correct - the reason and the solution. Further: if, for some reason or another, the source observable isn't available yet in the constructor (it depends on some inputs, for example), you could do something like this

user$: Observable<User> = new Subject<User>(); 
matches$: Observable<User[]> // no changes here

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

(A Subject is both an observable and an observer).

  • Related