简体   繁体   中英

How to avoid Observable inside Observable

I have a service with http request which return Observables of my Headlines

servise.ts

get(sort: string): Observable<Response> {
    return this.http.get<any>(this.url, {...});
})

delete(id) {
  return this.http.delete(`${this.url}/${id}`, {...});
}

and in my component I have a function that set this.headlines from service get request. It looks like this:

interface IRes {
  searches: {
    label: string,
    id: number,
    value: string,
    ...
  }
}
headlines = [];

loadHeadlines() {
  this.service.get(data).subscribe((res: IRes) => this.headlines= res.headlines);
}

The problem is that sometimes I receive headline with empty label and don't need to show them, so I need to filter it and send.delete() request for this headlines. I tried something like this( The idea was to add .pipe before subscribe and call inside another subscribe.) Something like that

loadHeadlines() {
  this.service.get(data)
    .pipe(
      map(res: IRes => {
        res.headlines.filter(headline => !!headline.label.trim())
          .forEach(headline => this.service.delete(headline.id).subscribe())
      })
    )
    .subscribe((res: IRes) => this.headlines= res.headlines);
}

but not sure that it's good idea. Which method would be better here?

You could use RxJS switchMap operator to map from one observable to another and forkJoin function to combine multiple observables. I've also used iif (with of ) function to check if there are any empty headline s to be deleted.

But I fail to understand the condition you're using at the moment. It seems you're removing all the headline elements with empty label property while invoking delete on the rest. So essentially you're calling delete on all the valid elements. Perhaps it must be adjusted.

Try the following

import { iif, forkJoin, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';

loadHeadlines() {
  this.service.get(data).pipe(
    switchMap((res: IRes) => {
      const emptyHeadlines = res.headlines.filter(headline => !headline.label.trim()); // <-- all headlines with empty labels
      const deletes$ = forkJoin(
        emptyHeadlines.map(headline => this.service.delete(headline.id))
      ).pipe(
        map(_ =>  ({     // <-- map back to return only the headlines with defined `label`
          ...res,
          headlines: headlines.filter(headline => !!headline.label.trim())
        }))
      );
      return iif(
        () => !!emptyHeadlines.length,
        deletes$, // <-- execute delete only if there are empty headlines 
        of(res)   // <-- if not forward the response
      );
    })
  ).subscribe(
    (res: IRes) => this.headlines = res.headlines,
    (error: any) => console.log(error)
  );
}

well, ..headline.label.trim() definitely isn't a good idea, because the double ! will delete all headlines that are NOT empty. also that map should be a tap .

so for the complete pipe:

this.service.get(data).pipe(
    map(res => res.headlines)
    tap(headlines => headlines.filter(headline => !headline.label.trim()).forEach(
        headline => this.service.delete(headline.id)
    )),
    map(headlines => headlines.filter(headline => !!headline.label.trim())),
).subscribe(headlines => this.headlines= headlines);

In a real scenario, i'd leave this to be handled by the backend. But tbh, this is strongly opinionated, as there is no objective right/wrong here.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM