0

I'm familiar with the patterns that are used to clean up subscriptions on observables as outlined here: Angular/RxJs When should I unsubscribe from Subscription

But is there a similar pattern for cleaning up event handlers?

I have a component which modifies a dynamically provided template to apply some event handlers to various elements among other things. There are other subscriptions involved and cleaned up using ngrx-take-until-destroy simplifying everything further. Currently, I'm just maintaining a separate array of Unsubscribable to collect and cleanup when destroyed.

@Component(...)
class MyComponent implements OnInit, OnDestroy {
  constructor(private renderer: Renderer2, private service: SomeService) {}
  private subscriptions: Unsubscribable[] = [];

  ngOnInit() {
    this.service.doSomething(...).pipe(
      untilDestroyed(this)
    ).subscribe(...);
  }

  private setTagLink(
    element: Element,
    clickHandler: (event: any) => (boolean|void)
  ) {
    this.subscriptions.push({
      unsubscribe: this.renderer.listen(element, 'click', clickHandler)
    });
  }

  ngOnDestroy() {
    this.subscriptions.forEach(s => s.unsubscribe());
  }
}

I'd rather not maintain this array and in a similar fashion have some automatic cleanup of the listeners. What are my options?

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
  • Can you add the html code as well? Using the angular `renderer2` should be the last resort. – Daniel Habenicht Oct 29 '19 at 20:56
  • Also this might already be answered: https://stackoverflow.com/questions/46688843/how-does-angular-destroy-event-handlers-and-property-bindings-when-a-component-i – Daniel Habenicht Oct 29 '19 at 21:00
  • @DanielHabenicht: The html retrieved dynamically and isn't processed by angular. As as far as I know, needs to be processed manually (which this component is doing). The html would otherwise look a little like this: `
    some text
    `
    – Jeff Mercado Oct 29 '19 at 21:28
  • Ahh, ok. Then the other Stackoverflow should help :) – Daniel Habenicht Oct 29 '19 at 21:31

3 Answers3

0

I think you could use fromEvent to create Observables that listen to events instead of using Renderer2.listen. And then just use the same unsubscribe pattern you use for other Observables.

Something like:

import { fromEvent } from 'rxjs';

private setTagLink(
  element: Element,
  clickHandler: (event: any) => void
) {
  fromEvent(element, 'click').pipe(
    untilDestroyed(this)
  ).subscribe(clickHandler);
}
frido
  • 13,065
  • 5
  • 42
  • 56
  • I thought about doing that, but I'm not sure whether it's advisable. I've seen a lot of advice on avoiding using DOM methods (in-)directly and stick to using angular's api's (which is why I was using Renderer2). – Jeff Mercado Nov 01 '19 at 00:35
0

When looking for something similar I came across two options:

  • Using subsink library which basically does what you are doing, but in less verbose way.
  • Creating your own unsubscribe decorator that does the dirty job, a tutorial by Natanel Basel can found here
Tal Ohana
  • 1,128
  • 8
  • 15
-1

You don't need to clean up event handlers on DOM elements that are part of the component, you would only need to clean up an event handler if the DOM element is still active after the component is destroyed.

The reason you need to clean up subscriptions to observables returned from services is because those observables are still referenced after the component is destroyed, if you have an observable created by the component then you can let the whole component fall to garbage collection without worrying about subscriptions. Same thing for DOM elements.

Adrian Brand
  • 20,384
  • 4
  • 39
  • 60
  • The elements are dynamically generated and not static so I'm not sure if this applies. But even then, there are other perfectly good cases for needing something to happen at a certain point of an application lifecycle, in this case, cleanup. In any case, I would not prefer to let the GC handle it, even if it probably could with 100% certainty. – Jeff Mercado Oct 29 '19 at 22:03
  • That is what garbage collection is for, if two objects that reference each other both fall into garbage collection you don't need to break the reference between them. If you dynamically create a DOM element that will be destroyed when the component is destroyed you don't need to remove event handlers. – Adrian Brand Oct 29 '19 at 22:26