MrMalt
MrMalt

Reputation: 42

How does Angular destroy event handlers and property bindings when a component is destroyed

I am trying to understand the destruction process of Angular Components a bit more detailed than what I could find in the documentation. I was hoping someone here would be able to answer the following questions:

Are properties on elements in the Component template removed before the event listeners of such elements are removed?

In the destruction process of a Component, when and how does the de-registration of an event listener happen?

Is there any more information available concerning the process of removing event listeners internally in Angular?

Upvotes: 1

Views: 1907

Answers (1)

Max Koretskyi
Max Koretskyi

Reputation: 105497

In JavaScript you cannot remove a DOM node per-se. If you have the following DOM tree:

div.children
   span

To "destroy" a span you simply need to remove it from the div.children. If there are no more links pointing to the span element it will be garbage collected. And the same holds true for objects.

Imagine the following structure in Angular:

ComponentA.nodes
   ComponentBElement -> ComponentBClass

Now Angular needs to "destroy" ComponentB. To do that it simply detaches ComponentBElement from the parent ComponentA.nodes. And this is what Angular does, for example, when you execute viewContainerRef.clear():

function execRenderNodeAction(...) {
  const renderer = view.renderer;
  switch (action) {
    ...
    case RenderNodeAction.RemoveChild:
      renderer.removeChild(parentNode, renderNode);
      break; 

Now, suppose Angular added some event listeners to ComponentBElement or its children.

Is there any need to explicitly call removeEventListners? Usually no, because once DOM elements are removed the event listeners are garbage collected as well. However, there's a possibility that a reference to the event listener is captured in some async task or an object that continues to live. This prevents the listener and the DOM from being garbage collected. So Angular ensures that event listeners are removed (in v5 it's DomEventsPlugin.removeEventListener method).

When Angular creates a component view it calls listenToElementOutputs:

function listenToElementOutputs(view, compView, def, el) {
    for (var i = 0; i < def.outputs.length; i++) {
        ...
        var disposable = listenerView.renderer.listen(listenTarget || el, output.eventName, handleEventClosure));
        ((view.disposables))[def.outputIndex + i] = disposable; <------
    }
}

You can see that event is attached using renderer and then the unsubscription callback (disposable) is stored into view.disposables. When Angular destroys a view these disposables are executed and event listeners are removed.:

function [destroyView](view) {
    ...
    if (view.disposables) {
        for (var i = 0; i < view.disposables.length; i++) {
            view.disposables[i](); <----------------
        }
    }

To learn more about views and compilation read:

Upvotes: 2

Related Questions