-
Notifications
You must be signed in to change notification settings - Fork 26.6k
Description
Which @angular/* package(s) are the source of the bug?
core
Is this a regression?
No
Description
I'm trying to create a fully dynamic and independent component (Tooltip) that could be programmatically rendered/destroyed in a certain place in the DOM (Dashboard) by a non-host component (Calendar). It is a tooltip shown on hover inside of Calendar (or over the calendar to be precise). It can't be added to the DOM as a child of Calendar though due to certain CSS limitations with position: fixed
+ overflow: auto
.
Here's the basic structure I want:
<app-dashboard>
<app-whatever>
<app-random>
<app-calendar>I programmatically add a tooltip to my distant ancestor on hover</app-calendar>
</app-random>
</app-whatever>
<app-tooltip>I am programmatically rendered and destroyed</app-tooltip>
<app-dashboard>
After searching the internet and the docs, I now think I have to use createComponent()
inside Calendar component.
I've tried to use the standalone createComponent()
function as well as viewContainer.createComponent()
. The latter doesn't allow to set a host element.
The former allows for that, but it has another issue: when I destroy Tooltip using this.tooltipInstance.destroy()
, it deletes the host element too. That breaks the rendering of new tooltips, since there's no host element anymore: the component is still created on hover, as far as I see in the logs, but it's not rendered.
Here's my setup:
<app-dashboard>
<app-wrapper>
<app-section>
<app-calendar>I programmatically add a tooltip to my distant ancestor on hover</app-calendar>
</app-section>
</app-wrapper>
<!-- I am wiped out on mouse leave :( -->
<div class="event-tooltip-host">
<app-tooltip>I am programmatically rendered and destroyed</app-tooltip>
</div>
<app-dashboard>
eventMouseEnter({ event, el }) {
this.tooltipInstance = createComponent(TooltipComponent, {
environmentInjector: this.injector,
hostElement: document.querySelector('.event-tooltip-host')!,
bindings: [inputBinding('event', () => event), inputBinding('element', () => el)]
})
this.appRef.attachView(this.tooltipInstance.hostView)
this.tooltipInstance.changeDetectorRef.detectChanges()
}
eventMouseLeave() {
this.appRef.detachView(this.tooltipInstance.hostView)
this.tooltipInstance.destroy()
}
It appears impossible to fully control dynamic rendering of a component back and forth from within a totally random component.
My backup plan is to do it from Dashboard component via some app-level actions which I can listen to in any component and then using viewContainer.createComponent()
. But I would really expect Angular to have a better way to do it.
So, as I see it, the bug is that hostElement
is destroyed along with the component.
If that's by design, is there any other way to achieve this without having to manage it with viewContainer.createComponent()
inside Dashboard component?
Please provide a link to a minimal reproduction of the bug
https://stackblitz.com/edit/stackblitz-starters-7jb7bw6y
Please provide the exception or error you saw
Please provide the environment you discovered this bug in (run ng version
)
Angular CLI: 20.0.2
Node: 22.15.1
Package Manager: yarn 1.22.22
OS: win32 x64
Angular: 20.0.3
... animations, cdk, common, compiler, compiler-cli, core, forms
... language-service, material, material-moment-adapter
... platform-browser, platform-browser-dynamic, platform-server
... router, service-worker
Package Version
------------------------------------------------------
@angular-devkit/architect 0.2000.2
@angular-devkit/core 20.0.2
@angular-devkit/schematics 20.0.2
@angular/build 20.0.2
@angular/cli 20.0.2
@angular/fire 18.0.1
@schematics/angular 20.0.2
rxjs 7.8.2
typescript 5.8.3
webpack 5.99.9
zone.js 0.15.1
Anything else?
No response