Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

193
Visualizações
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 Respostas
Responde à pergunta

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 Relatório

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 Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda