Why is my component not updating with the data being returned by an Observable?

33 Views Asked by At

This is the .ts file for my component:

 _myMovieRating: number = 0;

  get myMovieRating(): number {
    return Number(this._myMovieRating);
  }
  set myMovieRating(value: number) {
    this._myMovieRating = value;
    console.log('In setter:', this._myMovieRating);
  }


  ngOnInit(): void {
    const userId = Number(this.route.snapshot.paramMap.get('userId'));
    const movieId = this.route.snapshot.paramMap.get('movieId');
    if (movieId) {
      this.getMovie(userId, movieId);
      this.myMovieRating = this.getMovieRating(userId, movieId); 
      console.log('My Movie Rating: ' + this.myMovieRating);
    }
  }

getMovieRating(userId: number, movieId: string): number {
    this.userService
      .getUser(userId)
      .pipe(
        map((user) => {
          let found = user.movieRatings.find((rating) => rating.id == movieId);
          return user.movieRatings.find((rating) => rating.id == movieId)
            ? user.movieRatings.find((rating) => rating.id == movieId)?.rating
            : 0;
        })
      )
      .subscribe((returnedData) => console.log(returnedData));

    return 0; //is there a cleaner way to do this in the above call?
  }

Here is the template:

<div class="container">
    <h1>{{movie?.name}}</h1>
    <h3>Link: <a [href]="movie?.link">Open IMDB Page</a></h3>
    <h3>Year Released: {{movie?.year}}</h3>
    <h3>Official Rating: {{movie?.rating}}</h3>   
    <span><h3>My Rating (0-5): </h3> 
        <pm-star [rating]="myMovieRating"></pm-star> 
        <input type="text" [(ngModel)] = "myMovieRating"/>
        <button type="button" class="btn btn-primary" (click)="saveRating()">Save</button>
    </span>
</div>

In the console in the browser, first I see: My Movie Rating: 0 Then, from the console.log(returnedData), I then see 4.7 get logged in the browser console. So I know the getMovieRating() function is returning data, and I know it's bound to the template, but it's not reflecting in the UI, and I don't understand why it's not automatically updating. Am I missing something?

2

There are 2 best solutions below

1
Никита Середа On

try something like below (async pipe with observable in template):

<ng-container *ngIf="userId && movieID && (getMovie(userId, movieId) | async as movie)">
<div class="container">
    <h1>{{movie?.name}}</h1>
    <h3>Link: <a [href]="movie?.link">Open IMDB Page</a></h3>
    <h3>Year Released: {{movie?.year}}</h3>
    <ng-container *ngIf="getMovieRating() | async as rating">
    <h3>Official Rating: {{movie?.rating}}</h3>   
    <span><h3>My Rating (0-5): </h3> 
        <pm-star [rating]="myMovieRating"></pm-star> 
        <input type="text" [(ngModel)] = "myMovieRating"/>
        <button type="button" class="btn btn-primary" (click)="saveRating()">Save</button>
    </span>
    </ng-container>
</div>
</ng-container>
0
Jakub Tomáš On

Of course there are a lot of solutions how to solve this problem. First the easy solution but not the best is just in function getMovieRating in subsribe set value this.myMovieRating but you are not solving unsubsribe .and in ngOnInit just call function getMovieRating with parameters thats t

.subscribe((returnedData) => {
    this.myMovieRating = returnedData
  });

HERE is better solution

 _myMovieRating: number = 0;

   userId: number | null = null // create global values also for UI
  movieId: string | null = null

  myMovie$: Observable<any>; // change any to your type
  myMovieRating$: Observable<any>; // change any to your type

  get myMovieRating(): number {
    return Number(this._myMovieRating);
  }
  set myMovieRating(value: number) {
    this._myMovieRating = value;
    console.log('In setter:', this._myMovieRating);
  }

  ngOnInit(): void {
    this.userId = Number(this.route.snapshot.paramMap.get('userId'));
    this.movieId = this.route.snapshot.paramMap.get('movieId');

    if (this.movieId && this.userId) {
      this. myMovie$ = this.getMovie(this.userId, this.movieId); // function must return observable like getMovieRating function
      this.myMovieRating$=  this.getMovieRating(this.userId, this.movieId);
      console.log('My Movie Rating: ' + this.myMovieRating);
    }else{
      //not bad idea to write error message or something like this
    }
  }


  getMovieRating(userId: number, movieId: string): Observable<number> {
    return this.userService.getUser(userId).pipe(
      switchMap((user) => { //switchMap we need only last result , last request
        const foundRating = user.movieRatings.find((rating) => rating.id === movieId);
        return foundRating ? of(foundRating.rating) : of(0);
      })
    );
  }

TEMPLATE

<ng-container *ngIf="userId && movieID && (myMovie$ | async as movie)">
  <div class="container">
      <h1>{{movie?.name}}</h1>
      <h3>Link: <a [href]="movie?.link">Open IMDB Page</a></h3>
      <h3>Year Released: {{movie?.year}}</h3>
      <ng-container *ngIf=" myMovieRating$ | async as rating">
      <h3>Official Rating: {{movie?.rating}}</h3>   
      <span><h3>My Rating (0-5): </h3> 
          <pm-star [rating]="myMovieRating"></pm-star> 
          <input type="text" [(ngModel)] = "myMovieRating"/>
          <button type="button" class="btn btn-primary" (click)="saveRating()">Save</button>
      </span>
      </ng-container>
  </div>
  </ng-container>