Angular 12 RxJs BehaviourSubject 和 HTTP Get 请求问题

Angular 12 RxJs BehaviourSubject and HTTP Get request issue

我是 RxJs 的新手,在我的应用程序中使用 BehaviourSubject 时从 HTTP 请求获取响应时遇到问题。这样做的正确方法是什么? 通过使用 chrome 开发工具,我注意到该函数甚至没有进行网络调用。在尝试使用 BehaviourSubject 之前,我只使用了一个简单的 Observable 并且 http 请求有效。我还注意到只是发回一些静态数据而不是做一个 http 请求然后主题工作。

//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 class="pokedex">

    <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>

使用下面给出的方法,

  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);
     });
  }

您的构造函数中不应该有 任何 逻辑,这就是 ngOnInit 的用途。对于任何逻辑初始化和监听变化,在这种情况下,您的主题 ngOnInit 是处理它的正确位置。

现在,在您的点击事件中,您正在调用您的服务,但是没有人订阅该 HTTP 调用。您只是订阅了您的主题。

您的代码中缺少的是:

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

此外,您正在订阅自己的特殊可观察对象,也就是 pokemonList$ 很好,但是您没有处理订阅。您需要存储该订阅,以便在您导航到其他地方时可以“销毁”它,否则,您将在您的应用程序上造成内存泄漏。


....
....
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();
}

现在,如果您不想处理订阅,而不是订阅您的 pokemonList$,您可以通过管道将其存储在本地 属性 中,如:

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