24200

Component BehaviorSubject subscriber does not receive value emitted from async http request

Question:

I'm trying to understand why this BehaviorSubject subscriber does not receive emitted values.

In my component ngOnInit I setup a subscriber to a service method that returns a reference to a BehaviorSubject:

// project-detail.component.ts ngOnInit() { this.activatedRoute.params.subscribe((data: Params) => { if (data && data.id) { this.projectService.getProject(data.id).subscribe(project => { console.log(project); }); } }); }

In the service, I make an http request and then push the response data into the BehaviorSubject's next method:

// project.service.ts import { HttpClient } from '@angular/common/http'; import { BehaviorSubject, Observable } from 'rxjs'; @Injectable() export class ProjectService { private apiUrl = envLocal.apiJsonServerUrl; private currentProjectObs$ = new BehaviorSubject<IProject>(null); constructor(private http: HttpClient) { } getProject(id: number = 0) { if (id) { this.http.get(`${this.apiUrl}/projects/${id}`) .subscribe(project => { this.currentProjectObs$.next(project); }); } return this.currentProjectObs$; }

My understanding is that the subscriber in my component's ngOnInit should listen for changes from the BehaviorSubject reference returned from getProject (i.e. currentProjectObs$).

When ngOnInit first runs it calls getProject and since the http call inside getProject is asynchronous currentProjectObs$ is returned with the initial value of null. But once the http get request completes, currentProjectObs$.next(project) gets invoked and, I would expect, the subscriber in ngOnInit to receive the newly emitted value - but this doesn't happen.

I'd like to understand what's going on here and why the subscriber doesn't receive the async value from the http request, and how to fix it so that it would.

Answer1:

When you use below function it will again return null value because it will not wait for api call. and if you are using Behavior subject than you can directly take value from behavior subject, you not need to return it.

getProject(id: number = 0) { if (id) { this.http.get(`${this.apiUrl}/projects/${id}`) .subscribe(project => { this.currentProjectObs$.next(project); }); } return this.currentProjectObs$; }

You can do it with these two types. Do Change it to Something like this :

<strong>1. Without use of behavior subject.</strong>

// Your Service getProject(id: number = 0): Observable<any> { if (id) { return this.http.get(`${this.apiUrl}/projects/${id}`) .pipe(map(project => { return project; // this.currentProjectObs$.next(project); }); } return Observable.of(null); }; // Your Component ngOnInit() { this.activatedRoute.params.subscribe((data: Params) => { if (data && data.id) { this.projectService.getProject(data.id) .subscribe(project => { console.log(project); }); } }); }

<strong>2. Use With Behavior subject</strong>

// Your service public currentProjectObs$ = new BehaviorSubject<IProject>(null); getProject(id: number = 0) { if (id) { this.http.get(`${this.apiUrl}/projects/${id}`) .pipe(map(project => { this.currentProjectObs$.next(project); }); } // return this.currentProjectObs$; } // Your component this.activatedRoute.params.subscribe((data: Params) => { if (data && data.id) { this.projectService.getProject(data.id); } }); this.projectService.currentProjectObs$ .subscribe(project => { if(project){ console.log(project); } })

Answer2:

I think your code can be simplified a bit:

// project-detail.component.ts ngOnInit() { this.activatedRoute.params.pipe(switchMap(params => { if (data && data.id) { return this.projectService.getProject(data.id) } // return observable of undefined in case id was not found return of(undefined); })).subscribe(actualData => { console.log("actual data returned by api: ", actualData) }); }

In your service:

// project.service.ts import { HttpClient } from '@angular/common/http'; import { BehaviorSubject, Observable } from 'rxjs'; @Injectable() export class ProjectService { private apiUrl = envLocal.apiJsonServerUrl; constructor(private http: HttpClient) { } getProject(id: number = 0) { return this.http.get(`${this.apiUrl}/projects/${id}`); }

You should only subscribe at the end when you actually need the data to do something. Data can flow between one observable to another using Rxjs operators. Switch map subscribes to first observable and returns another which we subscribe to at the end.

Let me know if this works for you.

Recommend

  • JAXWS-RT: getting com.sun.xml.ws.spi.db.DatabindingException binding a simple class
  • Does many include's (in “case”) in ASP classic hurt the performance of the server?
  • How to prevent TreeItem selection?
  • Catch in Observable stops HTTP Calls from Observable.interval
  • WSO2 ESB 4.0.3 - Error installing Data Services feature from 4.0.* repositories
  • How to make http call with file in groovy to upload a file and build arguments
  • Angular5 Service Worker update(SWUpdate) not detecting on firefox. Working on chrome
  • JDBC Jtds can't establish a connection
  • Accessing 3rd level of JSON with Angular ng-repeat
  • inserting items in list in mongodb document
  • Slicing an SPA into several components and use AngularJS
  • to implement a spinner in angular2+
  • Why doesn't a local variable live long enough for thread::scoped?
  • Is there any way to call saveCurrentTurnWithMatchData without sending a push notification?
  • Angular Bootstrap Carousel Slide Transition not working correctly
  • Less Conflicting Session Manager for Zope 2
  • Sending cookie value via httpget but not getting the desired response
  • Does Mobilefirst provide a provision to access web services directly?
  • Silverlight DependencyProperty.SetCurrentValue Equivalent
  • Detect when Facebook like button is clicked
  • Needing to do .toArray() to get output of mongodb .find() on key name not value
  • Make VS2015 use angular-cli ng at build time in a .NET project
  • Meteor helpers not available in Angular template
  • Seeking advice on Jetty HttpClient Hang
  • Installing Hadoop, Java Exception about illegal characters at index 7?
  • Different response to non-authenticated users and AJAX calls
  • Perl system calls when running as another user using sudo
  • Can a Chrome extension content script make an jQuery AJAX request for an html file that is itself a
  • Upload files with Ajax and Jquery
  • Why winpcap requires both .lib and .dll to run?
  • Return words with double consecutive letters
  • How to pass list parameters for each object using Spring MVC?
  • Is there a mandatory requirement to switch app.yaml?
  • File upload with ng-file-upload throwing error
  • Python: how to group similar lists together in a list of lists?
  • ExecuteAsync RestSharp to allow backgroundWorker CancellationPending c#
  • AngularJs get employee from factory
  • Proper way to use connect-multiparty with express.js?
  • Busy indicator not showing up in wpf window [duplicate]
  • How to push additional view controllers onto NavigationController but keep the TabBar?