Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

184
Views
Unwanted subscription on observable service

Following the cached service pattern I'm using a BehaviourSubject subscribed by using a read-only Observable created from it in various components

// credential.service.ts
private _credentialList$: BehaviorSubject<Credential[]>
readonly credentialListObv$: Observable<Credential[]>

...
this. credentialListObv$ = this.credentialList$.asObservable()

...
getCredentialList(...): Observable<Credential[]> {
    return this.http.get<Credential[]>(
      ``
      ).pipe(
      tap(credList => {
        ...
        this._credentialList$.next(credList)
      }),
    )
  }

So from the component I trigger the getCredentialList method to emit the fetched list by the _credentialList$ subject and so all the subscribers to the credentialListObv$ should get the value.

The problem is that when I create my component I will get unwanted subscription when I invoke the getCredentialList method

// credential-list.component.ts

private _credentials$: Observable<Credential[]>

constructor(
    private credentialService: CredentialService,
  ) {
    this._credentials$ = this.credentialService.credentialListObv$.pipe(
      tap(() => {console.log("sub")}),
      ...
    )
  }

  ngAfterViewInit(): void {
    this.credentialService.getCredentialList(...).subscribe()
  }

  get credentials$() {
    return this._credentials$
  }

// credential-list.component.html
<mat-expansion-panel *ngFor="let credential of credentials$ | async; let i=index">
        ...

The result is that when I access the page "sub" will be printed twice. The first subscription happens from the async pipe inside the component template but why there is another one if I will never invoke subscribe on this._credentials$ from credential-list component?

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

That's because you're also subscribing to the service method that returns the HTTP observable. So now your component has two active subscriptions to one http call.

HTTP methods in Angular complete after emitting one value, so you can safely subscribe to the observable inside the method.

getCredentialList(...): Observable<Credential[]> {
    this.http.get<Credential[]>(
      ``
      ).pipe(
      tap(credList => {
        ...
        this._credentialList$.next(credList)
      }),
    ).subscribe();
  }

Then in your component, leave your observable the same, but don't subscribe to the method.

ngAfterViewInit(): void {
  this.credentialService.getCredentialList(...);
}

Now invoking the service method triggers the HTTP request, and your private _credentials$ observable still gets the data from the BehaviorSubject.


Edit: Since you said you don't want to subscribe to anything inside the service, I can offer you a solution that only requires one subscription in the component template.

// credential.service.ts
private credentialParams$ = new Subject<ApiParameters>();
public credentialListObv$: Observable<Credential[]>;

...
this.credentialListObv$ = this.credentialParams$
.pipe(
  switchMap(urlParameters=>
    this.http.get<Credential[]>(`url with ${urlParameters}`)
  )
);

...
getCredentialList(newParams:ApiParameters): void {
  this.credentialParams$.next(newParams);
}

Use the subject to emit whatever parameters you need for your API call. Then the observable takes that emitted parameter and directly returns the HTTP request observable.

// credential-list.component.ts

public credentials$: Observable<Credential[]>

constructor(
    private credentialService: CredentialService,
  ) {
    this.credentials$ = this.credentialService.credentialListObv$.pipe(
      tap(() => {console.log("sub")}),
      ...
    );
  }

  ngAfterViewInit(): void {
    this.credentialService.getCredentialList(apiParams);
  }

// credential-list.component.html
<mat-expansion-panel *ngFor="let credential of credentials$ | async; let i=index">

In the component, we only subscribe to the service observable. On the init lifecycle, we invoke the service method. This causes your component observable to react to the HTTP request that will be returned from the service observable.

P.S. It is okay to subscribe to something within a service, if you know that observable completes (like HTTP requests).

over 4 years ago · Santiago Trujillo Report

0

Because you use BehaviourSubject, the first emition is the initial value of it. Then, later, in NgAfterViewInit, you subscribe to getCredentialList which pushes a new value to the same BehaviourSubject.

Try to log the values, instead of typed string:

tap((val) => {console.log(val)}),

The first should be the default one that you used when instantiated subject (this._credentialList$ = new BehaviorSubject(...)

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!