- Notifications
You must be signed in to change notification settings - Fork26.7k
Description
Which @angular/* package(s) are the source of the bug?
elements
Is this a regression?
No
Description
I believe the current lifecycle of Angular Elements is fundamentally flawed.
Custom elements really have two lifecycle methods which are relevant here:
connectedCallback
- Element is attached to the DOM.disconnectedCallback
- Element is detached from the DOM.
Note that an element may be reattached at any time after being detached. "Destroy" of an element just involves detaching it and dropping all references, allowing the GC to reclaim that element.
This is notable, because it means there is no explicit "destroy" interaction. Any given disconnectcould be followed by a subsequent attach and any arbitrary time in the future, or could be reclaimed by the GC. There's fundamentally no way to know.
The role of Angular Elements is to translate these semantics into something reasonable for existing Angular components.
Currently onconnectedCallback
we create a component and attach it to theApplicationRef
. OndisconnectedCallback
we destroy that component. In theory, reattach should then recreate the component, though that'sa little broken right now.
I think this is flawed, because it loses any internal component state as part of the destroy process. We need the component to be inert and disabled but able to be reactivated in the future if it is reconnected. Basically, custom elements want an activate/deactivate lifecycle, but Angular only provides a destroy lifecycle, with no way to "undestroy" a component.
I think ideally:
connectedCallback()
should just callappRef.attachView
(pluscreateComponent
on first connect).disconnectedCallback()
should just callappRef.detachView
to deactivate it.
In this model, each reattachment to the DOM is just a pair ofattachView
anddetachView
calls managing its relationship with theApplicationRef
and the overall app lifecycle. When detached, the component should be inert and eligible for GC if not otherwise referenced. I'm not sure if that's actually possible today though or if youhave to callComponentRef.prototype.destroy
to break some reference which would otherwise retain it.
We would probably need newngOnConnected
andngOnDisconnected
lifecycle hooks which would allow a component to acquire and release resources compatible with this model. Otherwise a component containing a simple timer would never be reclaimable, this mechanism would be necessary to disable/re-enable the timer when the component is removed/readded. These hooks need to be available forevery component, not just the root component converted into a custom element, since any of them might be rendered as a descendant of the custom element.
One more challenge is that there is no appropriate time to callngOnDestroy
since we don't know a component should be destroyed until it gets GC'd. We'd either need to remove this hook altogether, or potentially link the Angular component to its custom element via aWeakRef
and use aFinalizationRegistry
to detect when the custom element is reclaimed and callComponentRef.prototype.destroy
in response. I'm very loathe to expose GC timing into application logic, but I don't think there's any other opportunity to do this.
Please provide a link to a minimal reproduction of the bug
Please provide the exception or error you saw
Please provide the environment you discovered this bug in (runng version
)
v20
Anything else?
No response