Behaviour subject value is empty when trying to fetch from a second component

烂漫一生 提交于 2021-01-29 11:01:59

问题


I am trying to share data between two components in Angular 8 using BehaviourSubject and a shared service. The code in component 1 is shown below.

constructor(private appSharedService: AppSharedService) { }

 ngOnInit() {
   this.appSharedService.getCars()
  .subscribe(response => {
    this.cars = response;
    this.appSharedService.updateCarsList(this.cars);
  }, error => {
    console.log(error);
  });
  }

The code in the shared app-shared service is

export class AppSharedService {

carsSubject : BehaviorSubject<Car[]> = new BehaviorSubject<Car[]>([]);

cars: Observable<Car[]>;

constructor(private http: HttpClient) { }

 getCars (): Observable<Car[]> {
  if (!this.cars) {
    this.cars = this.http.get<Car[]>('url', headers)
    .pipe(
        map( response => response),
        publishReplay(1), // caching the response
        refCount()        
    );
  }
   return this.cars;
 }

 updateCarsList(cars : Car[]) : void{
   this.carsSubject.next(cars);
 }

 getCarsList(): Observable<Car[]>{
   return this.carsSubject.asObservable();
 }
}

So I am initially making an API call to fetch data and then caching it so I don't make further API calls. After getting the data in component 1, I call this.appSharedService.updateCarsList(this.cars) to update this.carsSubject value in the shared service. Now I am trying to access this updated value in the component 2 as shown below. The code in component 2 is

this.appSharedService.getCarsList()
  .subscribe(response => {
    this.cars = response; // response is always empty
  }, error => {
    this.handleError(error);
  });

The problem is the response value I am getting in above component 2 is always empty. I don't understand why I can't get updated BehaviourSubject value in component 2. Please help me out with this.


回答1:


Change getCarsList() to:

getCarsList(){
    return this.carsSubject.getValue();
}

and then just call it:

this.cars = this.appSharedService.getCarsList();

EDIT

Could you try setting your carsSubject in getCars() and subscribing to the value in your component to see if it works?

    getCars () {
      if (!this.cars) {
         this.cars = this.http.get<Car[]>('url', headers)
         .pipe(
               map( response => response),
               publishReplay(1), // caching the response
               refCount()        
         );
         this.carsSubject.next(this.cars);
      }
    }

And in the constructor in your component:

this.appSharedService.carsSubject.subscribe(cars => {
    console.log(cars);
});



回答2:


You need to cast your BehaviorSubject as an observable and subscribe to that:

private readonly carsSubject: BehaviorSubject<Car[]> = new BehaviorSubject<Car[]>([]);
public readonly $carsObservable: Observable<Car[]> = this.carsSubject.asObservable();

Then subscribe to $carsObservable




回答3:


You need to do a subscribe of getCars() because it returns an observable ones you get the result from the API save the values into the behavior subject that means that you dont need cars: Observable<Car[]>; this is you principal error because you can not save values like that. I will modify the code

ngOnInit() {
    this.appSharedService.getCars()
    .subscribe(response => {    
      this.appSharedService.updateCarsList(response);
    }, error => {
       console.log(error);
    });
}  

getCars (): Observable<Car[]> {
   if (this.carsSubject.getValue().length <= 0) {
     return this.http.get<Car[]>('url', headers)
     .pipe(
         map( response => response),
         publishReplay(1), // caching the response
         refCount()        
     );
   }
   return this.carsSubject.asObservable();   
 }

With this modification you will handle the behavior subject as Cache too. When ever you call the API it will checks first if the behavior subject is empty if yes it will fetch the data from the server if not it will return the same behavior subject as observable.




回答4:


Try this:

AppSharedService

export class AppSharedService implements OnInit {

private carsSubject : BehaviorSubject<Car[]>;
public carsList$(): Observable<Car[]>;

constructor(private http: HttpClient) {
  this.carsSubject = new BehaviorSubject<Car[]>([]);
  this.carsList$ = this.carsSubject.asObservable();
}

ngOnInit(): void {
  this.http.get<Car[]>('url', headers).pipe(first()).subscribe(
     (cars) => {
       this.carsSubject.next(cars);
     }
  );
}

 updateCarsList(cars : Car[]) : void{
   this.carsSubject.next(cars);
 }
}

Then in your components you have to do:

this.appSharedService.carsList$
  .subscribe(response => {
    this.cars = response;
  }, error => {
    this.handleError(error);
  });

Let's explain what I did in the AppSharedService. when the AppSharedService is instantiated you are going to do the http request and save the value in the BehaviorSubject. Then when the components subscribe to the carsList$, if the http request was already done they are going to receive the value stored in the BehaviorSubject, on the other hand if the http request wasn't completed yet they will receive the cars list when the http request will complete.



来源:https://stackoverflow.com/questions/58976737/behaviour-subject-value-is-empty-when-trying-to-fetch-from-a-second-component

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!