DEV Community

Alireza Razinejad
Alireza Razinejad

Posted on

Best practice for subscribing to observables in services with Angular

I do not like to subscribe to observables inside services, as it feels expensive due to the fact that we are not able to unsubscribe from them when we do not need them any more and we all hate memory leaks, right?

So the best way to use this solution will be the way that we would be able to unsubscribe from them when we done listening to that observable.

First, we should not create that service as Singleton service, instead we should make sure the service will be specifically responsible only to handle some functionalities which is related to small part of our app at presentation layer or better to say, it will only take care of logics which related to the Dump component. It does not mean it should not be there for make container/smart components more simple, but in this example I was trying to do it for my Dump component.

Alright, let's say our service is looks like this

import {Injectable, OnDestroy} from '@angular/core';
import {Actions, ofType} from '@ngrx/effects';

import {BehaviorSubject, Observable, Subject} from 'rxjs';
import {takeUntil} from 'rxjs/operators';

import {updateNoteError, updateNoteSuccess} from '../../store/notebook.actions';
import {INote} from '../../interfaces/note.interface';

@Injectable()
export class CreationFacade implements OnDestroy {
  private destroyed$ = new Subject();

  private updateNote$ = new Subject<void>();

  constructor(private actions$: Actions) {
    this.listenToUpdateProcess();
  }

  ngOnDestroy(): void {
    this.destroyed$.next();
    this.destroyed$.complete();
  }

  updateStatus(): Observable<void> {
    return this.updateNote$.asObservable();
  }

  private listenToUpdateProcess(): void {
    this.actions$.pipe(
      ofType(updateNoteSuccess),
      takeUntil(this.destroyed$))
      .subscribe(() => {
        this.updateNote$.next()
      });
    this.actions$.pipe(
      ofType(updateNoteError),
      takeUntil(this.destroyed$))
      .subscribe((error) => {
        this.updateNote$.error(error)
      });
  }
}

Enter fullscreen mode Exit fullscreen mode

Have you noticed this line?

@Injectable()
export class CreationFacade implements OnDestroy 
Enter fullscreen mode Exit fullscreen mode

Yes, I've implemented OnDestroy interface to my service and Angular will call this lifecycle only if we provide it to the corresponding component and only there but not in the Module.

So meta data we will pass to to @Component decorator will be like this

@Component({
  selector: 'app-note-writing',
  templateUrl: './note-writing.component.html',
  styleUrls: ['./note-writing.component.scss'],
  providers: [CreationFacade]
})
Enter fullscreen mode Exit fullscreen mode

Isn't that great?

Now we can relax and be sure those observables inside the service will be unsubscribe in the end of the process and we will have happy users after all!

Thank you for reading,
Hope you enjoyed.

Top comments (3)

Collapse
 
alex_bit_6c431c4810e7e0f1 profile image
Alex Bit

great article. im curious if we can build a codemod that can at least "detect" the anti-pattern for devs and leave a comment for them to fix it? do you think it would be interesting?

if yes, what would be the detection logic?
something along the line of: hasDirectSubscription && (lacksOnDestroy || lacksUnsubscriptionLogic)?
can you help me share a link like this with some examples? go.codemod.com/Lf3JzbW

Collapse
 
jayant profile image
Jayant Singh Rawat • Edited

First of all Kudos to this brilliant post. Can you please suggest me with following scenario.

I have such similar kind of scenario, I want to connect to IoT devices using MQTT, in Angular. I was looking for such kind of solution where I can create a service or Facade (as above) , so I can access it from all page. I have different type of topics to subscribe.

I was thinking to use this service directly in app.component.ts file, but i have performance related concern,

What I have done till now is, E.g I have a temperature page. if i do active on temperature page then it works fine, subscribe to topic. What I want is to subscribe to temperature topic event thought I am not active on temperature page.??

hope I'm able to explain.

Collapse
 
ussdlover profile image
Alireza Razinejad • Edited

I can see a Redux pattern may be helpful here!
It feels like having NgRx stores responsible for storing that information!