Angular Element is not removed and readded to the DOM properly
See original GitHub issue🐞 bug report
Affected Package
The issue is caused by package @angular/elements
Is this a regression?
No
Description
When an Angular Element is detached from the DOM and then re-attached, none of the event bindings work (see minimal reproduction for demo). I am a Google engineer and this is causing issues for us (internal bug filed here: http://b/168062117).
I talked to @kseamon about this and he thinks the issue is located in either of these 2 places:
Specifically, this.scheduledDestroyFn
in not set to null
in the scheduled destroy and so may be called again upon connect. I’m testing adding one line to see if it fixes the immediate issue.
However, even with this fix, there will still exist a tiny race condition that occurs when an element is scheduled to be destroyed and then connects before the destroy executes.
🔬 Minimal Reproduction
https://stackblitz.com/edit/angular-ovchwr?file=src%2Findex.html
🔥 Exception or Error
No user-visible exception
🌍 Your Environment
Angular Version:
I’m running it against the latest Angular in google3.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:3
- Comments:21 (11 by maintainers)
Top GitHub Comments
To make sure we are on the same page, here is what the intention is with the current implementation:
This last bullet point is what we are failing to do at the moment, as mentioned in the previous comment (which is a bug), but that was the intention.
The reason we destroy the component instance is that there is some Angular-specific cleaning up that needs to happen when the element (and thus the underlying component instance) is no longer necessary (i.e. removed from the DOM for good).
The reason we have the short delay between removing the custom element from the DOM and destroying the component instance is to account for cases where you want to “move” the element around (i.e. remove it from its current location in the DOM and immediately insert it at a new location). In that case, it would be a waste to destroy the component synchronously, only to re-instantiate it right after.
From a quick look, it seems that fixing the current bug (i.e. allowing the component to be re-instantiated when the host element is re-inserted into the DOM) is not trivial. For example, how do we handle content projection. The original nodes have been extracted from the host element the first time, so they are no longer there for us to extract. (We could cache them and re-use the same nodes, but I wouldn’t be surprized if this broke in some edge cases.)
Even if we managed to fix this and allow the component to be re-instantiated, the internal state of the original component instance would not have been retained. This would lead to unexpected UI differences.
The main problem here is that there is no good way to automatically infer whether a removed element is intended to be re-used some time in the future (and thus the underlying component instance should be retained) or whether it is not needed any more (and thus the underlying component instance should be destroyed).
Therefore, we have to resort to unreliable heuristics (“if you haven’t added it back after 10ms you probably don’t need it”).
An alternative approach would be to provide a way for people to denote that they intend to re-use a particular custom element and therefore the underlying component instance should not be destroyed after the element has been removed from the DOM. Something like:
As a work-around, you can basically do this manually today (by using some private properties 😇):
Hi i have the same problem adding to angular js legacy app, ng-if breaks the angular web component.