Skip to content

ComponentRef.destroy() deletes host element #62150

@ilyakonrad

Description

@ilyakonrad

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: coreIssues related to the framework runtime

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions