Home > Net >  Observable angular not observed
Observable angular not observed

Time:05-30

This is my first time making my own observable, and i'm not sure if i'm doing it right because the data doesn't show when loaded, i see it in the console but the component doesn't refresh...

I retrieve the ids of my accounts and I make a request to retrieve the accounts then I would like to create an observable of Account [] from the table I just created

account service:

 getListFriendsNoConfirmation(): Observable<Account []> {
    return this.itSMe().pipe(
      switchMap((me: Account) => {
        return this.post('account/friendNoConfirmation', HelperAccount.toUpdatePayload(me))
      }),
        switchMap((resp: ApiResponse)=>{
          if(resp != null){
            let nofriends: Account [] = [];
            let ids= resp.data as number [];
            ids.forEach((id:number)=>{
              this.getOneAccount(id).subscribe((account: Account)=>{
                nofriends.push(account);
              })
            })
            return of(nofriends);
          }else{
            return []
          }


        })
    )
  }

  getOneAccount(id: number): Observable<Account>{
    return this.getWithName('account/one/',id.toString()).pipe(
      map((resp: ApiResponse)=>{
        return HelperAccount.fromDtoToAccount(resp.data as AccountDto);
      })
    )
  }

component.ts:

ngOnInit(): void {
   
    this.accountService.getListFriendsNoConfirmation().subscribe((accounts: Account [])=>{
      this.nofriends$ = accounts;
      console.log(accounts);
    })
  }

CodePudding user response:

there are a few problems, first, you are not returning an observable if resp is null. It should be else{ return of([]); }

second, you should never do a subscribe when chining the observables, this doesn't work. use forkjoin instead:

switchMap((resp: ApiResponse)=>{
  if(resp?.data === null || resp?.data === undefined || resp.data.length === 0){
    return of([]);
  }

  const nofriends$: Observable<Account>[] = (resp.data as number[]).map(
    id => this.getOneAccount(id)
  );
  return forkJoin(nofriends$);
)}

and maybe your last problem is in component.ts when you do this.nofriends$ = accounts;. At this point accounts is an array of account, and not an observable. so it should be:

public nofriends: Account[] = null;

ngOnInit(): void {
  this.accountService.getListFriendsNoConfirmation().subscribe((accounts: Account [])=>{
    this.nofriends = accounts;
    console.log(accounts);
  })
}

or if you want

public nofriends$: Observable<Account[]> = this.accountService.getListFriendsNoConfirmation().pipe(
  tap(accounts => {console.log(accounts)})
);

and then you subscribe to nofriends$ in the HTML using an async pipe

CodePudding user response:

Your switchMap is messy as you are returning empty array and then pushing items to it somewhere in the future. This not only makes your code less predictable, but also prevents angular from detecting changes of that array (and this is what you probably see). Also you are returning array [] in else block instead of observable which will simply break switchMap.

Use forkJoin to build your array

 getListFriendsNoConfirmation(): Observable<Account []> {
    return this.itSMe().pipe(
      switchMap((me: Account) => {
        return this.post('account/friendNoConfirmation', HelperAccount.toUpdatePayload(me))
      }),
        switchMap((resp: ApiResponse)=>{
          if(resp != null){
            let ids= resp.data as number [];
            // if does not work, spread it first with forkJoin(...ids.map())
            return forkJoin(ids.map((id:number)=>this.getOneAccount(id)))            
          }else{
            return of([])
          }
        })
    )
  }

//useage

getListFriendsNoConfirmation().subscribe((acc:Account[])=>console.log(acc));

This way your pipeline will return alredy initialized array of accounts as forkJoin block the pipeline until all observables passed to it completes.

Also remember that you always need to subscribe, otherwise pipeline will not be triggered.

  • Related