Home > Back-end >  how to wait the request to api done then return the value
how to wait the request to api done then return the value

Time:06-21

sorry if it's repeated I tried to solve the problem but I couldn't figure how to do

I'm trying to build a website for streaming (Movies, series, ..etc)

I have a function which returns the number of episodes for a show

my ts component code is:

export class MainCarouselComponent implements OnInit {
  constructor(
    private homeService: HomeService,
        private episodeService: EpisodesService
      ) {}
    
      MainCarousel?: IShow[];
      EpisodesNum?: number;
    
      ngOnInit(): void {
        this.homeService.getHomeMainCarousel().subscribe((shows) => {
          this.MainCarousel = shows;
          this.MainCarousel.forEach(
            (show) => (show.runtime = this.calculateRuntime(show))
          );
        });
      }
    
      calculateRuntime(_show: IShow): any {
        if (_show.runtime > 0) {
          var runtime = _show.runtime;
    
          var hours = runtime / 60;
          var rhours = Math.floor(hours);
    
          var minutes = (hours - rhours) * 60;
          var rminutes = Math.floor(minutes);
    
          return `${rhours} hr ${rminutes} min`;
        } else {
          return this.calculateEpisodes(_show.id);
        }
      }
    
      calculateEpisodes(_showId: number): number { //problem is here//
        let episodesNum;

        this.episodeService
          .getEpisodes(_showId)
          .subscribe((episodes) => (episodesNum = episodes.length));

        return episodesNum;
      }
    }

I want to return the number of episodes but it returns with undefiend, I know it's async so it doesn't wait, but can someone help me with the correct way to solve it ?

thanks a lot !

CodePudding user response:

you can use async pipe to show the value like this in component.ts

public episodes$ = this.episodeService.getEpisodes(_showId);

in component.html.

{{ (episodes$ | async)?.length }}

CodePudding user response:

This can be done, let's quickly look at the two problems:

You are mixing variable types in your calculateRuntime method:

if (_show.runtime > 0) {
     // calculations...
  return `${rhours} hr ${rminutes} min`;    // -> this returns a string
} else {
  return this.calculateEpisodes(_show.id);  // -> this returns undefined. observable, an "object"
}

Now, why undefined is returned? That's because your calculateEpisodes returns undefined`:

calculateEpisodes(_showId: number): number { //problem is here//
  // declare episodeNum; now it's undefined here
  let episodesNum;
  // call some service, whatever. It will go to network and do stuff later, but not yet.
  this.episodeService
    .getEpisodes(_showId)
    .subscribe((episodes) => (episodesNum = episodes.length));

  // Now return that `undefined` value
  return episodesNum;
}
// after your method ended here, returning undefined, the browser will go to network to get that data, and set the value of the variable, but it is already too late.

So how to fix it? We can do it multiple ways, let's try to get a "simple" and "correct" way.

First, Angular says to keep your logic out of components, put all this code in services. So let's try to create one service:

@Injectable()
export class ShowsService {
  constructor(
    private homeService: HomeService,
    private episodeService: EpisodeService
  ) {}

  getShows(): Observable<Show[]> {
    return this.homeService
      .getShows()
      .pipe(switchMap((shows) => this.calculateRuntimes(shows)));
  }

  calculateRuntimes(shows: Show[]): Observable<Show[]> {
    return forkJoin(shows.map((show) => this.calculateRuntime(show)));
  }

  calculateRuntime(show: Show): Observable<Show> {
    if (show.runtime > 0) {
      return of(show);
    } else {
      return this.episodeService.getShowRuntime(show.id).pipe(
        map((runtime) => ({
          ...show,
          runtime,
        }))
      );
    }
  }
}

Then you inject it in your MainCarouselComponent component instead of the two other services.

Here's an illustrative example.

CodePudding user response:

Add setter and getter function will be changed reactivly:

_episodesNum=0;
set episodesNum(val){
this._episodesNum = val
}

get episodesNum(){
return this._episodesNum
}

calculateEpisodes(_showId: number): number { //problem is here//
        let episodesNum;
        this.episodeService
          .getEpisodes(_showId)
          .subscribe((episodes) => {

episodesNum = episodes.length
//new new
// set the new value and it will be reflected ractively to your template
this.episodesNum = episodesNum

});

       // return episodesNum;
      }

template:

<div>
{{episodesNum}}
</div>

CodePudding user response:

I solved it by doing this:

calculateRuntime(_show: IShow): any {
if (_show.runtime > 0) {
  var runtime = _show.runtime;

  var hours = runtime / 60;
  var rhours = Math.floor(hours);

  var minutes = (hours - rhours) * 60;
  var rminutes = Math.floor(minutes);

  return `${rhours} hr ${rminutes} min`;
} else {
  this.episodeService
    .getEpisodes(_show.id)
    .subscribe(
      (episodes) => (_show.runtime = episodes.length   ' Episodes')
    );
}

}

so I set the value to the show itself in the subscribe, don't know if it's the best way but it was easy to do.

thanks for everyone tried to help I appreciate it .

  • Related