Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

185
Vistas
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 Respuestas
Responde la pregunta

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 Denunciar

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 Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda