0

I'm trying to subscribe to a service method that loads movie data from an API. Whenever the search is performed it searches for a different movie with a new ID. How do I subscribe to this from a component?

What I have done so far:

Service:

search(id): Observable<Movie> {
      return this.http.get<Movie>('https://api.themoviedb.org/3/movie/' + id + '?api_key=KeyHidden&language=en-US');
  }

Component:

export class TitleComponent implements OnInit {

  movie: Movie;

  constructor(public ms: MovieService) {}

  ngOnInit() {
    this.ms.search(this.ms.movieid).subscribe(data => this.movie = data);
  }
}

Button performing search:

<button type="submit" mdbBtn (click)="ms.search(ms.movieid)" id="sub"> Search
        </button>

Interface Movie:

export interface Movie {
    title: string;
}
  1. Serving the app throws an error because the id by default is undefined. How do I subscribe without getting an error.
  2. Why the search button isn't working?
  3. How do I get only the values that are defined in the interface and discard everything else?
Muhammad Ali
  • 3,478
  • 5
  • 19
  • 30

2 Answers2

0

The search button potentially works, but you are not listening to the result.

You do a subscribe in ngOnInit, but that by itself only gets the value once (with undefined id, which throws the error).

What you probably want, is to set up an observable in the service (an actual property) and upon button click you than call search and in there you push the result of the http to that observable with .next(value). That way the component will be notified about the result.

Something along the lines:

MovieService:

private _movie$: BehaviourSubject<Movie> = new BehaviorSubject(null);
movie$ = this.movie$.asObservable();

search(id) {
  this.http.get<Movie>(...).pipe(take(1)).subscribe(data => this.movie$.next(data));
}

TitleComponent

movie: Movie;

private terminate$: Subject = new Subject();

ngOnInit() {
  this.ms.movie$.pipe(takeUntil(terminate$)).subscribe(data => this.movie = data);
}

ngOnDestroy() {
  terminate$.next();
  terminate$.complete();
}

// You don't really show where the search-id comes from or how it is set, so here I assume it is passed in from the html
onSearch(id) {
  this.ms.search(id);
}
Gunnar B.
  • 2,879
  • 2
  • 11
  • 17
  • Type for subject should be movie right? private terminate$: Subject = new Subject(); – Muhammad Ali Aug 22 '20 at 21:22
  • No, that one is only to handle the unsubscribe with `takeUntil`. – Gunnar B. Aug 22 '20 at 21:28
  • It's working, so how do I map only the values that are defined in the interface? – Muhammad Ali Aug 22 '20 at 21:32
  • Does the object actually have more properties than the ones defined in the interface? If so, why is that a problem? You would only bind specific properties (the ones you want to show) of the object to display them anyways? Like `{{movie.title}}` – Gunnar B. Aug 22 '20 at 21:40
0

I personally would prefer creating a clicked observabe (see Observable from <button> click event in Angular2) and switchMap it to the result, which you subscribe to with async pipe.

movie$ = clicked$.pipe(
  switchMap(() => this.movieServic.search(movieId)),
);
MoxxiManagarm
  • 8,735
  • 3
  • 14
  • 43