Angular: Automatically Unsubscribe Observables on Destroy
Tags: angular, tutorial, observables, webdev
Published: 2022-05-30 20:29:15

Don't ever write another ngOnDestroy to clean your subscriptions

{% embed https://youtu.be/mdzS-8Ltk10 %}
_See this and many other articles at [lucaspaganini.com](lucaspaganini.com)_

One thing that really annoys me when working with Angular is having to save all my subscriptions just to unsubscribe from them on `ngOnDestroy`.

It's just so annoying, I have to do this everywhere.
I've searched for solutions but all of them involve declaring `ngOnDestroy`, and I don't want that.

I just want to subscribe to something and tell Angular to automatically unsubscribe when the component is destroyed.

I've realized, however, that I can achieve what I want by delegating ngOnDestroy to a service, which makes me so excited! This has been a pain point for me since Angular 2 was released.

## Service per Component

By providing a service in a component, its existence becomes dependent on the component.

When the component is destroyed, the service is destroyed too. I'll show you:

Provide service in your component, inject it in the constructor and log when `ngOnDestroy()` happens in the service and the component.

```ts
@Injectable()
export class UnsubscriberService implements OnDestroy {
  public ngOnDestroy(): void {
    console.log('service on destroy');
  }
}

@Component({
  selector: 'app-test',
  template: 'test',
  providers: [UnsubscriberService]
})
export class TestComponent implements OnDestroy {
  constructor(private readonly _unsubscriber: UnsubscriberService) {}

  public ngOnDestroy(): void {
    console.log('component on destroy');
  }
}
```

You'll see that the service is destroyed right before the component.

## Until the Service Destroys

Knowing that we can create an observable that emits when the service is destroyed and use `takeUntil()` to automatically unsubscribe when that happens.

```ts
@Injectable()
export class UnsubscriberService implements OnDestroy {
  private readonly _destroy$ = new Subject<void>();
  public readonly destroy$ = this._destroy$.asObservable();

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

@Component({
  selector: 'app-test',
  template: 'test',
  providers: [UnsubscriberService]
})
export class TestComponent implements OnInit {
  constructor(private readonly _unsubscriber: UnsubscriberService) {}

  public ngOnInit(): void {
    timer(0, 500)
      .pipe(takeUntil(this._unsubscriber.destroy$))
      .subscribe((x) => console.log(x));
  }
}
```

To simplify, we can put the `takeUntil()` logic in our service and expose it through a simple method.

```ts
@Injectable()
export class UnsubscriberService implements OnDestroy {
  private readonly _destroy$ = new Subject<void>();

  public readonly takeUntilDestroy = <T>(
    origin: Observable<T>
  ): Observable<T> => origin.pipe(takeUntil(this._destroy$));

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

@Component({
  selector: 'app-test',
  template: 'test',
  providers: [UnsubscriberService]
})
export class TestComponent implements OnInit {
  constructor(private readonly _unsubscriber: UnsubscriberService) {}

  public ngOnInit(): void {
    timer(0, 500)
      .pipe(this._unsubscriber.takeUntilDestroy)
      .subscribe((x) => console.log(x));
  }
}
```

I've added this to my Angular utilities library. If you don't want to write anything, you can just install the library.
`npm i @lucaspaganini/angular-utils`

## Conclusion

What do you think? Loved it? Hated it? Has a better solution? Let me know what you think on my [Twitter](https://twitter.com/LucasPaganini).
Have a great day, and I'll see you soon!

## References

1. [angular-ultils Library _npm_ ](https://www.npmjs.com/package/@lucaspaganini/angular-utils)
2. [Repository angular-utils _GitHub_](https://github.com/LucasPaganini/angular-utils)