Home > database >  Angular 12 RxJs BehaviourSubject and HTTP Get request issue
Angular 12 RxJs BehaviourSubject and HTTP Get request issue

Time:01-05

I’m new to RxJs and having issues getting a response back from a HTTP request when using a BehaviourSubject in my app. What is the correct approach to doing this? By using chrome dev tools Ive noticed that the function is not even doing the network call. Before I tried using the BehaviourSubject I just used a simple Observable and the http request worked. I also noticed just sending some static data back rather then doing a http request then the subject works.

//Service

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
import { tap } from 'rxjs/operators';
import { Pokemon } from '../_model/pokemon';

@Injectable({
  providedIn: 'root'
})
export class PokemonService {
  baseUrl = environment.apiUrl;

  private pokemonListSubject = new BehaviorSubject<any>(null);

  pokemonList$: Observable<Pokemon> = this.pokemonListSubject.asObservable();

  constructor(private http: HttpClient) { }

  getPokemonList2(){
    //This request works
    //this.pokemonListSubject.next('this is a pokemon') 

   this.http.get<Pokemon>(this.baseUrl   `pokemon/ditto`)
    .pipe(
      tap(value => {
        this.pokemonListSubject.next(value)
      })
    )
  }

}

//component

import { Component, OnInit } from '@angular/core';
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { Gen } from 'src/app/_model/gen';
import { Pokemon } from 'src/app/_model/pokemon';
import { GensService } from 'src/app/_services/gens.service';
import { PokemonService } from 'src/app/_services/pokemon.service';

@Component({
  selector: 'app-screen',
  templateUrl: './screen.component.html',
  styleUrls: ['./screen.component.css']

})
export class ScreenComponent implements OnInit {


  gens$: Observable<Gen[]>;
  pokemon2$?: any;

  constructor(public pokemonService: PokemonService, private genService: GensService) {
    this.gens$ = this.genService.getGens();

    this.pokemonService.pokemonList$.subscribe(pokemonList => this.pokemon2$ = pokemonList)
   }

  ngOnInit(): void {}

  getPokemonByGen2(gen: Gen){
    this.pokemonService.getPokemonList2();
  }
}
//html

<div id="screen">

  <div >

    <ul>
      <li *ngFor="let gen of (gens$ | async)"> <a (click)="getPokemonByGen2(gen)">{{gen.name}}</a></li>
    </ul>
  </div>

  <ng-container *ngIf="pokemon2$" >
    {{pokemon2$.name}}
  </ng-container>
</div>

CodePudding user response:

use your method like given below,

  getPokemonList2(): void {
    //This request works
    //this.pokemonListSubject.next('this is a pokemon') 

   this.http.get<Pokemon>(this.baseUrl   `pokemon/ditto`)
    .subscribe(val => {
       this.pokemonList$.next(val);
     });
  }

CodePudding user response:

You shouldn't have any logic in your constructor, that's what ngOnInit is for. For any logic initialization, and listening to changes, in this case, your subject, ngOnInit is the right place to handle that.

Now, in your click event, you are calling your service, but, no one is subscribing to that HTTP call. You are only subscribing to your subject.

What is missing in your code is this:

getPokemonByGen2(gen: Gen){
  this.pokemonService.getPokemonList2().subscribe();
}

Also, you are subscribing to your own special observable, aka pokemonList$ which is fine, but you are not handling the subscription. You need to store that subscription so you can "destroy" it if you navigate somewhere else, otherwise, you are creating a memory leak on your app.


....
....
mySubscription!: Subscription;

ngOnInit(): void {
   this.mySubscription = this.pokemonService.pokemonList$
     .subscribe(pokemonList => this.pokemon2$ = pokemonList);
}

ngOnDestroy(): void {
  this.mySubscription?.unsubscribe();
}

getPokemonByGen2(gen: Gen){
  this.pokemonService.getPokemonList2().subscribe();
}

Now if you don't want to handle the subscription, instead of subscribing into your pokemonList$, you could pipe it and store it in your local property as:

ngOnInit(): void {
 this.pokemon2$ = this.pokemonService.pokemonList$
     .pipe(
       map((pokemonList) => pokemonList)
     );
}
  •  Tags:  
  • Related