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